From 956a2b915e6ad1095fb03a6d7f7c2894ba5a3961 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Tue, 2 Jun 2015 17:27:02 -0700
Subject: [PATCH 01/16] initial move to loopback server
---
server/boot/authentication.js | 4 ++++
server/boot/explorer.js | 27 +++++++++++++++++++++
server/boot/middlewares.js | 0
server/boot/restApi.js | 4 ++++
server/config.json | 29 ++++++++++++++++++++++
server/datasources.json | 9 +++++++
server/middleware.json | 27 +++++++++++++++++++++
server/model-config.json | 33 +++++++++++++++++++++++++
server/server.js | 45 ++++++++++++++++-------------------
9 files changed, 153 insertions(+), 25 deletions(-)
create mode 100644 server/boot/authentication.js
create mode 100644 server/boot/explorer.js
create mode 100644 server/boot/middlewares.js
create mode 100644 server/boot/restApi.js
create mode 100644 server/config.json
create mode 100644 server/datasources.json
create mode 100644 server/middleware.json
create mode 100644 server/model-config.json
diff --git a/server/boot/authentication.js b/server/boot/authentication.js
new file mode 100644
index 0000000000..a87cd08161
--- /dev/null
+++ b/server/boot/authentication.js
@@ -0,0 +1,4 @@
+module.exports = function enableAuthentication(server) {
+ // enable authentication
+ server.enableAuth();
+};
diff --git a/server/boot/explorer.js b/server/boot/explorer.js
new file mode 100644
index 0000000000..de40021e5f
--- /dev/null
+++ b/server/boot/explorer.js
@@ -0,0 +1,27 @@
+module.exports = function mountLoopBackExplorer(server) {
+ var explorer;
+ try {
+ explorer = require('loopback-explorer');
+ } catch(err) {
+ // Print the message only when the app was started via `server.listen()`.
+ // Do not print any message when the project is used as a component.
+ server.once('started', function() {
+ console.log(
+ 'Run `npm install loopback-explorer` to enable the LoopBack explorer'
+ );
+ });
+ return;
+ }
+
+ var restApiRoot = server.get('restApiRoot');
+
+ var explorerApp = explorer(server, { basePath: restApiRoot });
+ server.use('/explorer', explorerApp);
+ server.once('started', function() {
+ var baseUrl = server.get('url').replace(/\/$/, '');
+ // express 4.x (loopback 2.x) uses `mountpath`
+ // express 3.x (loopback 1.x) uses `route`
+ var explorerPath = explorerApp.mountpath || explorerApp.route;
+ console.log('Browse your REST API at %s%s', baseUrl, explorerPath);
+ });
+};
diff --git a/server/boot/middlewares.js b/server/boot/middlewares.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/server/boot/restApi.js b/server/boot/restApi.js
new file mode 100644
index 0000000000..256579a813
--- /dev/null
+++ b/server/boot/restApi.js
@@ -0,0 +1,4 @@
+module.exports = function mountRestApi(server) {
+ var restApiRoot = server.get('restApiRoot');
+ server.use(restApiRoot, server.loopback.rest());
+};
diff --git a/server/config.json b/server/config.json
new file mode 100644
index 0000000000..8404e744fb
--- /dev/null
+++ b/server/config.json
@@ -0,0 +1,29 @@
+{
+ "restApiRoot": "/api",
+ "host": "0.0.0.0",
+ "port": 3000,
+ "remoting": {
+ "context": {
+ "enableHttpContext": false
+ },
+ "rest": {
+ "normalizeHttpPath": false,
+ "xml": false
+ },
+ "json": {
+ "strict": false,
+ "limit": "100kb"
+ },
+ "urlencoded": {
+ "extended": true,
+ "limit": "100kb"
+ },
+ "cors": {
+ "origin": true,
+ "credentials": true
+ },
+ "errorHandler": {
+ "disableStackTrace": false
+ }
+ }
+}
diff --git a/server/datasources.json b/server/datasources.json
new file mode 100644
index 0000000000..4ce65a253f
--- /dev/null
+++ b/server/datasources.json
@@ -0,0 +1,9 @@
+{
+ "db": {
+ "name": "db",
+ "connector": "mongodb",
+ "host": "127.0.0.1",
+ "database": "foobar",
+ "port": 27017
+ }
+}
diff --git a/server/middleware.json b/server/middleware.json
new file mode 100644
index 0000000000..664cacda58
--- /dev/null
+++ b/server/middleware.json
@@ -0,0 +1,27 @@
+{
+ "initial:before": {
+ "loopback#favicon": {}
+ },
+ "initial": {
+ "compression": {}
+ },
+ "session": {
+ },
+ "auth": {
+ },
+ "parse": {
+ },
+ "routes": {
+ },
+ "files": {
+ "loopback#static": {
+ "params": "$!../public"
+ }
+ },
+ "final": {
+ "loopback#urlNotFound": {}
+ },
+ "final:after": {
+ "errorhandler": {}
+ }
+}
diff --git a/server/model-config.json b/server/model-config.json
new file mode 100644
index 0000000000..7c86c514a9
--- /dev/null
+++ b/server/model-config.json
@@ -0,0 +1,33 @@
+{
+ "_meta": {
+ "sources": [
+ "loopback/common/models",
+ "loopback/server/models",
+ "../common/models",
+ "./models"
+ ]
+ },
+ "User": {
+ "dataSource": "db"
+ },
+ "AccessToken": {
+ "dataSource": "db",
+ "public": false
+ },
+ "ACL": {
+ "dataSource": "db",
+ "public": false
+ },
+ "RoleMapping": {
+ "dataSource": "db",
+ "public": false
+ },
+ "Role": {
+ "dataSource": "db",
+ "public": false
+ },
+ "bonfire": {
+ "dataSource": "db",
+ "public": true
+ }
+}
diff --git a/server/server.js b/server/server.js
index f5ae3b0a41..f9f07a7a22 100755
--- a/server/server.js
+++ b/server/server.js
@@ -9,7 +9,8 @@ process.on('uncaughtException', function (err) {
process.exit(1); // eslint-disable-line
});
-var express = require('express'),
+var loopback = require('loopback'),
+ boot = require('loopback-boot'),
accepts = require('accepts'),
cookieParser = require('cookie-parser'),
compress = require('compression'),
@@ -51,22 +52,7 @@ var express = require('express'),
/**
* Create Express server.
*/
-var app = express();
-
-/**
- * Connect to MongoDB.
- */
-mongoose.connect(secrets.db);
-mongoose.connection.on('error', function () {
- console.error(
- 'MongoDB Connection Error. Please make sure that MongoDB is running.'
- );
-});
-
-/**
- * Express configuration.
- */
-
+var app = loopback();
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
@@ -191,6 +177,7 @@ app.use(helmet.csp({
safari5: false
}));
+
app.use(function (req, res, next) {
// Make user object available in templates.
res.locals.user = req.user;
@@ -198,9 +185,10 @@ app.use(function (req, res, next) {
});
app.use(
- express.static(path.join(__dirname, '../public'), { maxAge: 86400000 })
+ loopback.static(path.join(__dirname, '../public'), { maxAge: 86400000 })
);
+boot(app, __dirname);
app.use(function (req, res, next) {
// Remember original destination before login.
var path = req.path.split('/')[1];
@@ -273,12 +261,19 @@ if (process.env.NODE_ENV === 'development') {
* Start Express server.
*/
-app.listen(app.get('port'), function () {
- console.log(
- 'FreeCodeCamp server listening on port %d in %s mode',
- app.get('port'),
- app.get('env')
- );
-});
+app.start = function() {
+ app.listen(app.get('port'), function () {
+ console.log(
+ 'FreeCodeCamp server listening on port %d in %s mode',
+ app.get('port'),
+ app.get('env')
+ );
+ });
+};
+
+// start the server if `$ node server.js`
+if (require.main === module) {
+ app.start();
+}
module.exports = app;
From e7d01424e95ae76e9c9dce38355150c0721b0368 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Tue, 2 Jun 2015 19:02:54 -0700
Subject: [PATCH 02/16] refactor challenge and challengeMap
---
package.json | 3 +
server/boot/challenge.js | 757 ++++++++++++++++++------------------
server/boot/challengeMap.js | 106 +++--
server/boot/jobs.js | 27 +-
4 files changed, 441 insertions(+), 452 deletions(-)
diff --git a/package.json b/package.json
index 81dbf86f39..c99b623ca0 100644
--- a/package.json
+++ b/package.json
@@ -57,6 +57,9 @@
"less": "~1.7.5",
"less-middleware": "~2.0.1",
"lodash": "~2.4.1",
+ "loopback": "^2.18.0",
+ "loopback-boot": "^2.8.0",
+ "loopback-connector-mongodb": "^1.10.0",
"lusca": "~1.0.2",
"method-override": "~2.3.0",
"moment": "~2.10.2",
diff --git a/server/boot/challenge.js b/server/boot/challenge.js
index b2bb53142c..da00222ff7 100644
--- a/server/boot/challenge.js
+++ b/server/boot/challenge.js
@@ -31,33 +31,13 @@
*/
var R = require('ramda'),
- express = require('express'),
- Challenge = require('../../common/models/Challenge'),
- User = require('../../common/models/User'),
resources = require('../resources/resources'),
userMigration = require('../resources/middleware').userMigration,
MDNlinks = require('../../seed_data/bonfireMDNlinks');
-var router = express.Router();
var challengeMapWithNames = resources.getChallengeMapWithNames();
var challengeMapWithIds = resources.getChallengeMapWithIds();
-router.get(
- '/challenges/next-challenge',
- userMigration,
- returnNextChallenge
-);
-
-router.get(
- '/challenges/:challengeName',
- userMigration,
- returnIndividualChallenge
-);
-
-router.get('/challenges/', userMigration, returnCurrentChallenge);
-router.post('/completed-challenge/', completedChallenge);
-router.post('/completed-zipline-or-basejump', completedZiplineOrBasejump);
-router.post('/completed-bonfire', completedBonfire);
function getMDNlinks(links) {
// takes in an array of links, which are strings
@@ -73,278 +53,300 @@ function getMDNlinks(links) {
return populatedLinks;
}
-function returnNextChallenge(req, res, next) {
- if (!req.user) {
- return res.redirect('../challenges/learn-how-free-code-camp-works');
- }
- var completed = req.user.completedChallenges.map(function (elem) {
- return elem._id;
- });
+module.exports = function(app) {
+ var router = app.Router();
+ var Challenge = app.models.Challenge;
+ var User = app.models.User;
- req.user.uncompletedChallenges = resources.allChallengeIds()
- .filter(function (elem) {
- if (completed.indexOf(elem) === -1) {
- return elem;
- }
- });
+ router.get(
+ '/challenges/next-challenge',
+ userMigration,
+ returnNextChallenge
+ );
- // find the user's current challenge and block
- // look in that block and find the index of their current challenge
- // if index + 1 < block.challenges.length
- // serve index + 1 challenge
- // otherwise increment block key and serve the first challenge in that block
- // unless the next block is undefined, which means no next block
- var nextChallengeName;
+ router.get(
+ '/challenges/:challengeName',
+ userMigration,
+ returnIndividualChallenge
+ );
- var challengeId = String(req.user.currentChallenge.challengeId);
- var challengeBlock = req.user.currentChallenge.challengeBlock;
- var indexOfChallenge = challengeMapWithIds[challengeBlock]
- .indexOf(challengeId);
+ router.get('/challenges/', userMigration, returnCurrentChallenge);
+ router.post('/completed-challenge/', completedChallenge);
+ router.post('/completed-zipline-or-basejump', completedZiplineOrBasejump);
+ router.post('/completed-bonfire', completedBonfire);
- if (indexOfChallenge + 1
- < challengeMapWithIds[challengeBlock].length) {
- nextChallengeName =
- challengeMapWithNames[challengeBlock][++indexOfChallenge];
- } else if (typeof challengeMapWithIds[++challengeBlock] !== 'undefined') {
- nextChallengeName = R.head(challengeMapWithNames[challengeBlock]);
- } else {
- req.flash('errors', {
- msg: 'It looks like you have finished all of our challenges.' +
- ' Great job! Now on to helping nonprofits!'
- });
- nextChallengeName = R.head(challengeMapWithNames[0].challenges);
- }
-
- var nameString = nextChallengeName.trim()
- .toLowerCase()
- .replace(/\s/g, '-');
-
- req.user.save(function(err) {
- if (err) {
- return next(err);
+ function returnNextChallenge(req, res, next) {
+ if (!req.user) {
+ return res.redirect('../challenges/learn-how-free-code-camp-works');
}
- return res.redirect('../challenges/' + nameString);
- });
-}
-
-function returnCurrentChallenge(req, res, next) {
- if (!req.user) {
- return res.redirect('../challenges/learn-how-free-code-camp-works');
- }
- var completed = req.user.completedChallenges.map(function (elem) {
- return elem._id;
- });
-
- req.user.uncompletedChallenges = resources.allChallengeIds()
- .filter(function (elem) {
- if (completed.indexOf(elem) === -1) {
- return elem;
- }
+ var completed = req.user.completedChallenges.map(function (elem) {
+ return elem._id;
});
- if (!req.user.currentChallenge) {
- req.user.currentChallenge = {};
- req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0];
- req.user.currentChallenge.challengeName = challengeMapWithNames['0'][0];
- req.user.currentChallenge.challengeBlock = '0';
+
+ req.user.uncompletedChallenges = resources.allChallengeIds()
+ .filter(function (elem) {
+ if (completed.indexOf(elem) === -1) {
+ return elem;
+ }
+ });
+
+ // find the user's current challenge and block
+ // look in that block and find the index of their current challenge
+ // if index + 1 < block.challenges.length
+ // serve index + 1 challenge
+ // otherwise increment block key and serve the first challenge in that block
+ // unless the next block is undefined, which means no next block
+ var nextChallengeName;
+
+ var challengeId = String(req.user.currentChallenge.challengeId);
+ var challengeBlock = req.user.currentChallenge.challengeBlock;
+ var indexOfChallenge = challengeMapWithIds[challengeBlock]
+ .indexOf(challengeId);
+
+ if (indexOfChallenge + 1
+ < challengeMapWithIds[challengeBlock].length) {
+ nextChallengeName =
+ challengeMapWithNames[challengeBlock][++indexOfChallenge];
+ } else if (typeof challengeMapWithIds[++challengeBlock] !== 'undefined') {
+ nextChallengeName = R.head(challengeMapWithNames[challengeBlock]);
+ } else {
+ req.flash('errors', {
+ msg: 'It looks like you have finished all of our challenges.' +
+ ' Great job! Now on to helping nonprofits!'
+ });
+ nextChallengeName = R.head(challengeMapWithNames[0].challenges);
+ }
+
+ var nameString = nextChallengeName.trim()
+ .toLowerCase()
+ .replace(/\s/g, '-');
+
req.user.save(function(err) {
if (err) {
return next(err);
}
+ return res.redirect('../challenges/' + nameString);
});
}
- var nameString = req.user.currentChallenge.challengeName.trim()
- .toLowerCase()
- .replace(/\s/g, '-')
- .replace(/[^a-z0-9\-\/.]/gi, '');
- req.user.save(function(err) {
- if (err) {
- return next(err);
+
+ function returnCurrentChallenge(req, res, next) {
+ if (!req.user) {
+ return res.redirect('../challenges/learn-how-free-code-camp-works');
}
- return res.redirect('../challenges/' + nameString);
- });
-}
-
-function returnIndividualChallenge(req, res, next) {
- var dashedName = req.params.challengeName;
-
- var challengeName =
- (/^(bonfire|waypoint|zipline|basejump)/i).test(dashedName) ?
- dashedName
- .replace(/\-/g, ' ')
- .split(' ')
- .slice(1)
- .join(' ') :
- dashedName.replace(/\-/g, ' ');
-
- Challenge.find({'name': new RegExp(challengeName, 'i')},
- function(err, challengeFromMongo) {
- if (err) {
- return next(err);
- }
- // Handle not found
- if (challengeFromMongo.length < 1) {
- req.flash('errors', {
- msg: '404: We couldn\'t find a challenge with that name. ' +
- 'Please double check the name.'
- });
- return res.redirect('/challenges');
- }
- var challenge = challengeFromMongo.pop();
- // Redirect to full name if the user only entered a partial
- var dashedNameFull = challenge.name
- .toLowerCase()
- .replace(/\s/g, '-')
- .replace(/[^a-z0-9\-\.]/gi, '');
- if (dashedNameFull !== dashedName) {
- return res.redirect('../challenges/' + dashedNameFull);
- } else if (req.user) {
- req.user.currentChallenge = {
- challengeId: challenge._id,
- challengeName: challenge.name,
- challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds).
- map(function (key) {
- return challengeMapWithIds[key]
- .filter(function (elem) {
- return String(elem) === String(challenge._id);
- }).map(function () {
- return key;
- });
- })
- ))
- };
- }
-
- var challengeType = {
- 0: function() {
- res.render('coursewares/showHTML', {
- title: challenge.name,
- dashedName: dashedName,
- name: challenge.name,
- brief: challenge.description[0],
- details: challenge.description.slice(1),
- tests: challenge.tests,
- challengeSeed: challenge.challengeSeed,
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
- challengeId: challenge._id,
- environment: resources.whichEnvironment(),
- challengeType: challenge.challengeType
- });
- },
-
- 1: function() {
- res.render('coursewares/showJS', {
- title: challenge.name,
- dashedName: dashedName,
- name: challenge.name,
- brief: challenge.description[0],
- details: challenge.description.slice(1),
- tests: challenge.tests,
- challengeSeed: challenge.challengeSeed,
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
- challengeId: challenge._id,
- challengeType: challenge.challengeType
- });
- },
-
- 2: function() {
- res.render('coursewares/showVideo', {
- title: challenge.name,
- dashedName: dashedName,
- name: challenge.name,
- details: challenge.description,
- tests: challenge.tests,
- video: challenge.challengeSeed[0],
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
- challengeId: challenge._id,
- challengeType: challenge.challengeType
- });
- },
-
- 3: function() {
- res.render('coursewares/showZiplineOrBasejump', {
- title: challenge.name,
- dashedName: dashedName,
- name: challenge.name,
- details: challenge.description,
- video: challenge.challengeSeed[0],
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
- challengeId: challenge._id,
- challengeType: challenge.challengeType
- });
- },
-
- 4: function() {
- res.render('coursewares/showZiplineOrBasejump', {
- title: challenge.name,
- dashedName: dashedName,
- name: challenge.name,
- details: challenge.description,
- video: challenge.challengeSeed[0],
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
- challengeId: challenge._id,
- challengeType: challenge.challengeType
- });
- },
-
- 5: function() {
- res.render('coursewares/showBonfire', {
- completedWith: null,
- title: challenge.name,
- dashedName: dashedName,
- name: challenge.name,
- difficulty: Math.floor(+challenge.difficulty),
- brief: challenge.description.shift(),
- details: challenge.description,
- tests: challenge.tests,
- challengeSeed: challenge.challengeSeed,
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
- bonfires: challenge,
- challengeId: challenge._id,
- MDNkeys: challenge.MDNlinks,
- MDNlinks: getMDNlinks(challenge.MDNlinks),
- challengeType: challenge.challengeType
- });
- }
- };
- if (req.user) {
- req.user.save(function (err) {
- if (err) {
- return next(err);
- }
- return challengeType[challenge.challengeType]();
- });
- } else {
- return challengeType[challenge.challengeType]();
- }
+ var completed = req.user.completedChallenges.map(function (elem) {
+ return elem._id;
});
-}
-function completedBonfire(req, res, next) {
- var isCompletedWith = req.body.challengeInfo.completedWith || '';
- var isCompletedDate = Math.round(+new Date());
- var challengeId = req.body.challengeInfo.challengeId;
- var isSolution = req.body.challengeInfo.solution;
- var challengeName = req.body.challengeInfo.challengeName;
-
- if (isCompletedWith) {
- var paired = User.find({'profile.username': isCompletedWith.toLowerCase()})
- .limit(1);
- paired.exec(function (err, pairedWith) {
+ req.user.uncompletedChallenges = resources.allChallengeIds()
+ .filter(function (elem) {
+ if (completed.indexOf(elem) === -1) {
+ return elem;
+ }
+ });
+ if (!req.user.currentChallenge) {
+ req.user.currentChallenge = {};
+ req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0];
+ req.user.currentChallenge.challengeName = challengeMapWithNames['0'][0];
+ req.user.currentChallenge.challengeBlock = '0';
+ req.user.save(function(err) {
+ if (err) {
+ return next(err);
+ }
+ });
+ }
+ var nameString = req.user.currentChallenge.challengeName.trim()
+ .toLowerCase()
+ .replace(/\s/g, '-')
+ .replace(/[^a-z0-9\-\/.]/gi, '');
+ req.user.save(function(err) {
if (err) {
return next(err);
- } else {
+ }
+ return res.redirect('../challenges/' + nameString);
+ });
+ }
+
+ function returnIndividualChallenge(req, res, next) {
+ var dashedName = req.params.challengeName;
+
+ var challengeName =
+ (/^(bonfire|waypoint|zipline|basejump)/i).test(dashedName) ?
+ dashedName
+ .replace(/\-/g, ' ')
+ .split(' ')
+ .slice(1)
+ .join(' ') :
+ dashedName.replace(/\-/g, ' ');
+
+ Challenge.find(
+ { where: { name: new RegExp(challengeName, 'i') } },
+ function(err, challengeFromMongo) {
+ if (err) { return next(err); }
+
+ // Handle not found
+ if (challengeFromMongo.length < 1) {
+ req.flash('errors', {
+ msg: '404: We couldn\'t find a challenge with that name. ' +
+ 'Please double check the name.'
+ });
+ return res.redirect('/challenges');
+ }
+ var challenge = challengeFromMongo.pop();
+ // Redirect to full name if the user only entered a partial
+ var dashedNameFull = challenge.name
+ .toLowerCase()
+ .replace(/\s/g, '-')
+ .replace(/[^a-z0-9\-\.]/gi, '');
+ if (dashedNameFull !== dashedName) {
+ return res.redirect('../challenges/' + dashedNameFull);
+ } else if (req.user) {
+ req.user.currentChallenge = {
+ challengeId: challenge._id,
+ challengeName: challenge.name,
+ challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds).
+ map(function (key) {
+ return challengeMapWithIds[key]
+ .filter(function (elem) {
+ return String(elem) === String(challenge._id);
+ }).map(function () {
+ return key;
+ });
+ })
+ ))
+ };
+ }
+
+ var challengeType = {
+ 0: function() {
+ res.render('coursewares/showHTML', {
+ title: challenge.name,
+ dashedName: dashedName,
+ name: challenge.name,
+ brief: challenge.description[0],
+ details: challenge.description.slice(1),
+ tests: challenge.tests,
+ challengeSeed: challenge.challengeSeed,
+ verb: resources.randomVerb(),
+ phrase: resources.randomPhrase(),
+ compliment: resources.randomCompliment(),
+ challengeId: challenge._id,
+ environment: resources.whichEnvironment(),
+ challengeType: challenge.challengeType
+ });
+ },
+
+ 1: function() {
+ res.render('coursewares/showJS', {
+ title: challenge.name,
+ dashedName: dashedName,
+ name: challenge.name,
+ brief: challenge.description[0],
+ details: challenge.description.slice(1),
+ tests: challenge.tests,
+ challengeSeed: challenge.challengeSeed,
+ verb: resources.randomVerb(),
+ phrase: resources.randomPhrase(),
+ compliment: resources.randomCompliment(),
+ challengeId: challenge._id,
+ challengeType: challenge.challengeType
+ });
+ },
+
+ 2: function() {
+ res.render('coursewares/showVideo', {
+ title: challenge.name,
+ dashedName: dashedName,
+ name: challenge.name,
+ details: challenge.description,
+ tests: challenge.tests,
+ video: challenge.challengeSeed[0],
+ verb: resources.randomVerb(),
+ phrase: resources.randomPhrase(),
+ compliment: resources.randomCompliment(),
+ challengeId: challenge._id,
+ challengeType: challenge.challengeType
+ });
+ },
+
+ 3: function() {
+ res.render('coursewares/showZiplineOrBasejump', {
+ title: challenge.name,
+ dashedName: dashedName,
+ name: challenge.name,
+ details: challenge.description,
+ video: challenge.challengeSeed[0],
+ verb: resources.randomVerb(),
+ phrase: resources.randomPhrase(),
+ compliment: resources.randomCompliment(),
+ challengeId: challenge._id,
+ challengeType: challenge.challengeType
+ });
+ },
+
+ 4: function() {
+ res.render('coursewares/showZiplineOrBasejump', {
+ title: challenge.name,
+ dashedName: dashedName,
+ name: challenge.name,
+ details: challenge.description,
+ video: challenge.challengeSeed[0],
+ verb: resources.randomVerb(),
+ phrase: resources.randomPhrase(),
+ compliment: resources.randomCompliment(),
+ challengeId: challenge._id,
+ challengeType: challenge.challengeType
+ });
+ },
+
+ 5: function() {
+ res.render('coursewares/showBonfire', {
+ completedWith: null,
+ title: challenge.name,
+ dashedName: dashedName,
+ name: challenge.name,
+ difficulty: Math.floor(+challenge.difficulty),
+ brief: challenge.description.shift(),
+ details: challenge.description,
+ tests: challenge.tests,
+ challengeSeed: challenge.challengeSeed,
+ verb: resources.randomVerb(),
+ phrase: resources.randomPhrase(),
+ compliment: resources.randomCompliment(),
+ bonfires: challenge,
+ challengeId: challenge._id,
+ MDNkeys: challenge.MDNlinks,
+ MDNlinks: getMDNlinks(challenge.MDNlinks),
+ challengeType: challenge.challengeType
+ });
+ }
+ };
+ if (req.user) {
+ req.user.save(function (err) {
+ if (err) {
+ return next(err);
+ }
+ return challengeType[challenge.challengeType]();
+ });
+ } else {
+ return challengeType[challenge.challengeType]();
+ }
+ });
+ }
+
+ function completedBonfire(req, res, next) {
+ var isCompletedWith = req.body.challengeInfo.completedWith || '';
+ var isCompletedDate = Math.round(+new Date());
+ var challengeId = req.body.challengeInfo.challengeId;
+ var isSolution = req.body.challengeInfo.solution;
+ var challengeName = req.body.challengeInfo.challengeName;
+
+ if (isCompletedWith) {
+ User.find({
+ where: { 'profile.username': isCompletedWith.toLowerCase() },
+ limit: 1
+ }, function (err, pairedWith) {
+ if (err) { return next(err); }
+
var index = req.user.uncompletedChallenges.indexOf(challengeId);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
@@ -379,20 +381,18 @@ function completedBonfire(req, res, next) {
});
}
// User said they paired, but pair wasn't found
- req.user.completedChallenges.push({
- _id: challengeId,
- name: challengeName,
- completedWith: null,
- completedDate: isCompletedDate,
- solution: isSolution,
- challengeType: 5
- });
-
+ req.user.completedChallenges.push({
+ _id: challengeId,
+ name: challengeName,
+ completedWith: null,
+ completedDate: isCompletedDate,
+ solution: isSolution,
+ challengeType: 5
+ });
req.user.save(function (err, user) {
- if (err) {
- return next(err);
- }
+ if (err) { return next(err); }
+
if (pairedWith) {
pairedWith.save(function (err, paired) {
if (err) {
@@ -406,21 +406,47 @@ function completedBonfire(req, res, next) {
res.send(true);
}
});
+ });
+ } else {
+ req.user.completedChallenges.push({
+ _id: challengeId,
+ name: challengeName,
+ completedWith: null,
+ completedDate: isCompletedDate,
+ solution: isSolution,
+ challengeType: 5
+ });
+
+ var index = req.user.uncompletedChallenges.indexOf(challengeId);
+ if (index > -1) {
+
+ req.user.progressTimestamps.push(Date.now() || 0);
+ req.user.uncompletedChallenges.splice(index, 1);
}
- });
- } else {
+
+ req.user.save(function (err) {
+ if (err) { return next(err); }
+ res.send(true);
+ });
+ }
+ }
+
+ function completedChallenge(req, res, next) {
+
+ var isCompletedDate = Math.round(+new Date());
+ var challengeId = req.body.challengeInfo.challengeId;
+
req.user.completedChallenges.push({
_id: challengeId,
- name: challengeName,
- completedWith: null,
completedDate: isCompletedDate,
- solution: isSolution,
- challengeType: 5
+ name: req.body.challengeInfo.challengeName,
+ solution: null,
+ githubLink: null,
+ verified: true
});
-
var index = req.user.uncompletedChallenges.indexOf(challengeId);
- if (index > -1) {
+ if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedChallenges.splice(index, 1);
}
@@ -429,70 +455,36 @@ function completedBonfire(req, res, next) {
if (err) {
return next(err);
}
- // NOTE(berks): Under certain conditions the res is never ended
if (user) {
- res.send(true);
+ res.sendStatus(200);
}
});
}
-}
-function completedChallenge(req, res, next) {
+ function completedZiplineOrBasejump(req, res, next) {
- var isCompletedDate = Math.round(+new Date());
- var challengeId = req.body.challengeInfo.challengeId;
-
- req.user.completedChallenges.push({
- _id: challengeId,
- completedDate: isCompletedDate,
- name: req.body.challengeInfo.challengeName,
- solution: null,
- githubLink: null,
- verified: true
- });
- var index = req.user.uncompletedChallenges.indexOf(challengeId);
-
- if (index > -1) {
- req.user.progressTimestamps.push(Date.now() || 0);
- req.user.uncompletedChallenges.splice(index, 1);
- }
-
- req.user.save(function (err, user) {
- if (err) {
- return next(err);
+ var isCompletedWith = req.body.challengeInfo.completedWith || false;
+ var isCompletedDate = Math.round(+new Date());
+ var challengeId = req.body.challengeInfo.challengeId;
+ var solutionLink = req.body.challengeInfo.publicURL;
+ var githubLink = req.body.challengeInfo.challengeType === '4'
+ ? req.body.challengeInfo.githubURL : true;
+ var challengeType = req.body.challengeInfo.challengeType === '4' ?
+ 4 : 3;
+ if (!solutionLink || !githubLink) {
+ req.flash('errors', {
+ msg: 'You haven\'t supplied the necessary URLs for us to inspect ' +
+ 'your work.'
+ });
+ return res.sendStatus(403);
}
- if (user) {
- res.sendStatus(200);
- }
- });
-}
-function completedZiplineOrBasejump(req, res, next) {
-
- var isCompletedWith = req.body.challengeInfo.completedWith || false;
- var isCompletedDate = Math.round(+new Date());
- var challengeId = req.body.challengeInfo.challengeId;
- var solutionLink = req.body.challengeInfo.publicURL;
- var githubLink = req.body.challengeInfo.challengeType === '4'
- ? req.body.challengeInfo.githubURL : true;
- var challengeType = req.body.challengeInfo.challengeType === '4' ?
- 4 : 3;
- if (!solutionLink || !githubLink) {
- req.flash('errors', {
- msg: 'You haven\'t supplied the necessary URLs for us to inspect ' +
- 'your work.'
- });
- return res.sendStatus(403);
- }
-
- if (isCompletedWith) {
- var paired = User.find({'profile.username': isCompletedWith.toLowerCase()})
- .limit(1);
-
- paired.exec(function (err, pairedWithFromMongo) {
- if (err) {
- return next(err);
- } else {
+ if (isCompletedWith) {
+ User.find({
+ where: { 'profile.username': isCompletedWith.toLowerCase() },
+ limit: 1
+ }, function (err, pairedWithFromMongo) {
+ if (err) { return next(err); }
var index = req.user.uncompletedChallenges.indexOf(challengeId);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
@@ -512,9 +504,7 @@ function completedZiplineOrBasejump(req, res, next) {
});
req.user.save(function (err, user) {
- if (err) {
- return next(err);
- }
+ if (err) { return next(err); }
if (req.user._id.toString() === pairedWith._id.toString()) {
return res.sendStatus(200);
@@ -545,37 +535,36 @@ function completedZiplineOrBasejump(req, res, next) {
}
});
});
+ });
+ } else {
+
+ req.user.completedChallenges.push({
+ _id: challengeId,
+ name: req.body.challengeInfo.challengeName,
+ completedWith: null,
+ completedDate: isCompletedDate,
+ solution: solutionLink,
+ githubLink: githubLink,
+ challengeType: challengeType,
+ verified: false
+ });
+
+ var index = req.user.uncompletedChallenges.indexOf(challengeId);
+ if (index > -1) {
+ req.user.progressTimestamps.push(Date.now() || 0);
+ req.user.uncompletedChallenges.splice(index, 1);
}
- });
- } else {
- req.user.completedChallenges.push({
- _id: challengeId,
- name: req.body.challengeInfo.challengeName,
- completedWith: null,
- completedDate: isCompletedDate,
- solution: solutionLink,
- githubLink: githubLink,
- challengeType: challengeType,
- verified: false
- });
-
- var index = req.user.uncompletedChallenges.indexOf(challengeId);
- if (index > -1) {
- req.user.progressTimestamps.push(Date.now() || 0);
- req.user.uncompletedChallenges.splice(index, 1);
+ req.user.save(function (err, user) {
+ if (err) {
+ return next(err);
+ }
+ // NOTE(berks): under certain conditions this will not close
+ // the response.
+ if (user) {
+ return res.sendStatus(200);
+ }
+ });
}
-
- req.user.save(function (err, user) {
- if (err) {
- return next(err);
- }
- // NOTE(berks): under certain conditions this will not close the response.
- if (user) {
- return res.sendStatus(200);
- }
- });
}
-}
-
-module.exports = router;
+};
diff --git a/server/boot/challengeMap.js b/server/boot/challengeMap.js
index 8d8bb727c9..8a95857e2f 100644
--- a/server/boot/challengeMap.js
+++ b/server/boot/challengeMap.js
@@ -1,65 +1,63 @@
var R = require('ramda'),
- express = require('express'),
// debug = require('debug')('freecc:cntr:challengeMap'),
- User = require('../../common/models/User'),
resources = require('./../resources/resources'),
- middleware = require('../resources/middleware'),
- router = express.Router();
+ middleware = require('../resources/middleware');
-router.get('/map', middleware.userMigration, challengeMap);
-router.get('/learn-to-code', function(req, res) {
- res.redirect(301, '/map');
-});
+module.exports = function(app) {
+ var User = app.models.User;
+ var router = app.Router();
-router.get('/about', function(req, res) {
- res.redirect(301, '/map');
-});
-
-function challengeMap(req, res, next) {
- var completedList = [];
-
- if (req.user) {
- completedList = req.user.completedChallenges;
- }
-
- var noDuplicatedChallenges = R.uniq(completedList);
-
- var completedChallengeList = noDuplicatedChallenges
- .map(function(challenge) {
- return challenge._id;
- });
- var challengeList = resources.
- getChallengeMapForDisplay(completedChallengeList);
-
- Object.keys(challengeList).forEach(function(key) {
- challengeList[key].completed = challengeList[key]
- .challenges.filter(function(elem) {
- return completedChallengeList.indexOf(elem._id) > -1;
- });
+ router.get('/map', middleware.userMigration, challengeMap);
+ router.get('/learn-to-code', function(req, res) {
+ res.redirect(301, '/map');
+ });
+ router.get('/about', function(req, res) {
+ res.redirect(301, '/map');
});
- function numberWithCommas(x) {
- return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
- }
+ function challengeMap(req, res, next) {
+ var completedList = [];
- var date1 = new Date('10/15/2014');
- var date2 = new Date();
- var timeDiff = Math.abs(date2.getTime() - date1.getTime());
- var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24));
-
- User.count({}, function (err, camperCount) {
- if (err) {
- return next(err);
+ if (req.user) {
+ completedList = req.user.completedChallenges;
}
- res.render('challengeMap/show', {
- daysRunning: daysRunning,
- camperCount: numberWithCommas(camperCount),
- title: "A map of all Free Code Camp's Challenges",
- challengeList: challengeList,
- completedChallengeList: completedChallengeList
- });
- });
-}
-module.exports = router;
+ var noDuplicatedChallenges = R.uniq(completedList);
+
+ var completedChallengeList = noDuplicatedChallenges
+ .map(function(challenge) {
+ return challenge._id;
+ });
+ var challengeList = resources.
+ getChallengeMapForDisplay(completedChallengeList);
+
+ Object.keys(challengeList).forEach(function(key) {
+ challengeList[key].completed = challengeList[key]
+ .challenges.filter(function(elem) {
+ return completedChallengeList.indexOf(elem._id) > -1;
+ });
+ });
+
+ function numberWithCommas(x) {
+ return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+ }
+
+ var date1 = new Date('10/15/2014');
+ var date2 = new Date();
+ var timeDiff = Math.abs(date2.getTime() - date1.getTime());
+ var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24));
+
+ User.count(function(err, camperCount) {
+ if (err) { return next(err); }
+
+ res.render('challengeMap/show', {
+ daysRunning: daysRunning,
+ camperCount: numberWithCommas(camperCount),
+ title: "A map of all Free Code Camp's Challenges",
+ challengeList: challengeList,
+ completedChallengeList: completedChallengeList
+ });
+ });
+ }
+};
diff --git a/server/boot/jobs.js b/server/boot/jobs.js
index 5cba0c2f64..abcda8a2b2 100644
--- a/server/boot/jobs.js
+++ b/server/boot/jobs.js
@@ -1,18 +1,17 @@
-var express = require('express');
-var Job = require('../../common/models/Job');
-var router = express.Router();
+module.exports = function(app) {
+ var Job = app.models.Job;
+ var router = app.Router();
-router.get('/jobs', jobsDirectory);
+ router.get('/jobs', jobsDirectory);
-function jobsDirectory(req, res, next) {
- Job.find({}, function(err, jobs) {
- if (err) { return next(err); }
+ function jobsDirectory(req, res, next) {
+ Job.find({}, function(err, jobs) {
+ if (err) { return next(err); }
- res.render('jobs/directory', {
- title: 'Junior JavaScript Engineer Jobs',
- jobs: jobs
+ res.render('jobs/directory', {
+ title: 'Junior JavaScript Engineer Jobs',
+ jobs: jobs
+ });
});
- });
-}
-
-module.exports = router;
+ }
+};
From 272d9b4ad58b57438534cdbe3e615a388b25c3dd Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Tue, 2 Jun 2015 20:32:10 -0700
Subject: [PATCH 03/16] Update seed directory to use loopback
---
README.md | 2 +-
{seed_data => seed}/bonfireMDNlinks.js | 0
{seed_data => seed}/challenge-migration.js | 0
{seed_data => seed}/challengeMapping.json | 0
.../challenges/advanced-bonfires.json | 0
{seed_data => seed}/challenges/basejumps.json | 0
.../challenges/basic-bonfires.json | 0
.../challenges/basic-html5-and-css.json | 2 +-
.../challenges/basic-javascript.json | 0
{seed_data => seed}/challenges/bootstrap.json | 0
.../challenges/computer-science.json | 0
.../challenges/full-stack-javascript.json | 0
.../challenges/functional-programming.json | 0
.../get-set-for-free-code-camp.json | 0
.../challenges/intermediate-bonfires.json | 0
.../challenges/jquery-ajax-and-json.json | 0
.../object-oriented-javascript.json | 0
{seed_data => seed}/challenges/ziplines.json | 0
{seed_data => seed}/field-guides.json | 2 +-
.../future-jquery-ajax-json.json | 0
seed/index.js | 99 +++++++++++++++++++
{seed_data => seed}/jobs.json | 0
{seed_data => seed}/nonprofits.json | 0
{seed_data => seed}/storyCleanup.js | 0
{seed_data => seed}/userMigration.js | 0
seed_data/seed.js | 97 ------------------
26 files changed, 102 insertions(+), 100 deletions(-)
rename {seed_data => seed}/bonfireMDNlinks.js (100%)
rename {seed_data => seed}/challenge-migration.js (100%)
rename {seed_data => seed}/challengeMapping.json (100%)
rename {seed_data => seed}/challenges/advanced-bonfires.json (100%)
rename {seed_data => seed}/challenges/basejumps.json (100%)
rename {seed_data => seed}/challenges/basic-bonfires.json (100%)
rename {seed_data => seed}/challenges/basic-html5-and-css.json (99%)
rename {seed_data => seed}/challenges/basic-javascript.json (100%)
rename {seed_data => seed}/challenges/bootstrap.json (100%)
rename {seed_data => seed}/challenges/computer-science.json (100%)
rename {seed_data => seed}/challenges/full-stack-javascript.json (100%)
rename {seed_data => seed}/challenges/functional-programming.json (100%)
rename {seed_data => seed}/challenges/get-set-for-free-code-camp.json (100%)
rename {seed_data => seed}/challenges/intermediate-bonfires.json (100%)
rename {seed_data => seed}/challenges/jquery-ajax-and-json.json (100%)
rename {seed_data => seed}/challenges/object-oriented-javascript.json (100%)
rename {seed_data => seed}/challenges/ziplines.json (100%)
rename {seed_data => seed}/field-guides.json (99%)
rename {seed_data => seed}/future-jquery-ajax-json.json (100%)
create mode 100644 seed/index.js
rename {seed_data => seed}/jobs.json (100%)
rename {seed_data => seed}/nonprofits.json (100%)
rename {seed_data => seed}/storyCleanup.js (100%)
rename {seed_data => seed}/userMigration.js (100%)
delete mode 100644 seed_data/seed.js
diff --git a/README.md b/README.md
index 15ba559e4a..dc4e2f7383 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,7 @@ DEBUG=true
mongod
# Seed your database with the challenges
-node seed_data/seed.js
+node seed/
# start the application
gulp
diff --git a/seed_data/bonfireMDNlinks.js b/seed/bonfireMDNlinks.js
similarity index 100%
rename from seed_data/bonfireMDNlinks.js
rename to seed/bonfireMDNlinks.js
diff --git a/seed_data/challenge-migration.js b/seed/challenge-migration.js
similarity index 100%
rename from seed_data/challenge-migration.js
rename to seed/challenge-migration.js
diff --git a/seed_data/challengeMapping.json b/seed/challengeMapping.json
similarity index 100%
rename from seed_data/challengeMapping.json
rename to seed/challengeMapping.json
diff --git a/seed_data/challenges/advanced-bonfires.json b/seed/challenges/advanced-bonfires.json
similarity index 100%
rename from seed_data/challenges/advanced-bonfires.json
rename to seed/challenges/advanced-bonfires.json
diff --git a/seed_data/challenges/basejumps.json b/seed/challenges/basejumps.json
similarity index 100%
rename from seed_data/challenges/basejumps.json
rename to seed/challenges/basejumps.json
diff --git a/seed_data/challenges/basic-bonfires.json b/seed/challenges/basic-bonfires.json
similarity index 100%
rename from seed_data/challenges/basic-bonfires.json
rename to seed/challenges/basic-bonfires.json
diff --git a/seed_data/challenges/basic-html5-and-css.json b/seed/challenges/basic-html5-and-css.json
similarity index 99%
rename from seed_data/challenges/basic-html5-and-css.json
rename to seed/challenges/basic-html5-and-css.json
index 93b5215440..d2a8e1f64e 100644
--- a/seed_data/challenges/basic-html5-and-css.json
+++ b/seed/challenges/basic-html5-and-css.json
@@ -237,7 +237,7 @@
"name": "Waypoint: Fill in the Blank with Placeholder Text",
"difficulty": 0.015,
"description": [
- "Replace the text inside your p
element with the first few words of the provided \"Ktty Ipsum\" text.",
+ "Replace the text inside your p
element with the first few words of the provided \"Kitty Ipsum\" text.",
"Web developers traditionally use \"Lorem Ipsum\" text as placeholder text. It's called \"Lorem Ipsum\" text because those are the first two words of a famous passage by Cicero of Ancient Rome.",
"\"Lorem Ipsum\" text has been used as placeholder text by typesetters since the 16th century, and this tradition continues on the web.",
"Well, 5 centuries is long enough. Since we're building a CatPhotoApp, let's use something called \"Kitty Ipsum\"!",
diff --git a/seed_data/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json
similarity index 100%
rename from seed_data/challenges/basic-javascript.json
rename to seed/challenges/basic-javascript.json
diff --git a/seed_data/challenges/bootstrap.json b/seed/challenges/bootstrap.json
similarity index 100%
rename from seed_data/challenges/bootstrap.json
rename to seed/challenges/bootstrap.json
diff --git a/seed_data/challenges/computer-science.json b/seed/challenges/computer-science.json
similarity index 100%
rename from seed_data/challenges/computer-science.json
rename to seed/challenges/computer-science.json
diff --git a/seed_data/challenges/full-stack-javascript.json b/seed/challenges/full-stack-javascript.json
similarity index 100%
rename from seed_data/challenges/full-stack-javascript.json
rename to seed/challenges/full-stack-javascript.json
diff --git a/seed_data/challenges/functional-programming.json b/seed/challenges/functional-programming.json
similarity index 100%
rename from seed_data/challenges/functional-programming.json
rename to seed/challenges/functional-programming.json
diff --git a/seed_data/challenges/get-set-for-free-code-camp.json b/seed/challenges/get-set-for-free-code-camp.json
similarity index 100%
rename from seed_data/challenges/get-set-for-free-code-camp.json
rename to seed/challenges/get-set-for-free-code-camp.json
diff --git a/seed_data/challenges/intermediate-bonfires.json b/seed/challenges/intermediate-bonfires.json
similarity index 100%
rename from seed_data/challenges/intermediate-bonfires.json
rename to seed/challenges/intermediate-bonfires.json
diff --git a/seed_data/challenges/jquery-ajax-and-json.json b/seed/challenges/jquery-ajax-and-json.json
similarity index 100%
rename from seed_data/challenges/jquery-ajax-and-json.json
rename to seed/challenges/jquery-ajax-and-json.json
diff --git a/seed_data/challenges/object-oriented-javascript.json b/seed/challenges/object-oriented-javascript.json
similarity index 100%
rename from seed_data/challenges/object-oriented-javascript.json
rename to seed/challenges/object-oriented-javascript.json
diff --git a/seed_data/challenges/ziplines.json b/seed/challenges/ziplines.json
similarity index 100%
rename from seed_data/challenges/ziplines.json
rename to seed/challenges/ziplines.json
diff --git a/seed_data/field-guides.json b/seed/field-guides.json
similarity index 99%
rename from seed_data/field-guides.json
rename to seed/field-guides.json
index de6edba981..d241f8858b 100644
--- a/seed_data/field-guides.json
+++ b/seed/field-guides.json
@@ -683,7 +683,7 @@
" Fork the Free Code Camp repository and open seed_data/bonfires.json
to become familiar with the format of our bonfires. ",
" Regardless of your bonfire's difficulty, put it as the last bonfire in the JSON file. Change one of the numbers in the ID to ensure that your bonfire has a unique ID. ",
" In the terminal, run node seed_data/seed.js
. Run gulp
. You should be able to navigate to your new bonfire in the challenge map. Whenever you make a change to bonfire.json, you'll need to reseed in order to see these changes in the browser. ",
- " Solved your own Bonfire. Confirmed that your tests work as expected and that your instructions are sufficiently clear. ",
+ " Solve your own Bonfire. Confirm that your tests work as expected and that your instructions are sufficiently clear. ",
" Submit a pull request to Free Code Camp's Staging branch and in the pull request body, link to a gist that has your algorithmic solution. ",
" ",
"
",
diff --git a/seed_data/future-jquery-ajax-json.json b/seed/future-jquery-ajax-json.json
similarity index 100%
rename from seed_data/future-jquery-ajax-json.json
rename to seed/future-jquery-ajax-json.json
diff --git a/seed/index.js b/seed/index.js
new file mode 100644
index 0000000000..ddad9d678f
--- /dev/null
+++ b/seed/index.js
@@ -0,0 +1,99 @@
+/* eslint-disable no-process-exit */
+require('dotenv').load();
+var fs = require('fs'),
+ path = require('path'),
+ app = require('../server/server'),
+ fieldGuides = require('./field-guides.json'),
+ nonprofits = require('./nonprofits.json'),
+ jobs = require('./jobs.json');
+
+var Challenge = app.models.Challenge;
+var FieldGuide = app.models.FieldGuide;
+var Nonprofit = app.models.Nonprofit;
+var Job = app.models.Job;
+var counter = 0;
+var challenges = fs.readdirSync(path.join(__dirname, '/challenges'));
+var offerings = 3 + challenges.length;
+
+var CompletionMonitor = function() {
+ counter++;
+ console.log('call ' + counter);
+
+ if (counter < offerings) {
+ return;
+ } else {
+ process.exit(0);
+ }
+};
+
+Challenge.destroyAll(function(err, info) {
+ if (err) {
+ console.err(err);
+ } else {
+ console.log('Deleted ', info);
+ }
+ challenges.forEach(function (file) {
+ Challenge.create(
+ require('./challenges/' + file).challenges,
+ function (err) {
+ if (err) {
+ console.log(err);
+ } else {
+ console.log('Successfully parsed %s', file);
+ CompletionMonitor();
+ }
+ }
+ );
+ });
+});
+
+FieldGuide.destroyAll(function(err, info) {
+ if (err) {
+ console.error(err);
+ } else {
+ console.log('Deleted ', info);
+ }
+ FieldGuide.create(fieldGuides, function(err, data) {
+ if (err) {
+ console.log(err);
+ } else {
+ console.log('Saved ', data);
+ }
+ CompletionMonitor();
+ console.log('field guides');
+ });
+});
+
+Nonprofit.destroyAll(function(err, info) {
+ if (err) {
+ console.error(err);
+ } else {
+ console.log('Deleted ', info);
+ }
+ Nonprofit.create(nonprofits, function(err, data) {
+ if (err) {
+ console.log(err);
+ } else {
+ console.log('Saved ', data);
+ }
+ CompletionMonitor();
+ console.log('nonprofits');
+ });
+});
+
+Job.destroyAll(function(err, info) {
+ if (err) {
+ console.error(err);
+ } else {
+ console.log('Deleted ', info);
+ }
+ Job.create(jobs, function(err, data) {
+ if (err) {
+ console.log(err);
+ } else {
+ console.log('Saved ', data);
+ }
+ console.log('jobs');
+ CompletionMonitor();
+ });
+});
diff --git a/seed_data/jobs.json b/seed/jobs.json
similarity index 100%
rename from seed_data/jobs.json
rename to seed/jobs.json
diff --git a/seed_data/nonprofits.json b/seed/nonprofits.json
similarity index 100%
rename from seed_data/nonprofits.json
rename to seed/nonprofits.json
diff --git a/seed_data/storyCleanup.js b/seed/storyCleanup.js
similarity index 100%
rename from seed_data/storyCleanup.js
rename to seed/storyCleanup.js
diff --git a/seed_data/userMigration.js b/seed/userMigration.js
similarity index 100%
rename from seed_data/userMigration.js
rename to seed/userMigration.js
diff --git a/seed_data/seed.js b/seed_data/seed.js
deleted file mode 100644
index 0221952d72..0000000000
--- a/seed_data/seed.js
+++ /dev/null
@@ -1,97 +0,0 @@
-require('dotenv').load();
-var Challenge = require('../models/Challenge.js'),
- FieldGuide = require('../models/FieldGuide.js'),
- Nonprofit = require('../models/Nonprofit.js'),
- Job = require('../models/Job.js'),
- mongoose = require('mongoose'),
- secrets = require('../config/secrets'),
- fieldGuides = require('./field-guides.json'),
- nonprofits = require('./nonprofits.json'),
- jobs = require('./jobs.json'),
- fs = require('fs');
-
-mongoose.connect(secrets.db);
-var challenges = fs.readdirSync(__dirname + '/challenges');
-
-var counter = 0;
-var offerings = 3 + challenges.length;
-
-var CompletionMonitor = function() {
- counter++;
- console.log('call ' + counter);
-
- if (counter < offerings) {
- return;
- } else {
- process.exit(0);
- }
-};
-
-Challenge.remove({}, function(err, data) {
- if (err) {
- console.err(err);
- } else {
- console.log('Deleted ', data);
- }
- challenges.forEach(function (file) {
- Challenge.create(require('./challenges/' + file).challenges, function (err, data) {
- if (err) {
- console.log(err);
- } else {
- console.log('Successfully parsed %s', file);
- CompletionMonitor();
- }
- });
- });
-});
-
-FieldGuide.remove({}, function(err, data) {
- if (err) {
- console.error(err);
- } else {
- console.log('Deleted ', data);
- }
- FieldGuide.create(fieldGuides, function(err, data) {
- if (err) {
- console.log(err);
- } else {
- console.log('Saved ', data);
- }
- CompletionMonitor();
- });
- console.log('field guides');
-});
-
-Nonprofit.remove({}, function(err, data) {
- if (err) {
- console.error(err);
- } else {
- console.log('Deleted ', data);
- }
- Nonprofit.create(nonprofits, function(err, data) {
- if (err) {
- console.log(err);
- } else {
- console.log('Saved ', data);
- }
- CompletionMonitor();
- });
- console.log('nonprofits');
-});
-
-Job.remove({}, function(err, data) {
- if (err) {
- console.error(err);
- } else {
- console.log('Deleted ', data);
- }
- Job.create(jobs, function(err, data) {
- if (err) {
- console.log(err);
- } else {
- console.log('Saved ', data);
- }
- CompletionMonitor();
- });
- console.log('jobs');
-});
From 79cbe13c62221df523726660c81413aaf580c9a2 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Wed, 3 Jun 2015 12:23:37 -0700
Subject: [PATCH 04/16] fix seed paths
---
server/boot/challenge.js | 2 +-
server/boot/fieldGuide.js | 2 +-
server/resources/resources.js | 8 ++++----
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/server/boot/challenge.js b/server/boot/challenge.js
index da00222ff7..26cb6d9f4e 100644
--- a/server/boot/challenge.js
+++ b/server/boot/challenge.js
@@ -33,7 +33,7 @@
var R = require('ramda'),
resources = require('../resources/resources'),
userMigration = require('../resources/middleware').userMigration,
- MDNlinks = require('../../seed_data/bonfireMDNlinks');
+ MDNlinks = require('../../seed/bonfireMDNlinks');
var challengeMapWithNames = resources.getChallengeMapWithNames();
var challengeMapWithIds = resources.getChallengeMapWithIds();
diff --git a/server/boot/fieldGuide.js b/server/boot/fieldGuide.js
index 7497a4440e..e5d8d784c3 100644
--- a/server/boot/fieldGuide.js
+++ b/server/boot/fieldGuide.js
@@ -90,7 +90,7 @@ function returnNextFieldGuide(req, res, next) {
"You've read all our current Field Guide entries. You can ",
'contribute to our Field Guide ',
"here ."
+ "staging/seed/field-guides.json'>here."
].join('')
});
}
diff --git a/server/resources/resources.js b/server/resources/resources.js
index 4cece5435e..865792e050 100644
--- a/server/resources/resources.js
+++ b/server/resources/resources.js
@@ -11,8 +11,8 @@ var async = require('async'),
Story = require('../../common/models/Story'),
Comment = require('../../common/models/Comment'),
resources = require('./resources.json'),
- nonprofits = require('../../seed_data/nonprofits.json'),
- fieldGuides = require('../../seed_data/field-guides.json');
+ nonprofits = require('../../seed/nonprofits.json'),
+ fieldGuides = require('../../seed/field-guides.json');
/**
* Cached values
@@ -41,12 +41,12 @@ Array.zip = function(left, right, combinerFunction) {
if (!challengeMap) {
var localChallengeMap = {};
var files = fs.readdirSync(
- path.join(__dirname, '../../seed_data/challenges')
+ path.join(__dirname, '../../seed/challenges')
);
var keyCounter = 0;
files = files.map(function (file) {
return require(
- path.join(__dirname, '../../seed_data/challenges/' + file)
+ path.join(__dirname, '../../seed/challenges/' + file)
);
});
files = files.sort(function (a, b) {
From 65cde59f0dc0906bb5c44d5dd2701fa26394d356 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Wed, 3 Jun 2015 12:26:11 -0700
Subject: [PATCH 05/16] use app instead of server in boot directories
---
server/boot/authentication.js | 4 ++--
server/boot/explorer.js | 16 ++++++++--------
server/boot/fieldGuide.js | 1 -
server/boot/restApi.js | 6 +++---
4 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/server/boot/authentication.js b/server/boot/authentication.js
index a87cd08161..3ccd6de22e 100644
--- a/server/boot/authentication.js
+++ b/server/boot/authentication.js
@@ -1,4 +1,4 @@
-module.exports = function enableAuthentication(server) {
+module.exports = function enableAuthentication(app) {
// enable authentication
- server.enableAuth();
+ app.enableAuth();
};
diff --git a/server/boot/explorer.js b/server/boot/explorer.js
index de40021e5f..7461836f63 100644
--- a/server/boot/explorer.js
+++ b/server/boot/explorer.js
@@ -1,11 +1,11 @@
-module.exports = function mountLoopBackExplorer(server) {
+module.exports = function mountLoopBackExplorer(app) {
var explorer;
try {
explorer = require('loopback-explorer');
} catch(err) {
- // Print the message only when the app was started via `server.listen()`.
+ // Print the message only when the app was started via `app.listen()`.
// Do not print any message when the project is used as a component.
- server.once('started', function() {
+ app.once('started', function() {
console.log(
'Run `npm install loopback-explorer` to enable the LoopBack explorer'
);
@@ -13,12 +13,12 @@ module.exports = function mountLoopBackExplorer(server) {
return;
}
- var restApiRoot = server.get('restApiRoot');
+ var restApiRoot = app.get('restApiRoot');
- var explorerApp = explorer(server, { basePath: restApiRoot });
- server.use('/explorer', explorerApp);
- server.once('started', function() {
- var baseUrl = server.get('url').replace(/\/$/, '');
+ var explorerApp = explorer(app, { basePath: restApiRoot });
+ app.use('/explorer', explorerApp);
+ app.once('started', function() {
+ var baseUrl = app.get('url').replace(/\/$/, '');
// express 4.x (loopback 2.x) uses `mountpath`
// express 3.x (loopback 1.x) uses `route`
var explorerPath = explorerApp.mountpath || explorerApp.route;
diff --git a/server/boot/fieldGuide.js b/server/boot/fieldGuide.js
index e5d8d784c3..c3275e6f62 100644
--- a/server/boot/fieldGuide.js
+++ b/server/boot/fieldGuide.js
@@ -1,7 +1,6 @@
var R = require('ramda'),
express = require('express'),
// debug = require('debug')('freecc:fieldguides'),
- FieldGuide = require('../../common/models/FieldGuide'),
resources = require('../resources/resources');
var router = express.Router();
diff --git a/server/boot/restApi.js b/server/boot/restApi.js
index 256579a813..e81c692006 100644
--- a/server/boot/restApi.js
+++ b/server/boot/restApi.js
@@ -1,4 +1,4 @@
-module.exports = function mountRestApi(server) {
- var restApiRoot = server.get('restApiRoot');
- server.use(restApiRoot, server.loopback.rest());
+module.exports = function mountRestApi(app) {
+ var restApiRoot = app.get('restApiRoot');
+ app.use(restApiRoot, app.loopback.rest());
};
From 8165105b29608c6ace45ed205bcb46ec420a4857 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Wed, 3 Jun 2015 16:19:23 -0700
Subject: [PATCH 06/16] refactor all boot to export func and use loopback
---
package.json | 1 +
server/boot/fieldGuide.js | 220 +++---
server/boot/home.js | 29 +-
server/boot/nonprofits.js | 236 +++---
server/boot/passport.js | 109 ++-
server/boot/redirects.js | 75 +-
server/boot/story.js | 1058 +++++++++++++--------------
server/boot/user.js | 1425 +++++++++++++++++++------------------
server/boot/utility.js | 826 +++++++++++----------
9 files changed, 2028 insertions(+), 1951 deletions(-)
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'
+ }
+ ]
+ );
+ }
+};
From bfd33d8b408961e8f8d83bdb0aca143ce23d5338 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Wed, 3 Jun 2015 16:31:42 -0700
Subject: [PATCH 07/16] fix use app.use(router) to add sub router
---
server/boot/challenge.js | 4 +++-
server/boot/challengeMap.js | 4 +++-
server/boot/fieldGuide.js | 4 +++-
server/boot/home.js | 4 +++-
server/boot/jobs.js | 3 ++-
server/boot/nonprofits.js | 4 +++-
server/boot/passport.js | 4 +++-
server/boot/redirects.js | 4 +++-
server/boot/story.js | 4 +++-
server/boot/user.js | 4 +++-
server/boot/utility.js | 4 +++-
11 files changed, 32 insertions(+), 11 deletions(-)
diff --git a/server/boot/challenge.js b/server/boot/challenge.js
index 26cb6d9f4e..df85d803d4 100644
--- a/server/boot/challenge.js
+++ b/server/boot/challenge.js
@@ -54,7 +54,7 @@ function getMDNlinks(links) {
}
module.exports = function(app) {
- var router = app.Router();
+ var router = app.loopback.Router();
var Challenge = app.models.Challenge;
var User = app.models.User;
@@ -75,6 +75,8 @@ module.exports = function(app) {
router.post('/completed-zipline-or-basejump', completedZiplineOrBasejump);
router.post('/completed-bonfire', completedBonfire);
+ app.use(router);
+
function returnNextChallenge(req, res, next) {
if (!req.user) {
return res.redirect('../challenges/learn-how-free-code-camp-works');
diff --git a/server/boot/challengeMap.js b/server/boot/challengeMap.js
index 8a95857e2f..c6820c5020 100644
--- a/server/boot/challengeMap.js
+++ b/server/boot/challengeMap.js
@@ -6,7 +6,7 @@ var R = require('ramda'),
module.exports = function(app) {
var User = app.models.User;
- var router = app.Router();
+ var router = app.loopback.Router();
router.get('/map', middleware.userMigration, challengeMap);
router.get('/learn-to-code', function(req, res) {
@@ -16,6 +16,8 @@ module.exports = function(app) {
res.redirect(301, '/map');
});
+ app.use(router);
+
function challengeMap(req, res, next) {
var completedList = [];
diff --git a/server/boot/fieldGuide.js b/server/boot/fieldGuide.js
index 038af52c08..7f29036941 100644
--- a/server/boot/fieldGuide.js
+++ b/server/boot/fieldGuide.js
@@ -4,7 +4,7 @@ var R = require('ramda'),
resources = require('../resources/resources');
module.exports = function(app) {
- var router = app.Router();
+ var router = app.loopback.Router();
var FieldGuide = app.models.FieldGuide;
router.get('/field-guide/all-articles', showAllFieldGuides);
@@ -12,6 +12,8 @@ module.exports = function(app) {
router.get('/field-guide/', returnNextFieldGuide);
router.post('/completed-field-guide/', completedFieldGuide);
+ app.use(router);
+
function returnIndividualFieldGuide(req, res, next) {
var dashedName = req.params.fieldGuideName;
if (req.user) {
diff --git a/server/boot/home.js b/server/boot/home.js
index 036d454945..56bacf5c0f 100644
--- a/server/boot/home.js
+++ b/server/boot/home.js
@@ -2,9 +2,11 @@ var message =
'Learn to Code JavaScript and get a Coding Job by Helping Nonprofits';
module.exports = function(app) {
- var router = app.Router();
+ var router = app.loopback.Router();
router.get('/', index);
+ app.use(router);
+
function index(req, res, next) {
if (req.user && !req.user.profile.picture) {
req.user.profile.picture =
diff --git a/server/boot/jobs.js b/server/boot/jobs.js
index abcda8a2b2..65d3feae99 100644
--- a/server/boot/jobs.js
+++ b/server/boot/jobs.js
@@ -1,8 +1,9 @@
module.exports = function(app) {
var Job = app.models.Job;
- var router = app.Router();
+ var router = app.loopback.Router();
router.get('/jobs', jobsDirectory);
+ app.use(router);
function jobsDirectory(req, res, next) {
Job.find({}, function(err, jobs) {
diff --git a/server/boot/nonprofits.js b/server/boot/nonprofits.js
index 507f366b49..1ba62bf5b5 100644
--- a/server/boot/nonprofits.js
+++ b/server/boot/nonprofits.js
@@ -1,11 +1,13 @@
module.exports = function(app) {
- var router = app.Router();
+ var router = app.loopback.Router();
var Nonprofit = app.models.Nonprofit;
router.get('/nonprofits/directory', nonprofitsDirectory);
router.get('/nonprofits/:nonprofitName', returnIndividualNonprofit);
+ app.use(router);
+
function nonprofitsDirectory(req, res, next) {
Nonprofit.find(
{ where: { estimatedHours: { $gt: 0 } } },
diff --git a/server/boot/passport.js b/server/boot/passport.js
index 53918dd346..53b914afc2 100644
--- a/server/boot/passport.js
+++ b/server/boot/passport.js
@@ -2,7 +2,7 @@ var passport = require('passport'),
passportConf = require('../../config/passport');
module.exports = function(app) {
- var router = app.Router();
+ var router = app.loopback.Router();
var passportOptions = {
successRedirect: '/',
failureRedirect: '/login'
@@ -64,4 +64,6 @@ module.exports = function(app) {
res.redirect(req.session.returnTo || '/');
}
);
+
+ app.use(router);
};
diff --git a/server/boot/redirects.js b/server/boot/redirects.js
index 2ad49ac83c..bac16345af 100644
--- a/server/boot/redirects.js
+++ b/server/boot/redirects.js
@@ -1,5 +1,5 @@
module.exports = function(app) {
- var router = app.Router();
+ var router = app.loopback.Router();
router.get('/nonprofit-project-instructions', function(req, res) {
res.redirect(
@@ -45,4 +45,6 @@ module.exports = function(app) {
301, '/field-guide/what-is-the-free-code-camp-privacy-policy?'
);
});
+
+ app.use(router);
};
diff --git a/server/boot/story.js b/server/boot/story.js
index d2dd96d7ba..be2a473614 100755
--- a/server/boot/story.js
+++ b/server/boot/story.js
@@ -8,7 +8,7 @@ var nodemailer = require('nodemailer'),
secrets = require('../../config/secrets');
module.exports = function(app) {
- var router = app.Router();
+ var router = app.loopback.Router();
var User = app.models.User;
var Story = app.models.Story;
@@ -27,6 +27,8 @@ module.exports = function(app) {
router.get('/news/:storyName', returnIndividualStory);
router.post('/stories/upvote/', upvote);
+ app.use(router);
+
function hotRank(timeValue, rank) {
/*
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
diff --git a/server/boot/user.js b/server/boot/user.js
index 5317fdddad..c78ff7dd45 100644
--- a/server/boot/user.js
+++ b/server/boot/user.js
@@ -11,7 +11,7 @@ var _ = require('lodash'),
resources = require('./../resources/resources');
module.exports = function(app) {
- var router = app.Router();
+ var router = app.loopback.Router();
var User = app.models.User;
router.get('/login', function(req, res) {
@@ -43,6 +43,8 @@ module.exports = function(app) {
// Ensure this is the last route!
router.get('/:username', returnUser);
+ app.use(router);
+
/**
* GET /signin
* Siginin page.
diff --git a/server/boot/utility.js b/server/boot/utility.js
index 1c78fb409c..ea45d06461 100644
--- a/server/boot/utility.js
+++ b/server/boot/utility.js
@@ -11,7 +11,7 @@ var Rx = require('rx'),
var slack = new Slack(secrets.slackHook);
module.exports = function(app) {
- var router = app.Router();
+ var router = app.loopback.Router();
var User = app.models.User;
var Challenge = app.models.Challenge;
var Story = app.models.Store;
@@ -39,6 +39,8 @@ module.exports = function(app) {
router.get('/api/slack', slackInvite);
+ app.use(router);
+
function slackInvite(req, res, next) {
if (req.user) {
if (req.user.email) {
From 415d46b5910fa7a6fd0af8a77d8024e457baa637 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Wed, 3 Jun 2015 16:35:22 -0700
Subject: [PATCH 08/16] fix add models to model-config
---
server/model-config.json | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/server/model-config.json b/server/model-config.json
index 7c86c514a9..dedcb678b2 100644
--- a/server/model-config.json
+++ b/server/model-config.json
@@ -29,5 +29,33 @@
"bonfire": {
"dataSource": "db",
"public": true
+ },
+ "challenge": {
+ "dataSource": "db",
+ "public": true
+ },
+ "comment": {
+ "dataSource": "db",
+ "public": true
+ },
+ "field-guide": {
+ "dataSource": "db",
+ "public": true
+ },
+ "job": {
+ "dataSource": "db",
+ "public": true
+ },
+ "nonprofit": {
+ "dataSource": "db",
+ "public": true
+ },
+ "story": {
+ "dataSource": "db",
+ "public": true
+ },
+ "user": {
+ "dataSource": "db",
+ "public": true
}
}
From 393f95082e6946a117df39535d53c2bf0977047d Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Wed, 3 Jun 2015 16:47:59 -0700
Subject: [PATCH 09/16] bump gulp-nodemon
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 35742ea05b..853c3e9c88 100644
--- a/package.json
+++ b/package.json
@@ -98,7 +98,7 @@
"gulp": "~3.8.8",
"gulp-eslint": "~0.9.0",
"gulp-inject": "~1.0.2",
- "gulp-nodemon": "~1.0.4",
+ "gulp-nodemon": "^2.0.3",
"mocha": "~2.0.1",
"multiline": "~1.0.1",
"supertest": "~0.15.0"
From 75f9b06e902bac2fe5e17290df86dac614dea787 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Wed, 3 Jun 2015 16:48:17 -0700
Subject: [PATCH 10/16] fix incorrect model name
---
common/models/nonprofit.json | 2 +-
common/models/story.json | 2 +-
common/models/user.json | 4 ++--
server/model-config.json | 2 +-
4 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/common/models/nonprofit.json b/common/models/nonprofit.json
index fdc3d673ce..e4347dcc6b 100644
--- a/common/models/nonprofit.json
+++ b/common/models/nonprofit.json
@@ -18,7 +18,7 @@
"projectDescription": "string",
"logoUrl": "string",
"imageUrl": "string",
- "estimatedHours": 0,
+ "estimatedHours": "number",
"interestedCampers": [],
"confirmedCampers": [],
"currentStatus": "string"
diff --git a/common/models/story.json b/common/models/story.json
index 2e46a9f606..2ce2dc5ce4 100644
--- a/common/models/story.json
+++ b/common/models/story.json
@@ -1,5 +1,5 @@
{
- "name": "bonfire",
+ "name": "story",
"base": "PersistedModel",
"trackChanges": false,
"idInjection": true,
diff --git a/common/models/user.json b/common/models/user.json
index 5bdab4a1b6..97f8f0b36e 100644
--- a/common/models/user.json
+++ b/common/models/user.json
@@ -1,6 +1,6 @@
{
- "name": "bonfire",
- "base": "PersistedModel",
+ "name": "user",
+ "base": "User",
"trackChanges": false,
"idInjection": true,
"properties": {
diff --git a/server/model-config.json b/server/model-config.json
index dedcb678b2..dd5f43e0c6 100644
--- a/server/model-config.json
+++ b/server/model-config.json
@@ -38,7 +38,7 @@
"dataSource": "db",
"public": true
},
- "field-guide": {
+ "fieldGuide": {
"dataSource": "db",
"public": true
},
From e349797a4b39bb1f5b7fe7b9f76c0c4a0081dcc7 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Wed, 3 Jun 2015 16:56:48 -0700
Subject: [PATCH 11/16] rename boot utility remove subrouter use in server.js
---
server/boot/{utility.js => randomAPIs.js} | 0
server/server.js | 27 -----------------------
2 files changed, 27 deletions(-)
rename server/boot/{utility.js => randomAPIs.js} (100%)
diff --git a/server/boot/utility.js b/server/boot/randomAPIs.js
similarity index 100%
rename from server/boot/utility.js
rename to server/boot/randomAPIs.js
diff --git a/server/server.js b/server/server.js
index f9f07a7a22..7c5581cc01 100755
--- a/server/server.js
+++ b/server/server.js
@@ -23,27 +23,12 @@ var loopback = require('loopback'),
MongoStore = require('connect-mongo')(session),
flash = require('express-flash'),
path = require('path'),
- mongoose = require('mongoose'),
passport = require('passport'),
expressValidator = require('express-validator'),
// request = require('request'),
forceDomain = require('forcedomain'),
lessMiddleware = require('less-middleware'),
- /**
- * routers.
- */
- homeRouter = require('./boot/home'),
- userRouter = require('./boot/user'),
- fieldGuideRouter = require('./boot/fieldGuide'),
- challengeMapRouter = require('./boot/challengeMap'),
- challengeRouter = require('./boot/challenge'),
- jobsRouter = require('./boot/jobs'),
- redirectsRouter = require('./boot/redirects'),
- utilityRouter = require('./boot/utility'),
- storyRouter = require('./boot/story'),
- passportRouter = require('./boot/passport'),
-
/**
* API keys and Passport configuration.
*/
@@ -201,18 +186,6 @@ app.use(function (req, res, next) {
next();
});
-// add sub routers
-app.use(fieldGuideRouter);
-app.use(challengeMapRouter);
-app.use(challengeRouter);
-app.use(jobsRouter);
-app.use(redirectsRouter);
-app.use(utilityRouter);
-app.use(storyRouter);
-app.use(passportRouter);
-app.use(homeRouter);
-app.use(userRouter);
-
/**
* OAuth sign-in routes.
*/
From 075d851374ad86074445ad9e33ba95d4ebdfabe3 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Wed, 3 Jun 2015 17:14:45 -0700
Subject: [PATCH 12/16] refactor resources to utils
---
server/boot/challenge.js | 50 ++++++------
server/boot/challengeMap.js | 6 +-
server/boot/fieldGuide.js | 6 +-
server/boot/randomAPIs.js | 2 +-
server/boot/story.js | 4 +-
server/boot/user.js | 62 ++++++++++++++-
.../{resources => utils}/constantStrings.json | 0
.../resources.js => utils/index.js} | 77 +++----------------
server/{resources => utils}/middleware.js | 0
server/{resources => utils}/resources.json | 0
10 files changed, 103 insertions(+), 104 deletions(-)
rename server/{resources => utils}/constantStrings.json (100%)
rename server/{resources/resources.js => utils/index.js} (74%)
rename server/{resources => utils}/middleware.js (100%)
rename server/{resources => utils}/resources.json (100%)
diff --git a/server/boot/challenge.js b/server/boot/challenge.js
index df85d803d4..135523087b 100644
--- a/server/boot/challenge.js
+++ b/server/boot/challenge.js
@@ -31,12 +31,12 @@
*/
var R = require('ramda'),
- resources = require('../resources/resources'),
- userMigration = require('../resources/middleware').userMigration,
+ utils = require('../utils'),
+ userMigration = require('../utils/middleware').userMigration,
MDNlinks = require('../../seed/bonfireMDNlinks');
-var challengeMapWithNames = resources.getChallengeMapWithNames();
-var challengeMapWithIds = resources.getChallengeMapWithIds();
+var challengeMapWithNames = utils.getChallengeMapWithNames();
+var challengeMapWithIds = utils.getChallengeMapWithIds();
function getMDNlinks(links) {
@@ -85,7 +85,7 @@ module.exports = function(app) {
return elem._id;
});
- req.user.uncompletedChallenges = resources.allChallengeIds()
+ req.user.uncompletedChallenges = utils.allChallengeIds()
.filter(function (elem) {
if (completed.indexOf(elem) === -1) {
return elem;
@@ -139,7 +139,7 @@ module.exports = function(app) {
return elem._id;
});
- req.user.uncompletedChallenges = resources.allChallengeIds()
+ req.user.uncompletedChallenges = utils.allChallengeIds()
.filter(function (elem) {
if (completed.indexOf(elem) === -1) {
return elem;
@@ -228,11 +228,11 @@ module.exports = function(app) {
details: challenge.description.slice(1),
tests: challenge.tests,
challengeSeed: challenge.challengeSeed,
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
+ verb: utils.randomVerb(),
+ phrase: utils.randomPhrase(),
+ compliment: utils.randomCompliment(),
challengeId: challenge._id,
- environment: resources.whichEnvironment(),
+ environment: utils.whichEnvironment(),
challengeType: challenge.challengeType
});
},
@@ -246,9 +246,9 @@ module.exports = function(app) {
details: challenge.description.slice(1),
tests: challenge.tests,
challengeSeed: challenge.challengeSeed,
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
+ verb: utils.randomVerb(),
+ phrase: utils.randomPhrase(),
+ compliment: utils.randomCompliment(),
challengeId: challenge._id,
challengeType: challenge.challengeType
});
@@ -262,9 +262,9 @@ module.exports = function(app) {
details: challenge.description,
tests: challenge.tests,
video: challenge.challengeSeed[0],
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
+ verb: utils.randomVerb(),
+ phrase: utils.randomPhrase(),
+ compliment: utils.randomCompliment(),
challengeId: challenge._id,
challengeType: challenge.challengeType
});
@@ -277,9 +277,9 @@ module.exports = function(app) {
name: challenge.name,
details: challenge.description,
video: challenge.challengeSeed[0],
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
+ verb: utils.randomVerb(),
+ phrase: utils.randomPhrase(),
+ compliment: utils.randomCompliment(),
challengeId: challenge._id,
challengeType: challenge.challengeType
});
@@ -292,9 +292,9 @@ module.exports = function(app) {
name: challenge.name,
details: challenge.description,
video: challenge.challengeSeed[0],
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
+ verb: utils.randomVerb(),
+ phrase: utils.randomPhrase(),
+ compliment: utils.randomCompliment(),
challengeId: challenge._id,
challengeType: challenge.challengeType
});
@@ -311,9 +311,9 @@ module.exports = function(app) {
details: challenge.description,
tests: challenge.tests,
challengeSeed: challenge.challengeSeed,
- verb: resources.randomVerb(),
- phrase: resources.randomPhrase(),
- compliment: resources.randomCompliment(),
+ verb: utils.randomVerb(),
+ phrase: utils.randomPhrase(),
+ compliment: utils.randomCompliment(),
bonfires: challenge,
challengeId: challenge._id,
MDNkeys: challenge.MDNlinks,
diff --git a/server/boot/challengeMap.js b/server/boot/challengeMap.js
index c6820c5020..42261ac378 100644
--- a/server/boot/challengeMap.js
+++ b/server/boot/challengeMap.js
@@ -1,7 +1,7 @@
var R = require('ramda'),
// debug = require('debug')('freecc:cntr:challengeMap'),
- resources = require('./../resources/resources'),
- middleware = require('../resources/middleware');
+ utils = require('./../utils'),
+ middleware = require('../utils/middleware');
module.exports = function(app) {
@@ -31,7 +31,7 @@ module.exports = function(app) {
.map(function(challenge) {
return challenge._id;
});
- var challengeList = resources.
+ var challengeList = utils.
getChallengeMapForDisplay(completedChallengeList);
Object.keys(challengeList).forEach(function(key) {
diff --git a/server/boot/fieldGuide.js b/server/boot/fieldGuide.js
index 7f29036941..8cebbe461b 100644
--- a/server/boot/fieldGuide.js
+++ b/server/boot/fieldGuide.js
@@ -1,7 +1,7 @@
var R = require('ramda'),
// Rx = require('rx'),
// debug = require('debug')('freecc:fieldguides'),
- resources = require('../resources/resources');
+ utils = require('../utils');
module.exports = function(app) {
var router = app.loopback.Router();
@@ -19,7 +19,7 @@ module.exports = function(app) {
if (req.user) {
var completed = req.user.completedFieldGuides;
- var uncompletedFieldGuides = resources.allFieldGuideIds()
+ var uncompletedFieldGuides = utils.allFieldGuideIds()
.filter(function (elem) {
if (completed.indexOf(elem) === -1) {
return elem;
@@ -65,7 +65,7 @@ module.exports = function(app) {
}
function showAllFieldGuides(req, res) {
- var allFieldGuideNamesAndIds = resources.allFieldGuideNamesAndIds();
+ var allFieldGuideNamesAndIds = utils.allFieldGuideNamesAndIds();
var completedFieldGuides = [];
if (req.user && req.user.completedFieldGuides) {
diff --git a/server/boot/randomAPIs.js b/server/boot/randomAPIs.js
index ea45d06461..d07ca1879a 100644
--- a/server/boot/randomAPIs.js
+++ b/server/boot/randomAPIs.js
@@ -6,7 +6,7 @@ var Rx = require('rx'),
request = require('request'),
debug = require('debug')('freecc:cntr:resources'),
- constantStrings = require('../resources/constantStrings.json'),
+ constantStrings = require('../utils/constantStrings.json'),
secrets = require('../../config/secrets');
var slack = new Slack(secrets.slackHook);
diff --git a/server/boot/story.js b/server/boot/story.js
index be2a473614..b28580aa09 100755
--- a/server/boot/story.js
+++ b/server/boot/story.js
@@ -3,7 +3,7 @@ var nodemailer = require('nodemailer'),
moment = require('moment'),
mongodb = require('mongodb'),
// debug = require('debug')('freecc:cntr:story'),
- resources = require('../resources/resources'),
+ utils = require('../utils'),
MongoClient = mongodb.MongoClient,
secrets = require('../../config/secrets');
@@ -327,7 +327,7 @@ module.exports = function(app) {
storyURL: '/news/' + story.pop().storyLink
});
}
- resources.getURLTitle(url, processResponse);
+ utils.getURLTitle(url, processResponse);
}
);
diff --git a/server/boot/user.js b/server/boot/user.js
index c78ff7dd45..271c3723e9 100644
--- a/server/boot/user.js
+++ b/server/boot/user.js
@@ -7,12 +7,13 @@ var _ = require('lodash'),
moment = require('moment'),
debug = require('debug')('freecc:cntr:userController'),
- secrets = require('../../config/secrets'),
- resources = require('./../resources/resources');
+ secrets = require('../../config/secrets');
module.exports = function(app) {
var router = app.loopback.Router();
var User = app.models.User;
+ var Story = app.models.Story;
+ var Comment = app.models.Comment;
router.get('/login', function(req, res) {
res.redirect(301, '/signin');
@@ -523,7 +524,7 @@ module.exports = function(app) {
if (err) {
return next(err);
}
- resources.updateUserStoryPictures(
+ updateUserStoryPictures(
user._id.toString(),
user.profile.picture,
user.profile.username,
@@ -820,4 +821,59 @@ module.exports = function(app) {
res.redirect('/forgot');
});
}
+
+ function updateUserStoryPictures(userId, picture, username, cb) {
+
+ var counter = 0,
+ foundStories,
+ foundComments;
+
+ Story.find({ 'author.userId': userId }, function (err, stories) {
+ if (err) {
+ return cb(err);
+ }
+ foundStories = stories;
+ counter++;
+ saveStoriesAndComments();
+ });
+
+ Comment.find({ 'author.userId': userId }, function (err, comments) {
+ if (err) {
+ return cb(err);
+ }
+ foundComments = comments;
+ counter++;
+ saveStoriesAndComments();
+ });
+
+ function saveStoriesAndComments() {
+ if (counter !== 2) {
+ return;
+ }
+ var tasks = [];
+ R.forEach(function (comment) {
+ comment.author.picture = picture;
+ comment.author.username = username;
+ comment.markModified('author');
+ tasks.push(function (cb) {
+ comment.save(cb);
+ });
+ }, foundComments);
+
+ R.forEach(function (story) {
+ story.author.picture = picture;
+ story.author.username = username;
+ story.markModified('author');
+ tasks.push(function (cb) {
+ story.save(cb);
+ });
+ }, foundStories);
+ async.parallel(tasks, function (err) {
+ if (err) {
+ return cb(err);
+ }
+ cb();
+ });
+ }
+ }
};
diff --git a/server/resources/constantStrings.json b/server/utils/constantStrings.json
similarity index 100%
rename from server/resources/constantStrings.json
rename to server/utils/constantStrings.json
diff --git a/server/resources/resources.js b/server/utils/index.js
similarity index 74%
rename from server/resources/resources.js
rename to server/utils/index.js
index 865792e050..a0f082bd4a 100644
--- a/server/resources/resources.js
+++ b/server/utils/index.js
@@ -1,18 +1,15 @@
-var async = require('async'),
- path = require('path'),
- // debug = require('debug')('freecc:cntr:resources'),
- cheerio = require('cheerio'),
- request = require('request'),
- R = require('ramda'),
- _ = require('lodash'),
- fs = require('fs'),
+var path = require('path'),
+ // debug = require('debug')('freecc:cntr:resources'),
+ cheerio = require('cheerio'),
+ request = require('request'),
+ R = require('ramda'),
+ _ = require('lodash'),
+ fs = require('fs'),
- Story = require('../../common/models/Story'),
- Comment = require('../../common/models/Comment'),
- resources = require('./resources.json'),
- nonprofits = require('../../seed/nonprofits.json'),
- fieldGuides = require('../../seed/field-guides.json');
+ resources = require('./resources.json'),
+ nonprofits = require('../../seed/nonprofits.json'),
+ fieldGuides = require('../../seed/field-guides.json');
/**
* Cached values
@@ -215,59 +212,5 @@ module.exports = {
}
});
})();
- },
-
- updateUserStoryPictures: function (userId, picture, username, cb) {
-
- var counter = 0,
- foundStories,
- foundComments;
-
- Story.find({'author.userId': userId}, function (err, stories) {
- if (err) {
- return cb(err);
- }
- foundStories = stories;
- counter++;
- saveStoriesAndComments();
- });
- Comment.find({'author.userId': userId}, function (err, comments) {
- if (err) {
- return cb(err);
- }
- foundComments = comments;
- counter++;
- saveStoriesAndComments();
- });
-
- function saveStoriesAndComments() {
- if (counter !== 2) {
- return;
- }
- var tasks = [];
- R.forEach(function (comment) {
- comment.author.picture = picture;
- comment.author.username = username;
- comment.markModified('author');
- tasks.push(function (cb) {
- comment.save(cb);
- });
- }, foundComments);
-
- R.forEach(function (story) {
- story.author.picture = picture;
- story.author.username = username;
- story.markModified('author');
- tasks.push(function (cb) {
- story.save(cb);
- });
- }, foundStories);
- async.parallel(tasks, function (err) {
- if (err) {
- return cb(err);
- }
- cb();
- });
- }
}
};
diff --git a/server/resources/middleware.js b/server/utils/middleware.js
similarity index 100%
rename from server/resources/middleware.js
rename to server/utils/middleware.js
diff --git a/server/resources/resources.json b/server/utils/resources.json
similarity index 100%
rename from server/resources/resources.json
rename to server/utils/resources.json
From 66eff3129468536c23f1ed90035fc3ac7d49e50e Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Thu, 4 Jun 2015 10:52:12 -0700
Subject: [PATCH 13/16] Adding passport config stuff
---
common/config.global.js | 7 ++
common/models/User-Credential.json | 16 ++++
common/models/User-Identity.js | 27 ++++++
common/models/User-Identity.json | 16 ++++
common/models/User.js | 19 ++++
package.json | 1 +
server/config.development.js | 18 ++++
server/config.local.js | 5 +
server/datasources.development.js | 6 ++
server/model-config.json | 8 ++
server/passport-providers.js | 149 +++++++++++++++++++++++++++++
server/server.js | 69 ++++++++-----
12 files changed, 317 insertions(+), 24 deletions(-)
create mode 100644 common/config.global.js
create mode 100644 common/models/User-Credential.json
create mode 100644 common/models/User-Identity.js
create mode 100644 common/models/User-Identity.json
create mode 100644 common/models/User.js
create mode 100644 server/config.development.js
create mode 100644 server/config.local.js
create mode 100644 server/datasources.development.js
create mode 100644 server/passport-providers.js
diff --git a/common/config.global.js b/common/config.global.js
new file mode 100644
index 0000000000..fa435086c2
--- /dev/null
+++ b/common/config.global.js
@@ -0,0 +1,7 @@
+// The path where to mount the REST API app
+exports.restApiRoot = '/api';
+//
+// The URL where the browser client can access the REST API is available
+// Replace with a full url (including hostname) if your client is being
+// served from a different server than your REST API.
+exports.restApiUrl = exports.restApiRoot;
diff --git a/common/models/User-Credential.json b/common/models/User-Credential.json
new file mode 100644
index 0000000000..15a271faa4
--- /dev/null
+++ b/common/models/User-Credential.json
@@ -0,0 +1,16 @@
+{
+ "name": "userCredential",
+ "plural": "userCredentials",
+ "base": "UserCredential",
+ "properties": {},
+ "validations": [],
+ "relations": {
+ "user": {
+ "type": "belongsTo",
+ "model": "user",
+ "foreignKey": "userId"
+ }
+ },
+ "acls": [],
+ "methods": []
+}
diff --git a/common/models/User-Identity.js b/common/models/User-Identity.js
new file mode 100644
index 0000000000..9879ac1f29
--- /dev/null
+++ b/common/models/User-Identity.js
@@ -0,0 +1,27 @@
+//var debug = require('debug')('freecc:models:userIdent');
+//
+//module.exports = function(UserIdent) {
+//
+// UserIdent.observe('before save', function(ctx, next) {
+//
+// var userIdent = ctx.instance;
+// userIdent.user(function(err, user) {
+// if (err) { return next(err); }
+// debug('got user', user.username);
+//
+// // check if user has picture
+// // set user.picture from twitter
+// if (!user.picture) {
+// debug('use has no pic');
+// user.picture = userIdent.profile.photos[0].value;
+// user.save(function(err) {
+// if (err) { return next(err); }
+// next();
+// });
+// } else {
+// debug('exiting after user ident');
+// next();
+// }
+// });
+// });
+//};
diff --git a/common/models/User-Identity.json b/common/models/User-Identity.json
new file mode 100644
index 0000000000..d63e5c0af3
--- /dev/null
+++ b/common/models/User-Identity.json
@@ -0,0 +1,16 @@
+{
+ "name": "userIdentity",
+ "plural": "userIdentities",
+ "base": "UserIdentity",
+ "properties": {},
+ "validations": [],
+ "relations": {
+ "user": {
+ "type": "belongsTo",
+ "model": "user",
+ "foreignKey": "userId"
+ }
+ },
+ "acls": [],
+ "methods": []
+}
diff --git a/common/models/User.js b/common/models/User.js
new file mode 100644
index 0000000000..83273683f5
--- /dev/null
+++ b/common/models/User.js
@@ -0,0 +1,19 @@
+var debug = require('debug')('freecc:models:user');
+
+module.exports = function(User) {
+ debug('setting up user hooks');
+ /*
+ * NOTE(berks): not sure if this is still needed
+ User.observe('before save', function setUsername(ctx, next) {
+ // set username from twitter
+ if (ctx.instance.username && ctx.instance.username.match(/twitter/g)) {
+ ctx.instance.username =
+ ctx.instance.username.match(/twitter/g) ?
+ ctx.instance.username.split('.').pop().toLowerCase() :
+ ctx.instance.username;
+ debug('username set', ctx.instance.username);
+ }
+ next();
+ });
+ */
+};
diff --git a/package.json b/package.json
index 853c3e9c88..0560e3c89f 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
"lodash": "~2.4.1",
"loopback": "^2.18.0",
"loopback-boot": "^2.8.0",
+ "loopback-component-passport": "^1.3.1",
"loopback-connector-mongodb": "^1.10.0",
"lusca": "~1.0.2",
"method-override": "~2.3.0",
diff --git a/server/config.development.js b/server/config.development.js
new file mode 100644
index 0000000000..d7df882ace
--- /dev/null
+++ b/server/config.development.js
@@ -0,0 +1,18 @@
+module.exports = {
+ host: '127.0.0.1',
+ sessionSecret: process.env.SESSION_SECRET,
+
+ trello: {
+ key: process.env.TRELLO_KEY,
+ secret: process.env.TRELLO_SECRET
+ },
+
+ blogger: {
+ key: process.env.BLOGGER_KEY
+ },
+
+ github: {
+ clientID: process.env.GITHUB_ID,
+ clientSecret: process.env.GITHUB_SECRET
+ }
+};
diff --git a/server/config.local.js b/server/config.local.js
new file mode 100644
index 0000000000..e5058ced89
--- /dev/null
+++ b/server/config.local.js
@@ -0,0 +1,5 @@
+var globalConfig = require('../common/config.global');
+
+module.exports = {
+ restApiRoot: globalConfig.restApi
+};
diff --git a/server/datasources.development.js b/server/datasources.development.js
new file mode 100644
index 0000000000..f7777c5e9f
--- /dev/null
+++ b/server/datasources.development.js
@@ -0,0 +1,6 @@
+module.exports = {
+ db: {
+ connector: 'mongodb',
+ url: process.env.MONGOHQ_URL
+ }
+};
diff --git a/server/model-config.json b/server/model-config.json
index dd5f43e0c6..23f21c295b 100644
--- a/server/model-config.json
+++ b/server/model-config.json
@@ -57,5 +57,13 @@
"user": {
"dataSource": "db",
"public": true
+ },
+ "userCredential": {
+ "dataSource": "db",
+ "public": true
+ },
+ "userIdentity": {
+ "dataSource": "db",
+ "public": true
}
}
diff --git a/server/passport-providers.js b/server/passport-providers.js
new file mode 100644
index 0000000000..36ae610834
--- /dev/null
+++ b/server/passport-providers.js
@@ -0,0 +1,149 @@
+var successRedirect = '/';
+var failureRedirect = '/login';
+module.exports = {
+ local: {
+ provider: 'local',
+ module: 'passport-local',
+ usernameField: 'email',
+ passwordField: 'password',
+ authPath: '/auth/local',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ failureFlash: true
+ },
+ 'facebook-login': {
+ provider: 'facebook',
+ module: 'passport-facebook',
+ clientID: process.env.FACEBOOK_ID,
+ clientSecret: process.env.FACEBOOK_SECRET,
+ authPath: '/auth/facebook',
+ callbackURL: '/auth/facebook/callback',
+ callbackPath: '/auth/facebook/callback',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ scope: ['email'],
+ failureFlash: true
+ },
+ 'facebook-link': {
+ provider: 'facebook',
+ module: 'passport-facebook',
+ clientID: process.env.FACEBOOK_ID,
+ clientSecret: process.env.FACEBOOK_SECRET,
+ authPath: '/link/facebook',
+ callbackURL: '/link/facebook/callback',
+ callbackPath: '/link/facebook/callback',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ scope: ['email', 'user_likes'],
+ link: true,
+ failureFlash: true
+ },
+ 'github-login': {
+ provider: 'github',
+ module: 'passport-github',
+ clientID: process.env.GITHUB_ID,
+ clientSecret: process.env.GITHUB_SECRET,
+ authPath: '/auth/github',
+ callbackURL: '/auth/github/callback',
+ callbackPath: '/auth/github/callback',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ scope: ['email'],
+ failureFlash: true
+ },
+ 'github-link': {
+ provider: 'github',
+ module: 'passport-github',
+ clientID: process.env.GITHUB_ID,
+ clientSecret: process.env.GITHUB_SECRET,
+ authPath: '/link/github',
+ callbackURL: '/link/github/callback',
+ callbackPath: '/link/github/callback',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ scope: ['email', 'user_likes'],
+ link: true,
+ failureFlash: true
+ },
+ 'google-login': {
+ provider: 'google',
+ module: 'passport-google-oauth2',
+ clientID: process.env.GOOGLE_ID,
+ clientSecret: process.env.GOOGLE_SECRET,
+ authPath: '/auth/google',
+ callbackURL: '/auth/google/callback',
+ callbackPath: '/auth/google/callback',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ scope: ['email', 'profile'],
+ failureFlash: true
+ },
+ 'google-link': {
+ provider: 'google',
+ module: 'passport-google-oauth2',
+ clientID: process.env.GOOGLE_ID,
+ clientSecret: process.env.GOOGLE_SECRET,
+ authPath: '/link/google',
+ callbackURL: '/link/google/callback',
+ callbackPath: '/link/google/callback',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ scope: ['email', 'profile'],
+ link: true,
+ failureFlash: true
+ },
+ 'twitter-login': {
+ provider: 'twitter',
+ authScheme: 'oauth',
+ module: 'passport-twitter',
+ authPath: '/auth/twitter',
+ callbackURL: '/auth/twitter/callback',
+ callbackPath: '/auth/twitter/callback',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ consumerKey: process.env.TWITTER_KEY,
+ consumerSecret: process.env.TWITTER_SECRET,
+ failureFlash: true
+ },
+ 'twitter-link': {
+ provider: 'twitter',
+ authScheme: 'oauth',
+ module: 'passport-twitter',
+ authPath: '/link/twitter',
+ callbackURL: '/link/twitter/callback',
+ callbackPath: '/link/twitter/callback',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ consumerKey: process.env.TWITTER_KEY,
+ consumerSecret: process.env.TWITTER_SECRET,
+ failureFlash: true
+ },
+ 'linkedin-login': {
+ provider: 'linkedin',
+ authScheme: 'oauth',
+ module: 'passport-linkedin-oauth2',
+ authPath: '/auth/linkedin',
+ callbackURL: '/auth/linkedin/callback',
+ callbackPath: '/auth/linkedin/callback',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ clientID: process.env.LINKEDIN_ID,
+ clientSecret: process.env.LINKEDIN_SECRET,
+ scope: ['r_fullprofile', 'r_emailaddress'],
+ failureFlash: true
+ },
+ 'linkedin-link': {
+ provider: 'linkedin',
+ authScheme: 'oauth',
+ module: 'passport-linkedin-oauth2',
+ authPath: '/link/linkedin',
+ callbackURL: '/link/linkedin/callback',
+ callbackPath: '/link/linkedin/callback',
+ successRedirect: successRedirect,
+ failureRedirect: failureRedirect,
+ clientID: process.env.LINKEDIN_ID,
+ clientSecret: process.env.LINKEDIN_SECRET,
+ scope: ['r_fullprofile', 'r_emailaddress'],
+ failureFlash: true
+ }
+};
diff --git a/server/server.js b/server/server.js
index 7c5581cc01..06fd5d00c1 100755
--- a/server/server.js
+++ b/server/server.js
@@ -9,35 +9,39 @@ process.on('uncaughtException', function (err) {
process.exit(1); // eslint-disable-line
});
-var loopback = require('loopback'),
- boot = require('loopback-boot'),
- accepts = require('accepts'),
- cookieParser = require('cookie-parser'),
- compress = require('compression'),
- session = require('express-session'),
- logger = require('morgan'),
- errorHandler = require('errorhandler'),
- methodOverride = require('method-override'),
- bodyParser = require('body-parser'),
- helmet = require('helmet'),
- MongoStore = require('connect-mongo')(session),
- flash = require('express-flash'),
- path = require('path'),
- passport = require('passport'),
- expressValidator = require('express-validator'),
- // request = require('request'),
- forceDomain = require('forcedomain'),
- lessMiddleware = require('less-middleware'),
+var R = require('ramda'),
+ loopback = require('loopback'),
+ boot = require('loopback-boot'),
+ accepts = require('accepts'),
+ cookieParser = require('cookie-parser'),
+ compress = require('compression'),
+ session = require('express-session'),
+ logger = require('morgan'),
+ errorHandler = require('errorhandler'),
+ methodOverride = require('method-override'),
+ bodyParser = require('body-parser'),
+ helmet = require('helmet'),
+ MongoStore = require('connect-mongo')(session),
+ flash = require('express-flash'),
+ path = require('path'),
+ passport = require('passport'),
+ expressValidator = require('express-validator'),
+ forceDomain = require('forcedomain'),
+ lessMiddleware = require('less-middleware'),
- /**
- * API keys and Passport configuration.
- */
- secrets = require('./../config/secrets');
+ passportProviders = require('./passport-providers'),
+ /**
+ * API keys and Passport configuration.
+ */
+ secrets = require('./../config/secrets');
/**
* Create Express server.
*/
var app = loopback();
+var PassportConfigurator =
+ require('loopback-component-passport').PassportConfigurator;
+var passportConfigurator = new PassportConfigurator(app);
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
@@ -162,6 +166,7 @@ app.use(helmet.csp({
safari5: false
}));
+passportConfigurator.init();
app.use(function (req, res, next) {
// Make user object available in templates.
@@ -173,7 +178,11 @@ app.use(
loopback.static(path.join(__dirname, '../public'), { maxAge: 86400000 })
);
-boot(app, __dirname);
+boot(app, {
+ appRootDir: __dirname,
+ dev: process.env.NODE_ENV
+});
+
app.use(function (req, res, next) {
// Remember original destination before login.
var path = req.path.split('/')[1];
@@ -186,6 +195,18 @@ app.use(function (req, res, next) {
next();
});
+passportConfigurator.setupModels({
+ userModel: app.models.user,
+ userIdentityModel: app.models.userIdentity,
+ userCredentialModel: app.models.userCredential
+});
+
+R.keys(passportProviders).map(function(strategy) {
+ var config = passportProviders[strategy];
+ config.session = config.session !== false;
+ passportConfigurator.configureProvider(strategy, config);
+});
+
/**
* OAuth sign-in routes.
*/
From 4a696462d2944113ef5c3b27438fe69b110f88a8 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Thu, 4 Jun 2015 10:53:01 -0700
Subject: [PATCH 14/16] Add reactify branch react components
---
common/screens/App.jsx | 19 ++++
common/screens/Router.jsx | 34 ++++++
common/screens/bonfires/Actions.js | 63 +++++++++++
common/screens/bonfires/Bonfires.jsx | 99 +++++++++++++++++
common/screens/bonfires/Results.jsx | 62 +++++++++++
common/screens/bonfires/SidePanel.jsx | 129 ++++++++++++++++++++++
common/screens/bonfires/Store.js | 67 +++++++++++
common/screens/bonfires/executeBonfire.js | 27 +++++
common/screens/bonfires/index.js | 1 +
common/screens/context/Actions.js | 34 ++++++
common/screens/context/Store.js | 18 +++
common/screens/displayCode/Display.jsx | 51 +++++++++
common/screens/displayCode/index.js | 1 +
common/screens/editor/Editor.jsx | 91 +++++++++++++++
common/screens/editor/index.js | 1 +
common/screens/footer/Footer.jsx | 106 ++++++++++++++++++
common/screens/footer/index.js | 1 +
common/screens/nav/Nav.jsx | 81 ++++++++++++++
common/screens/nav/NavItem.jsx | 66 +++++++++++
common/screens/nav/index.js | 1 +
20 files changed, 952 insertions(+)
create mode 100644 common/screens/App.jsx
create mode 100644 common/screens/Router.jsx
create mode 100644 common/screens/bonfires/Actions.js
create mode 100644 common/screens/bonfires/Bonfires.jsx
create mode 100644 common/screens/bonfires/Results.jsx
create mode 100644 common/screens/bonfires/SidePanel.jsx
create mode 100644 common/screens/bonfires/Store.js
create mode 100644 common/screens/bonfires/executeBonfire.js
create mode 100644 common/screens/bonfires/index.js
create mode 100644 common/screens/context/Actions.js
create mode 100644 common/screens/context/Store.js
create mode 100644 common/screens/displayCode/Display.jsx
create mode 100644 common/screens/displayCode/index.js
create mode 100644 common/screens/editor/Editor.jsx
create mode 100644 common/screens/editor/index.js
create mode 100644 common/screens/footer/Footer.jsx
create mode 100644 common/screens/footer/index.js
create mode 100644 common/screens/nav/Nav.jsx
create mode 100644 common/screens/nav/NavItem.jsx
create mode 100644 common/screens/nav/index.js
diff --git a/common/screens/App.jsx b/common/screens/App.jsx
new file mode 100644
index 0000000000..a0756abc14
--- /dev/null
+++ b/common/screens/App.jsx
@@ -0,0 +1,19 @@
+var React = require('react'),
+ RouteHandler = require('react-router').RouteHandler,
+
+ // ## components
+ Nav = require('./nav'),
+ Footer = require('./footer');
+
+var App = React.createClass({
+ render: function() {
+ return (
+
+
+
+
+
+ );
+ }
+});
+module.exports = App;
diff --git a/common/screens/Router.jsx b/common/screens/Router.jsx
new file mode 100644
index 0000000000..650c4107ad
--- /dev/null
+++ b/common/screens/Router.jsx
@@ -0,0 +1,34 @@
+var React = require('react'),
+
+ // react router
+ Router = require('react-router'),
+ Route = Router.Route,
+ // NotFound = Router.NotFoundRoute,
+ DefaultRoute = Router.DefaultRoute,
+
+ // # Components
+ App = require('./App.jsx'),
+ Bonfires = require('./bonfires');
+
+var routes = (
+
+
+
+
+
+
+);
+
+module.exports = function(Location) {
+ return Router.create({
+ routes: routes,
+ location: Location
+ });
+};
diff --git a/common/screens/bonfires/Actions.js b/common/screens/bonfires/Actions.js
new file mode 100644
index 0000000000..1930b48890
--- /dev/null
+++ b/common/screens/bonfires/Actions.js
@@ -0,0 +1,63 @@
+var Action = require('thundercats').Action,
+ executeBonfire = require('./executeBonfire'),
+ getModel = require('../../utils/getModel'),
+ debug = require('debug')('freecc:common:bonfires');
+
+var BonfireActions = Action.createActions([
+ 'setUserCode',
+ 'testUserCode',
+ 'setResults',
+ 'setDisplay',
+ 'setBonfire',
+ 'getBonfire',
+ 'handleBonfireError',
+ 'openCompletionModal'
+]);
+
+BonfireActions
+ .getBonfire
+ .subscribe(function(params) {
+ var Bonfire = getModel('bonfire');
+ var bonfireName = params.bonfireName ?
+ params.bonfireName.replace(/\-/g, ' ') :
+ 'meet bonfire';
+ debug('getting bonfire for: ', bonfireName);
+ var regQuery = { name: { like: bonfireName, options: 'i' } };
+ Bonfire.find(
+ { where: regQuery },
+ function(err, bonfire) {
+ if (err) {
+ return debug('bonfire get err', err);
+ }
+ if (!bonfire || bonfire.length < 1) {
+ return debug('404 no bonfire found for ', bonfireName);
+ }
+ bonfire = bonfire.pop();
+ if (bonfire) {
+ debug(
+ 'found bonfire %s for route %s',
+ bonfire.name,
+ bonfireName
+ );
+ }
+ BonfireActions.setBonfire(bonfire);
+ }
+ );
+ });
+
+
+BonfireActions
+ .testUserCode
+ .subscribe(function({ userCode, tests }) {
+ debug('test bonfire');
+ executeBonfire(userCode, tests, function(err, { output, results }) {
+ if (err) {
+ debug('error running tests', err);
+ return BonfireActions.setDisplay(err);
+ }
+ BonfireActions.setDisplay(output);
+ BonfireActions.setResults(results);
+ });
+ });
+
+module.exports = BonfireActions;
diff --git a/common/screens/bonfires/Bonfires.jsx b/common/screens/bonfires/Bonfires.jsx
new file mode 100644
index 0000000000..9da6db9320
--- /dev/null
+++ b/common/screens/bonfires/Bonfires.jsx
@@ -0,0 +1,99 @@
+var React = require('react'),
+
+ // ## mixins
+ { ObservableStateMixin } = require('thundercats'),
+
+ // ## components
+ SidePanel = require('./SidePanel.jsx'),
+ Results = require('./Results.jsx'),
+ Display = require('../displayCode'),
+ Editor = require('../editor'),
+ { Grid, Row, Col } = require('react-bootstrap'),
+
+ // ## flux
+ BonfireActions = require('./Actions'),
+ BonfireStore = require('./Store');
+
+var Bonfire = React.createClass({
+
+ mixins: [ObservableStateMixin],
+
+ contextTypes: {
+ makePath: React.PropTypes.func.isRequired,
+ replaceWith: React.PropTypes.func.isRequired
+ },
+
+ getObservable: function() {
+ return BonfireStore;
+ },
+
+ componentDidMount: function() {
+ // get history object
+ var his = typeof window !== 'undefined' ? window.history : null;
+ // spinal-case bonfireName
+ var bonfireName = this.state.name.toLowerCase().replace(/\s/g, '-');
+ // create proper URI from react-router
+ var path = this.context.makePath('bonfires', { bonfireName: bonfireName });
+
+ // if html5 push state exists, update URI
+ // else we are using hash location and should just cause a re render
+ if (his) {
+ his.replaceState({ path: path }, '', path);
+ } else {
+ this.context.replaceWith('bonfires', { bonfireName: bonfireName});
+ }
+ },
+
+ _onTestBonfire: function() {
+ BonfireActions.testUserCode({
+ userCode: this.state.userCode,
+ tests: this.state.tests
+ });
+ },
+
+ render: function() {
+ var {
+ name,
+ userCode,
+ difficulty,
+ description,
+ results,
+ display
+ } = this.state;
+ var brief = description.slice(0, 1).pop();
+
+ // convert bonfire difficulty from floating point string
+ // to integer.
+ var difficultyInt = Math.floor(+difficulty);
+
+ return (
+
+
+
+ 1 ? description : [] }/>
+
+
+
+
+
+
+
+
+ );
+ }
+});
+
+module.exports = Bonfire;
diff --git a/common/screens/bonfires/Results.jsx b/common/screens/bonfires/Results.jsx
new file mode 100644
index 0000000000..8e0e11b449
--- /dev/null
+++ b/common/screens/bonfires/Results.jsx
@@ -0,0 +1,62 @@
+var React = require('react'),
+ classNames = require('classnames'),
+ { Grid, Row, Col } = require('react-bootstrap');
+
+var Results = React.createClass({
+
+ propTypes: {
+ results: React.PropTypes.array
+ },
+
+ _renderText: function(text, textClass) {
+ return (
+
+ { text }
+
+ );
+ },
+
+ _renderResult: function(results) {
+ return results.map(function(result, idx) {
+ var err = result.err;
+ var iconClass = {
+ 'ion-close-circled big-error-icon': err,
+ 'ion-checkmark-circled big-success-icon': !err
+ };
+ var textClass = {
+ 'test-output wrappable': true,
+ 'test-vertical-center': !err
+ };
+ return (
+
+
+
+
+
+ { this._renderText(result.text, textClass) }
+ { err ? this._renderText(err, textClass) : null }
+
+
+
+ );
+ }.bind(this));
+ },
+
+ render: function() {
+ var results = this.props.results;
+ if (!results || results.length && results.length === 0) {
+ return null;
+ }
+ return (
+
+ { this._renderResult(this.props.results) }
+
+ );
+ }
+});
+
+module.exports = Results;
diff --git a/common/screens/bonfires/SidePanel.jsx b/common/screens/bonfires/SidePanel.jsx
new file mode 100644
index 0000000000..078e9fe72b
--- /dev/null
+++ b/common/screens/bonfires/SidePanel.jsx
@@ -0,0 +1,129 @@
+var React = require('react'),
+
+ // ## components
+ {
+ Well,
+ Row,
+ Col,
+ Button,
+ } = require('react-bootstrap');
+
+var SidePanel = React.createClass({
+
+ propTypes: {
+ name: React.PropTypes.string,
+ brief: React.PropTypes.string,
+ description: React.PropTypes.array,
+ difficulty: React.PropTypes.number,
+ onTestBonfire: React.PropTypes.func
+ },
+
+ getDefaultProps: function() {
+ return {
+ name: 'Welcome to Bonfires!',
+ difficulty: 5,
+ brief: 'This is a brief description'
+ };
+ },
+
+ getInitialState: function() {
+ return {
+ isMoreInfoOpen: false
+ };
+ },
+
+ _toggleMoreInfo: function() {
+ this.setState({
+ isMoreInfoOpen: !this.state.isMoreInfoOpen
+ });
+ },
+
+ _renderFlames: function() {
+ var difficulty = this.props.difficulty;
+
+ return [1, 2, 3, 4, 5].map(num => {
+ var className = 'ion-ios-flame';
+ if (num > difficulty) {
+ className += '-outline';
+ }
+ return (
+
+ );
+ });
+ },
+
+ _renderMoreInfo: function(isDescription) {
+ var description = this.props.description.map((sentance, index) => {
+ return { sentance }
;
+ });
+
+ if (isDescription && this.state.isMoreInfoOpen) {
+ return (
+
+
+ { description }
+
+
+ );
+ }
+ return null;
+ },
+
+ _renderMoreInfoButton: function(isDescription) {
+ if (isDescription) {
+ return (
+
+
+ More information
+
+ );
+ }
+ return null;
+ },
+
+ render: function() {
+ var isDescription = this.props.description &&
+ this.props.description.length > 1;
+
+ return (
+
+
{ this.props.name }
+
+
+ Difficulty:
+ { this._renderFlames() }
+
+
+
+
+
+
+
{ this.props.brief }
+
+ { this._renderMoreInfo(isDescription) }
+ { this._renderMoreInfoButton(isDescription) }
+
+
+
+
+
+
+ Run Code (ctrl + enter)
+
+
+
+ );
+ }
+});
+
+module.exports = SidePanel;
diff --git a/common/screens/bonfires/Store.js b/common/screens/bonfires/Store.js
new file mode 100644
index 0000000000..4a83c223d6
--- /dev/null
+++ b/common/screens/bonfires/Store.js
@@ -0,0 +1,67 @@
+var BonfiresActions = require('./Actions');
+var { Store, setStateUtil } = require('thundercats');
+
+var BonfiresStore = Store.create({
+
+ getInitialValue: function() {
+ return {
+ userCode: 'console.log(\'FreeCodeCamp!\')',
+ difficulty: 0,
+ description: [
+ 'default state'
+ ],
+ tests: [],
+ results: null
+ };
+ },
+
+ getOperations: function() {
+ var {
+ setBonfire,
+ setUserCode,
+ setResults,
+ setDisplay
+ } = BonfiresActions;
+
+ return [
+ setBonfire
+ .map(function(bonfire) {
+ var {
+ name,
+ description,
+ difficulty,
+ tests
+ } = bonfire;
+ var userCode = bonfire.challengeSeed;
+ return {
+ name,
+ userCode,
+ tests,
+ description,
+ difficulty
+ };
+ })
+ .map(setStateUtil),
+
+ setUserCode
+ .map(function(userCode) {
+ return { userCode };
+ })
+ .map(setStateUtil),
+
+ setDisplay
+ .map(function(display) {
+ return { display };
+ })
+ .map(setStateUtil),
+
+ setResults
+ .map(function(results) {
+ return { results };
+ })
+ .map(setStateUtil)
+ ];
+ }
+});
+
+module.exports = BonfiresStore;
diff --git a/common/screens/bonfires/executeBonfire.js b/common/screens/bonfires/executeBonfire.js
new file mode 100644
index 0000000000..0d1795894a
--- /dev/null
+++ b/common/screens/bonfires/executeBonfire.js
@@ -0,0 +1,27 @@
+var debug = require('debug')('freecc:executebonfire');
+var {
+ addTests,
+ runTests,
+ testCode
+} = require('../../utils');
+
+module.exports = executeBonfire;
+
+function executeBonfire(userCode, tests, cb) {
+
+ // TODO: move this into componentDidMount
+ // ga('send', 'event', 'Bonfire', 'ran-code', bonfireName);
+ var testSalt = Math.random();
+ var { preppedCode, userTests } = addTests(userCode, tests, testSalt);
+
+ debug('sending code to web worker for testing');
+ testCode(preppedCode, function(err, data) {
+ if (err) { return cb(err); }
+ var results = runTests(userTests, data, testSalt);
+ debug('testing complete', results);
+ cb(null, {
+ output: data.output,
+ results
+ });
+ });
+}
diff --git a/common/screens/bonfires/index.js b/common/screens/bonfires/index.js
new file mode 100644
index 0000000000..5557fd9f16
--- /dev/null
+++ b/common/screens/bonfires/index.js
@@ -0,0 +1 @@
+module.exports = require('./Bonfires.jsx');
diff --git a/common/screens/context/Actions.js b/common/screens/context/Actions.js
new file mode 100644
index 0000000000..83e06c38e1
--- /dev/null
+++ b/common/screens/context/Actions.js
@@ -0,0 +1,34 @@
+var debug = require('debug')('freecc:context'),
+ BonfireActions = require('../bonfires/Actions'),
+ BonfireStore = require('../bonfires/Store');
+
+var {
+ Action,
+ waitFor
+} = require('thundercats');
+
+var actions = Action.createActions([
+ 'setContext',
+ 'renderToUser'
+]);
+
+actions
+ .setContext
+ .filter(function(ctx) {
+ return ctx.state.path.indexOf('/bonfire') !== -1;
+ })
+ .subscribe(function(ctx) {
+ debug('set ctx');
+ BonfireActions.getBonfire(ctx.state.params);
+ waitFor(BonfireStore)
+ .firstOrDefault()
+ .catch(function(err) {
+ // handle timeout error
+ debug('err', err);
+ })
+ .subscribe(function() {
+ actions.renderToUser(ctx);
+ });
+ });
+
+module.exports = actions;
diff --git a/common/screens/context/Store.js b/common/screens/context/Store.js
new file mode 100644
index 0000000000..f94308ace1
--- /dev/null
+++ b/common/screens/context/Store.js
@@ -0,0 +1,18 @@
+var Store = require('thundercats').Store,
+ ContextActions = require('./Actions');
+
+var ContextStore = Store.create({
+ getInitialValue: function() {
+ return {};
+ },
+
+ getOperations: function() {
+ return ContextActions
+ .renderToUser
+ .map(function(ctx) {
+ return { value: ctx };
+ });
+ }
+});
+
+module.exports = ContextStore;
diff --git a/common/screens/displayCode/Display.jsx b/common/screens/displayCode/Display.jsx
new file mode 100644
index 0000000000..9eb60db8da
--- /dev/null
+++ b/common/screens/displayCode/Display.jsx
@@ -0,0 +1,51 @@
+var React = require('react'),
+ Tailspin = require('tailspin');
+
+var Editor = React.createClass({
+
+ propTypes: {
+ value: React.PropTypes.string
+ },
+
+ getDefaultProps: function() {
+ return {
+ value: [
+ '/**',
+ '* Your output will go here.',
+ '* Console.log() -type statements',
+ '* will appear in your browser\'s',
+ '* DevTools JavaScript console.',
+ '**/'
+ ].join('\n')
+ };
+ },
+
+ render: function() {
+ var value = this.props.value;
+ var options = {
+ lineNumbers: false,
+ lineWrapping: true,
+ mode: 'text',
+ readOnly: 'noCursor',
+ textAreaClassName: 'hide-textarea',
+ theme: 'monokai',
+ value: value
+ };
+
+ var config = {
+ setSize: ['100%', '100%']
+ };
+
+ return (
+
+ );
+ }
+});
+
+module.exports = Editor;
diff --git a/common/screens/displayCode/index.js b/common/screens/displayCode/index.js
new file mode 100644
index 0000000000..d3e3560174
--- /dev/null
+++ b/common/screens/displayCode/index.js
@@ -0,0 +1 @@
+module.exports = require('./Display.jsx');
diff --git a/common/screens/editor/Editor.jsx b/common/screens/editor/Editor.jsx
new file mode 100644
index 0000000000..8e72f5d4a0
--- /dev/null
+++ b/common/screens/editor/Editor.jsx
@@ -0,0 +1,91 @@
+var React = require('react'),
+ debug = require('debug')('freecc:comp:editor'),
+ jshint = require('jshint').JSHINT,
+ Tailspin = require('tailspin');
+
+var Editor = React.createClass({
+
+ propTypes: {
+ onValueChange: React.PropTypes.func,
+ value: React.PropTypes.string
+ },
+
+ getDefaultProps: function() {
+ return {
+ value: 'console.log(\'freeCodeCamp is awesome\')'
+ };
+ },
+
+ getInitialState: function() {
+ return {
+ value: this.props.value
+ };
+ },
+
+ render: function() {
+ var options = {
+ autoCloseBrackets: true,
+ gutters: ['CodeMirror-lint-markers'],
+ lint: true,
+ linter: jshint,
+ lineNumbers: true,
+ lineWrapping: true,
+ mode: 'javascript',
+ matchBrackets: true,
+ runnable: true,
+ scrollbarStyle: 'null',
+ theme: 'monokai',
+ textAreaClassName: 'hide-textarea',
+ value: this.state.value,
+ onChange: e => {
+ this.setState({ value: e.target.value});
+ if (typeof this.props.onValueChange === 'function') {
+ this.props.onValueChange(e.target.value);
+ }
+ }
+ };
+
+ var config = {
+ setSize: ['100%', 'auto'],
+ extraKeys: {
+ Tab: function(cm) {
+ debug('tab pressed');
+ if (cm.somethingSelected()) {
+ cm.indentSelection('add');
+ } else {
+ var spaces = new Array(cm.getOption('indentUnit') + 1).join(' ');
+ cm.replaceSelection(spaces);
+ }
+ },
+ 'Shift-Tab': function(cm) {
+ debug('shift-tab pressed');
+ if (cm.somethingSelected()) {
+ cm.indentSelection('subtract');
+ } else {
+ var spaces = new Array(cm.getOption('indentUnit') + 1).join(' ');
+ cm.replaceSelection(spaces);
+ }
+ },
+ 'Ctrl-Enter': function() {
+ debug('C-enter pressed');
+ // execute bonfire action
+ return false;
+ }
+ }
+ };
+
+ return (
+
+ );
+ }
+});
+
+module.exports = Editor;
diff --git a/common/screens/editor/index.js b/common/screens/editor/index.js
new file mode 100644
index 0000000000..5e431fabff
--- /dev/null
+++ b/common/screens/editor/index.js
@@ -0,0 +1 @@
+module.exports = require('./Editor.jsx');
diff --git a/common/screens/footer/Footer.jsx b/common/screens/footer/Footer.jsx
new file mode 100644
index 0000000000..5dd5e1b296
--- /dev/null
+++ b/common/screens/footer/Footer.jsx
@@ -0,0 +1,106 @@
+var React = require('react');
+
+var Footer = React.createClass({
+ render: function() {
+ return (
+
+ );
+ }
+});
+
+module.exports = Footer;
diff --git a/common/screens/footer/index.js b/common/screens/footer/index.js
new file mode 100644
index 0000000000..286ef5f0fb
--- /dev/null
+++ b/common/screens/footer/index.js
@@ -0,0 +1 @@
+module.exports = require('./Footer.jsx');
diff --git a/common/screens/nav/Nav.jsx b/common/screens/nav/Nav.jsx
new file mode 100644
index 0000000000..52a9a8728b
--- /dev/null
+++ b/common/screens/nav/Nav.jsx
@@ -0,0 +1,81 @@
+var React = require('react'),
+ bootStrap = require('react-bootstrap'),
+ Navbar = bootStrap.Navbar,
+ Nav = bootStrap.Nav,
+ NavItem = bootStrap.NavItem,
+ NavItemFCC = require('./NavItem.jsx');
+
+var NavBarComp = React.createClass({
+
+ propTypes: { signedIn: React.PropTypes.bool },
+
+ getDefaultProps: function() {
+ return { signedIn: false };
+ },
+
+ _renderBrand: function() {
+ var fCClogo = 'https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg';
+ return (
+
+
+
+ );
+ },
+
+ _renderSignin: function() {
+ if (this.props.signedIn) {
+ return (
+
+ Show Picture
+
+ );
+ } else {
+ return (
+
+ Sign In
+
+ );
+ }
+ },
+
+ render: function() {
+
+ return (
+
+
+
+ Challenges
+
+
+ Chat
+
+
+ Bonfires
+
+ { this._renderSignin() }
+
+
+ );
+ }
+});
+module.exports = NavBarComp;
diff --git a/common/screens/nav/NavItem.jsx b/common/screens/nav/NavItem.jsx
new file mode 100644
index 0000000000..88c4f363a9
--- /dev/null
+++ b/common/screens/nav/NavItem.jsx
@@ -0,0 +1,66 @@
+var React = require('react/addons');
+var joinClasses = require('react-bootstrap/lib/utils/joinClasses');
+var classSet = React.addons.classSet;
+var BootstrapMixin = require('react-bootstrap').BootstrapMixin;
+
+var NavItem = React.createClass({
+ mixins: [BootstrapMixin],
+
+ propTypes: {
+ onSelect: React.PropTypes.func,
+ active: React.PropTypes.bool,
+ disabled: React.PropTypes.bool,
+ href: React.PropTypes.string,
+ title: React.PropTypes.string,
+ eventKey: React.PropTypes.any,
+ target: React.PropTypes.string
+ },
+
+ getDefaultProps: function () {
+ return {
+ href: '#'
+ };
+ },
+
+ render: function () {
+ var {
+ disabled,
+ active,
+ href,
+ title,
+ target,
+ children,
+ } = this.props,
+ props = this.props,
+ classes = {
+ 'active': active,
+ 'disabled': disabled
+ };
+
+ return (
+
+
+ { children }
+
+
+ );
+ },
+
+ handleClick: function (e) {
+ if (this.props.onSelect) {
+ e.preventDefault();
+
+ if (!this.props.disabled) {
+ this.props.onSelect(this.props.eventKey, this.props.href, this.props.target);
+ }
+ }
+ }
+});
+
+module.exports = NavItem;
diff --git a/common/screens/nav/index.js b/common/screens/nav/index.js
new file mode 100644
index 0000000000..cdf087548a
--- /dev/null
+++ b/common/screens/nav/index.js
@@ -0,0 +1 @@
+module.exports = require('./Nav.jsx');
From bba0e0a9fe23ff8bf47621ac264a9c1b90af0651 Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Thu, 4 Jun 2015 11:30:05 -0700
Subject: [PATCH 15/16] remove unused deps
---
package.json | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/package.json b/package.json
index 0560e3c89f..3df4bc0c78 100644
--- a/package.json
+++ b/package.json
@@ -64,25 +64,13 @@
"lusca": "~1.0.2",
"method-override": "~2.3.0",
"moment": "~2.10.2",
- "mongodb": "~1.4.33",
- "mongoose": "~4.0.1",
- "mongoose-long": "0.0.2",
"morgan": "~1.5.0",
"node-slack": "0.0.7",
"nodemailer": "~1.3.0",
- "passport": "~0.2.1",
- "passport-facebook": "~1.0.3",
- "passport-github": "~0.1.5",
- "passport-google-oauth": "~0.1.5",
- "passport-linkedin-oauth2": "~1.2.1",
- "passport-local": "~1.0.0",
- "passport-oauth": "~1.0.0",
- "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",
"uglify-js": "~2.4.15",
"validator": "~3.22.1",
From 7a2bcc06f736d9678da0b15cf896be5cb26c8fdb Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Thu, 4 Jun 2015 12:03:53 -0700
Subject: [PATCH 16/16] add passport stuff back Because Berkeley really needs
to wait for his coffee to kick in before commiting anything...
---
package.json | 8 ++++++++
server/boot/passport.js | 2 ++
server/boot/user.js | 8 +++++---
server/passport-providers.js | 27 ---------------------------
server/server.js | 4 +---
5 files changed, 16 insertions(+), 33 deletions(-)
diff --git a/package.json b/package.json
index 3df4bc0c78..e75a2f2a1d 100644
--- a/package.json
+++ b/package.json
@@ -64,9 +64,17 @@
"lusca": "~1.0.2",
"method-override": "~2.3.0",
"moment": "~2.10.2",
+ "mongodb": "^2.0.33",
"morgan": "~1.5.0",
"node-slack": "0.0.7",
"nodemailer": "~1.3.0",
+ "passport-facebook": "^2.0.0",
+ "passport-google-oauth": "^0.2.0",
+ "passport-google-oauth2": "^0.1.6",
+ "passport-linkedin-oauth2": "^1.2.1",
+ "passport-local": "^1.0.0",
+ "passport-oauth": "^1.0.0",
+ "passport-twitter": "^1.0.3",
"ramda": "~0.10.0",
"request": "~2.53.0",
"rx": "^2.5.3",
diff --git a/server/boot/passport.js b/server/boot/passport.js
index 53b914afc2..326a575af0 100644
--- a/server/boot/passport.js
+++ b/server/boot/passport.js
@@ -1,3 +1,4 @@
+/*
var passport = require('passport'),
passportConf = require('../../config/passport');
@@ -67,3 +68,4 @@ module.exports = function(app) {
app.use(router);
};
+*/
diff --git a/server/boot/user.js b/server/boot/user.js
index 271c3723e9..d008c3649d 100644
--- a/server/boot/user.js
+++ b/server/boot/user.js
@@ -3,7 +3,6 @@ var _ = require('lodash'),
async = require('async'),
crypto = require('crypto'),
nodemailer = require('nodemailer'),
- passport = require('passport'),
moment = require('moment'),
debug = require('debug')('freecc:cntr:userController'),
@@ -22,7 +21,7 @@ module.exports = function(app) {
res.redirect(301, '/signout');
});
router.get('/signin', getSignin);
- router.post('/signin', postSignin);
+ // router.post('/signin', postSignin);
router.get('/signout', signout);
router.get('/forgot', getForgot);
router.post('/forgot', postForgot);
@@ -31,7 +30,7 @@ module.exports = function(app) {
router.get('/email-signup', getEmailSignup);
router.get('/email-signin', getEmailSignin);
router.post('/email-signup', postEmailSignup);
- router.post('/email-signin', postSignin);
+ // router.post('/email-signin', postSignin);
router.get('/account/api', getAccountAngular);
router.get('/api/checkUniqueUsername/:username', checkUniqueUsername);
router.get('/api/checkExistingUsername/:username', checkExistingUsername);
@@ -65,6 +64,8 @@ module.exports = function(app) {
* Sign in using email and password.
*/
+ /*
+ * TODO(berks): this should be done using loopback
function postSignin (req, res, next) {
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password cannot be blank').notEmpty();
@@ -99,6 +100,7 @@ module.exports = function(app) {
});
})(req, res, next);
}
+ */
/**
* GET /signout
diff --git a/server/passport-providers.js b/server/passport-providers.js
index 36ae610834..97bb12bbe6 100644
--- a/server/passport-providers.js
+++ b/server/passport-providers.js
@@ -38,33 +38,6 @@ module.exports = {
link: true,
failureFlash: true
},
- 'github-login': {
- provider: 'github',
- module: 'passport-github',
- clientID: process.env.GITHUB_ID,
- clientSecret: process.env.GITHUB_SECRET,
- authPath: '/auth/github',
- callbackURL: '/auth/github/callback',
- callbackPath: '/auth/github/callback',
- successRedirect: successRedirect,
- failureRedirect: failureRedirect,
- scope: ['email'],
- failureFlash: true
- },
- 'github-link': {
- provider: 'github',
- module: 'passport-github',
- clientID: process.env.GITHUB_ID,
- clientSecret: process.env.GITHUB_SECRET,
- authPath: '/link/github',
- callbackURL: '/link/github/callback',
- callbackPath: '/link/github/callback',
- successRedirect: successRedirect,
- failureRedirect: failureRedirect,
- scope: ['email', 'user_likes'],
- link: true,
- failureFlash: true
- },
'google-login': {
provider: 'google',
module: 'passport-google-oauth2',
diff --git a/server/server.js b/server/server.js
index 06fd5d00c1..4e4c7b02bd 100755
--- a/server/server.js
+++ b/server/server.js
@@ -24,7 +24,6 @@ var R = require('ramda'),
MongoStore = require('connect-mongo')(session),
flash = require('express-flash'),
path = require('path'),
- passport = require('passport'),
expressValidator = require('express-validator'),
forceDomain = require('forcedomain'),
lessMiddleware = require('less-middleware'),
@@ -76,8 +75,7 @@ app.use(session({
'autoReconnect': true
})
}));
-app.use(passport.initialize());
-app.use(passport.session());
+
app.use(flash());
app.disable('x-powered-by');