diff --git a/app.js b/app.js
index cb0abde460..6243d927b6 100644
--- a/app.js
+++ b/app.js
@@ -1,63 +1,61 @@
+require('newrelic');
/**
* Module dependencies.
*/
-require('newrelic');
-var express = require('express');
-var debug = require('debug')('freecc:server');
-var cookieParser = require('cookie-parser');
-var compress = require('compression');
-var session = require('express-session');
-var bodyParser = require('body-parser');
-var logger = require('morgan');
-var errorHandler = require('errorhandler');
-var methodOverride = require('method-override');
-var bodyParser = require('body-parser');
-var helmet = require('helmet');
+var express = require('express'),
+ debug = require('debug')('freecc:server'),
+ cookieParser = require('cookie-parser'),
+ compress = require('compression'),
+ session = require('express-session'),
+ bodyParser = require('body-parser'),
+ logger = require('morgan'),
+ errorHandler = require('errorhandler'),
+ methodOverride = require('method-override'),
+ bodyParser = require('body-parser'),
+ helmet = require('helmet'),
-var _ = require('lodash');
-var MongoStore = require('connect-mongo')(session);
-var flash = require('express-flash');
-var path = require('path');
-var mongoose = require('mongoose');
-var passport = require('passport');
-var expressValidator = require('express-validator');
-var connectAssets = require('connect-assets');
+ _ = require('lodash'),
+ MongoStore = require('connect-mongo')(session),
+ flash = require('express-flash'),
+ path = require('path'),
+ mongoose = require('mongoose'),
+ passport = require('passport'),
+ expressValidator = require('express-validator'),
+ connectAssets = require('connect-assets'),
-/**
- * Controllers (route handlers).
- */
+ /**
+ * Controllers (route handlers).
+ */
+ homeController = require('./controllers/home'),
+ challengesController = require('./controllers/challenges'),
+ resourcesController = require('./controllers/resources'),
+ userController = require('./controllers/user'),
+ contactController = require('./controllers/contact'),
-var homeController = require('./controllers/home');
-var challengesController = require('./controllers/challenges');
-var resourcesController = require('./controllers/resources');
-var userController = require('./controllers/user');
-var apiController = require('./controllers/api');
-var contactController = require('./controllers/contact');
+ /**
+ * User model
+ */
+ User = require('./models/User'),
-/**
- * User model
- */
-var User = require('./models/User');
-/**
- * API keys and Passport configuration.
- */
-
-var secrets = require('./config/secrets');
-var passportConf = require('./config/passport');
+ /**
+ * API keys and Passport configuration.
+ */
+ secrets = require('./config/secrets'),
+ passportConf = require('./config/passport');
/**
* 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.');
+ console.error(
+ 'MongoDB Connection Error. Please make sure that MongoDB is running.'
+ );
});
/**
@@ -69,7 +67,10 @@ app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(compress());
app.use(connectAssets({
- paths: [path.join(__dirname, 'public/css'), path.join(__dirname, 'public/js')],
+ paths: [
+ path.join(__dirname, 'public/css'),
+ path.join(__dirname, 'public/js')
+ ],
helperContext: app.locals
}));
app.use(logger('dev'));
@@ -91,38 +92,41 @@ app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.disable('x-powered-by');
+
app.use(helmet.xssFilter());
app.use(helmet.xframe());
+
var trusted = [
- "'self'",
+ '"self"',
'*.freecodecamp.com',
- "*.google-analytics.com",
- "*.googleapis.com",
- "*.gstatic.com",
- "*.doubleclick.net",
- "*.twitter.com",
+ '*.google-analytics.com',
+ '*.googleapis.com',
+ '*.gstatic.com',
+ '*.doubleclick.net',
+ '*.twitter.com',
'*.twimg.com',
- "*.githubusercontent.com",
- "'unsafe-eval'",
- "'unsafe-inline'"
+ '*.githubusercontent.com',
+ '"unsafe-eval"',
+ '"unsafe-inline"'
];
-//var connectSrc;
-//if (process.env.NODE_ENV === 'development') {
-// debug('Pushing');
-// connectSrc = ['"self"', 'ws://localhost:3001/'];
-//} else {
-// debug('Not');
-// connectSrc = [];
-//}
+//TODO(Berks): conditionally add localhost domains to csp;
+/*var connectSrc;
+if (process.env.NODE_ENV === 'development') {
+ debug('Pushing');
+ connectSrc = [''self'', 'ws://localhost:3001/'];
+} else {
+ debug('Not');
+ connectSrc = [];
+}*/
debug(trusted);
app.use(helmet.contentSecurityPolicy({
defaultSrc: trusted,
scriptSrc: ['*.optimizely.com'].concat(trusted),
- 'connect-src': process.env.NODE_ENV === 'development' ? ['ws://localhost:3001/', 'http://localhost:3001/'] : [],
+ 'connect-src': ['ws://localhost:3001/', 'http://localhost:3001/'],
styleSrc: trusted,
- imgSrc: ['*.evernote.com', '*.amazonaws.com', "data:"].concat(trusted),
- fontSrc: ["'self", '*.googleapis.com'].concat(trusted),
+ imgSrc: ['*.evernote.com', '*.amazonaws.com', 'data:'].concat(trusted),
+ fontSrc: ['"self"', '*.googleapis.com'].concat(trusted),
mediaSrc: ['*.amazonaws.com', '*.twitter.com'],
frameSrc: ['*.gitter.im', '*.vimeo.com', '*.twitter.com'],
// sandbox: ['allow-forms', 'allow-scripts'],
@@ -148,7 +152,9 @@ app.use(function(req, res, next) {
next();
});
-app.use(express.static(path.join(__dirname, 'public'), { maxAge: 31557600000 }));
+app.use(
+ express.static(path.join(__dirname, 'public'), { maxAge: 31557600000 })
+);
/**
* Main routes.
@@ -164,9 +170,19 @@ app.get('/jquery-exercises', resourcesController.jqueryExercises);
app.get('/live-pair-programming', resourcesController.livePairProgramming);
app.get('/javascript-in-your-inbox', resourcesController.javaScriptInYourInbox);
app.get('/chromebook', resourcesController.chromebook);
-app.get('/pair-program-with-team-viewer', resourcesController.pairProgramWithTeamViewer);
-app.get('/done-with-first-100-hours', resourcesController.doneWithFirst100Hours);
-app.get('/programmer-interview-questions-app', resourcesController.programmerInterviewQuestionsApp);
+
+app.get(
+ '/pair-program-with-team-viewer',
+ resourcesController.pairProgramWithTeamViewer
+);
+app.get(
+ '/done-with-first-100-hours',
+ resourcesController.doneWithFirst100Hours
+);
+app.get(
+ '/programmer-interview-questions-app',
+ resourcesController.programmerInterviewQuestionsApp
+);
app.get('/about', resourcesController.about);
app.get('/login', userController.getLogin);
@@ -185,14 +201,17 @@ app.post('/nonprofits', contactController.postContact);
// # Protected routes, user must be logged in.
app.post(
- '/update-progress',
- passportConf.isAuthenticated,
- userController.updateProgress);
+ '/update-progress',
+ passportConf.isAuthenticated,
+ userController.updateProgress
+);
app.get(
- '/challenges/:challengeNumber',
- passportConf.isAuthenticated,
- challengesController.returnChallenge);
+ '/challenges/:challengeNumber',
+ passportConf.isAuthenticated,
+ challengesController.returnChallenge
+);
+
app.all('/account', passportConf.isAuthenticated);
app.get('/account', userController.getAccount);
app.post('/account/profile', userController.postUpdateProfile);
@@ -207,11 +226,12 @@ app.get('/account/unlink/:provider', userController.getOauthUnlink);
*
*/
app.post('/completed_challenge', function(req, res) {
- req.user.challengesHash[parseInt(req.body.challengeNumber)] = Math.round(+new Date() / 1000);
+ req.user.challengesHash[parseInt(req.body.challengeNumber)] =
+ Math.round(+ new Date() / 1000);
var ch = req.user.challengesHash;
var p = 0;
- for (k in ch) {
- if (ch[k] > 0) { p += 1}
+ for (var k in ch) {
+ if (ch[k] > 0) { p += 1; }
}
req.user.points = p;
req.user.save();
@@ -220,7 +240,6 @@ app.post('/completed_challenge', function(req, res) {
/**
* OAuth sign-in routes.
*/
-
app.get('/auth/twitter', passport.authenticate('twitter'));
app.get(
'/auth/twitter/callback',
@@ -246,20 +265,40 @@ app.get(
res.redirect(req.session.returnTo || '/');
});
-app.get('/auth/facebook', passport.authenticate('facebook', { scope: ['email', 'user_location'] }));
-app.get('/auth/facebook/callback', passport.authenticate('facebook', { successRedirect: '/',failureRedirect: '/login' }), function(req, res) {
+app.get(
+ '/auth/facebook',
+ passport.authenticate('facebook', { scope: ['email', 'user_location'] })
+);
+
+var passportOptions = {
+ successRedirect: '/',
+ failureRedirect: '/login'
+};
+app.get(
+ '/auth/facebook/callback',
+ passport.authenticate('facebook', passportOptions), function(req, res) {
res.redirect(req.session.returnTo || '/');
-});
+ }
+);
app.get('/auth/github', passport.authenticate('github'));
-app.get('/auth/github/callback', passport.authenticate('github', { successRedirect: '/',failureRedirect: '/login' }), function(req, res) {
+app.get(
+ '/auth/github/callback',
+ passport.authenticate('github', passportOptions), function(req, res) {
res.redirect(req.session.returnTo || '/');
-});
+ }
+);
-app.get('/auth/google', passport.authenticate('google', { scope: 'profile email' }));
-app.get('/auth/google/callback', passport.authenticate('google', { successRedirect: '/',failureRedirect: '/login' }), function(req, res) {
+app.get(
+ '/auth/google',
+ passport.authenticate('google', { scope: 'profile email' })
+);
+app.get(
+ '/auth/google/callback',
+ passport.authenticate('google', passportOptions), function(req, res) {
res.redirect(req.session.returnTo || '/');
-});
+ }
+);
/**
* 500 Error Handler.
diff --git a/config/bootstrap.js b/config/bootstrap.js
index c7845e061c..c38ffac08e 100644
--- a/config/bootstrap.js
+++ b/config/bootstrap.js
@@ -1,22 +1,29 @@
-var mongoose = require('mongoose');
-var secrets = require('./secrets');
+var mongoose = require('mongoose'),
+ debug = require('debug')('freecc:config:boot'),
+ secrets = require('./secrets'),
+ courses = require('../seed_data/courses.json'),
+ Course = require('./../models/Course'),
+ challenges = require('../seed_data/challenges.json'),
+ Challenge = require('./../models/Challenge');
+
mongoose.connect(secrets.db);
mongoose.connection.on('error', function() {
console.error('MongoDB Connection Error. Make sure MongoDB is running.');
});
-var courses = require('../seed_data/courses.json');
-var challenges = require('../seed_data/challenges.json');
-
-Challenge = require ('./../models/Challenge');
-Course = require ('./../models/Course');
Course.create(courses, function(err, data) {
- if (err) console.log(err);
- else console.log('Saved ', data );
+ if (err) {
+ debug(err);
+ } else {
+ debug('Saved ', data);
+ }
});
Challenge.create(challenges, function(err, data) {
- if (err) console.log(err);
- else console.log('Saved ', data );
-});
\ No newline at end of file
+ if (err) {
+ console.log(err);
+ } else {
+ console.log('Saved ', data);
+ }
+});
diff --git a/config/passport.js b/config/passport.js
index 814a4ec7d0..6157ffa1c6 100644
--- a/config/passport.js
+++ b/config/passport.js
@@ -1,25 +1,31 @@
-var _ = require('lodash');
-var passport = require('passport');
-var InstagramStrategy = require('passport-instagram').Strategy;
-var LocalStrategy = require('passport-local').Strategy;
-var FacebookStrategy = require('passport-facebook').Strategy;
-var TwitterStrategy = require('passport-twitter').Strategy;
-var GitHubStrategy = require('passport-github').Strategy;
-var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
-var LinkedInStrategy = require('passport-linkedin-oauth2').Strategy;
-var OAuthStrategy = require('passport-oauth').OAuthStrategy;
-var OAuth2Strategy = require('passport-oauth').OAuth2Strategy;
-var User = require('../models/User');
-var secrets = require('./secrets');
+var _ = require('lodash'),
+ passport = require('passport'),
+ LocalStrategy = require('passport-local').Strategy,
+ FacebookStrategy = require('passport-facebook').Strategy,
+ TwitterStrategy = require('passport-twitter').Strategy,
+ GitHubStrategy = require('passport-github').Strategy,
+ GoogleStrategy = require('passport-google-oauth').OAuth2Strategy,
+ LinkedInStrategy = require('passport-linkedin-oauth2').Strategy,
+ OAuthStrategy = require('passport-oauth').OAuthStrategy,
+ OAuth2Strategy = require('passport-oauth').OAuth2Strategy,
+ User = require('../models/User'),
+ secrets = require('./secrets');
+
+// Login Required middleware.
+module.exports = {
+ isAuthenticated: isAuthenticated,
+ isAuthorized: isAuthorized
+};
passport.serializeUser(function(user, done) {
- done(null, user.id);
+ done(null, user.id);
});
passport.deserializeUser(function(id, done) {
- User.findById(id, function(err, user) {
- done(err, user);
- });
+ User.findById(id, function(err, user) {
+ if (err) { return done(err); }
+ done(null, user);
+ });
});
//function sendWelcomeEmail(u) {
@@ -55,247 +61,452 @@ passport.deserializeUser(function(id, done) {
*/
// Sign in with Twitter.
-passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tokenSecret, profile, done) {
- if (req.user) {
+passport.use(
+ new TwitterStrategy(
+ secrets.twitter, function(req, accessToken, tokenSecret, profile, done) {
+ if (req.user) {
User.findOne({ twitter: profile.id }, function(err, existingUser) {
- if (existingUser) {
- req.flash('errors', { msg: 'There is already a Twitter account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
- done(err);
- } else {
- User.findById(req.user.id, function(err, user) {
- user.twitter = profile.id;
- user.tokens.push({ kind: 'twitter', accessToken: accessToken, tokenSecret: tokenSecret });
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.location = user.profile.location || profile._json.location;
- user.profile.picture = user.profile.picture || profile._json.profile_image_url_https;
- user.profile.username = profile.username;
- user.save(function(err) {
- req.flash('info', { msg: 'Twitter account has been linked.' });
- done(err, user);
- });
- });
- }
+ if (err) { return done(err); }
+
+ if (existingUser) {
+ req.flash('errors', {
+ msg: [
+ 'There is already a Twitter account that belongs to you. ',
+ 'Sign in with that account or delete it, then link it with ',
+ 'your current account.'
+ ].join('')
+ });
+ done();
+ } else {
+ User.findById(req.user.id, function(err, user) {
+ if (err) { return done(err); }
+
+ user.twitter = profile.id;
+ user.tokens.push({
+ kind: 'twitter',
+ accessToken: accessToken,
+ tokenSecret: tokenSecret
+ });
+ user.profile.name = user.profile.name || profile.displayName;
+
+ user.profile.location =
+ user.profile.location || profile._json.location;
+
+ user.profile.picture =
+ user.profile.picture || profile._json.profile_image_url_https;
+
+ user.profile.username = profile.username;
+ user.save(function(err) {
+ req.flash('info', { msg: 'Twitter account has been linked.' });
+ done(null, user);
+ });
+ });
+ }
});
- } else {
+ } else {
User.findOne({ twitter: profile.id }, function(err, existingUser) {
- //if (existingUser) return done(null, existingUser);
- // Twitter will not provide an email address. Period.
- // But a person’s twitter username is guaranteed to be unique
- // so we can "fake" a twitter email address as follows:
- //user.email = profile.username + "@twitter.com";
- var user = existingUser || new User;
- user.twitter = profile.id;
- user.email = user.email || '';
- user.tokens.push({ kind: 'twitter', accessToken: accessToken, tokenSecret: tokenSecret });
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.location = user.profile.location || profile._json.location;
- user.profile.picture = user.profile.picture || profile._json.profile_image_url_https;
- user.save(function(err) {
- done(err, user);
- });
- //if (!user.email) {
- // req.redirect('/account');
- // req.flash('errors', { msg: 'OK, you are signed in. Please add your email address to your profile.' });
- //}
+ if (err) { return done(err); }
+
+ //if (existingUser) return done(null, existingUser);
+ // Twitter will not provide an email address. Period.
+ // But a person’s twitter username is guaranteed to be unique
+ // so we can "fake" a twitter email address as follows:
+ //user.email = profile.username + "@twitter.com";
+ var user = existingUser || new User();
+ user.twitter = profile.id;
+ user.email = user.email || '';
+ user.tokens.push({
+ kind: 'twitter',
+ accessToken: accessToken,
+ tokenSecret: tokenSecret
+ });
+ user.profile.name = user.profile.name || profile.displayName;
+
+ user.profile.location =
+ user.profile.location || profile._json.location;
+ user.profile.picture =
+ user.profile.picture || profile._json.profile_image_url_https;
+
+ user.save(function(err) {
+ if (err) { return done(err); }
+ done(null, user);
+ });
+ //TODO: Twitter redirect to capture user email.
+ //if (!user.email) {
+ // req.redirect('/account');
+ // req.flash('errors', {
+ // msg:
+ // 'OK, you are signed in. ' +
+ // 'Please add your email address to your profile.'
+ // });
+ //}
});
- }
-}));
+ }
+ })
+);
// Sign in with LinkedIn.
-passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, refreshToken, profile, done) {
- if (req.user) {
+passport.use(
+ new LinkedInStrategy(
+ secrets.linkedin, function(req, accessToken, refreshToken, profile, done) {
+ if (req.user) {
User.findOne({ linkedin: profile.id }, function(err, existingUser) {
- if (existingUser) {
- req.flash('errors', { msg: 'There is already a LinkedIn account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
- done(err);
- } else {
- User.findById(req.user.id, function(err, user) {
- user.linkedin = profile.id;
- user.tokens.push({ kind: 'linkedin', accessToken: accessToken });
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.location = user.profile.location || profile._json.location.name;
- user.profile.picture = user.profile.picture || profile._json.pictureUrl;
- user.profile.website = user.profile.website || profile._json.publicProfileUrl;
- user.save(function(err) {
- req.flash('info', { msg: 'LinkedIn account has been linked.' });
- done(err, user);
- });
- });
- }
- });
- } else {
- User.findOne({ linkedin: profile.id }, function(err, existingUser) {
- if (existingUser) return done(null, existingUser);
- User.findOne({ email: profile._json.emailAddress }, function(err, existingEmailUser) {
- var user = existingEmailUser || new User;
- user.linkedin = profile.id;
- user.tokens.push({ kind: 'linkedin', accessToken: accessToken });
- user.email = user.email || profile._json.emailAddress;
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.location = user.profile.location || profile._json.location.name;
- user.profile.picture = user.profile.picture || profile._json.pictureUrl;
- user.profile.website = user.profile.website || profile._json.publicProfileUrl;
- user.challengesComplete = user.challengesCompleted || [];
- user.save(function(err) {
- done(err, user);
- });
+ if (err) { return done(err); }
+
+ if (existingUser) {
+ req.flash('errors', {
+ msg: [
+ 'There is already a LinkedIn account that belongs to you.',
+ 'Sign in with that account or delete it,',
+ 'then link it with your current account.'
+ ].join('')
});
+ done();
+ } else {
+ User.findById(req.user.id, function(err, user) {
+ if (err) { return done(err); }
+
+ user.linkedin = profile.id;
+ user.tokens.push({
+ kind: 'linkedin',
+ accessToken: accessToken
+ });
+ user.profile.name = user.profile.name || profile.displayName;
+
+ user.profile.location =
+ user.profile.location || profile._json.location.name;
+
+ user.profile.picture =
+ user.profile.picture || profile._json.pictureUrl;
+
+ user.profile.website =
+ user.profile.website || profile._json.publicProfileUrl;
+
+ user.save(function(err, _user) {
+ if (err) { return done(err); }
+ req.flash(
+ 'info', { msg: 'LinkedIn account has been linked.' }
+ );
+ done(null, user);
+ });
+ });
+ }
});
- }
+ } else {
+ User.findOne({ linkedin: profile.id }, function(err, existingUser) {
+ if (err) { return done(err); }
+
+ if (existingUser) { return done(null, existingUser); }
+ User.findOne(
+ { email: profile._json.emailAddress },
+ function(err, existingEmailUser) {
+ if (err) { return done(err); }
+
+ var user = existingEmailUser || new User();
+
+ user.linkedin = profile.id;
+ user.tokens.push({
+ kind: 'linkedin',
+ accessToken: accessToken
+ });
+ user.email = user.email || profile._json.emailAddress;
+ user.profile.name = user.profile.name || profile.displayName;
+
+ user.profile.location =
+ user.profile.location || profile._json.location.name;
+
+ user.profile.picture =
+ user.profile.picture || profile._json.pictureUrl;
+
+ user.profile.website =
+ user.profile.website || profile._json.publicProfileUrl;
+
+ user.challengesComplete = user.challengesCompleted || [];
+ user.save(function(err) {
+ if (err) { return done(err); }
+ done(null, user);
+ });
+ });
+
+ });
+ }
}));
// Sign in using Email and Password.
-passport.use(new LocalStrategy({ usernameField: 'email' }, function(email, password, done) {
+passport.use(
+ new LocalStrategy(
+ { usernameField: 'email' }, function(email, password, done) {
User.findOne({ email: email }, function(err, user) {
- if (!user) return done(null, false, { message: 'Email ' + email + ' not found'});
- user.comparePassword(password, function(err, isMatch) {
- if (isMatch) {
- return done(null, user);
- } else {
- return done(null, false, { message: 'Invalid email or password.' });
- }
- });
+ if (err) { return done(err); }
+
+ if (!user) {
+ return done(null, false, { message: 'Email ' + email + ' not found'});
+ }
+ user.comparePassword(password, function(err, isMatch) {
+ if (err) { return done(err); }
+
+ if (isMatch) {
+ return done(null, user);
+ } else {
+ return done(null, false, { message: 'Invalid email or password.' });
+ }
+ });
});
}));
// Sign in with Facebook.
+passport.use(
+ new FacebookStrategy(
+ secrets.facebook, function(req, accessToken, refreshToken, profile, done) {
+ if (req.user) {
+ User.findOne({ facebook: profile.id }, function(err, existingUser) {
+ if (err) { return done(err); }
-passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) {
- if (req.user) {
- User.findOne({ facebook: profile.id }, function(err, existingUser) {
- if (existingUser) {
- req.flash('errors', { msg: 'There is already a Facebook account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
- done(err);
- } else {
- User.findById(req.user.id, function(err, user) {
- user.facebook = profile.id;
- user.tokens.push({ kind: 'facebook', accessToken: accessToken });
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.gender = user.profile.gender || profile._json.gender;
- user.profile.picture = user.profile.picture || 'https://graph.facebook.com/' + profile.id + '/picture?type=large';
- user.save(function(err) {
- req.flash('info', { msg: 'Facebook account has been linked.' });
- done(err, user);
- });
- });
- }
+ if (existingUser) {
+ req.flash('errors', {
+ msg: [
+ 'There is already a Facebook account that belongs to you.',
+ 'Sign in with that account or delete it, then link it with',
+ 'your current account.'
+ ].join(' ')
+ });
+ done();
+ } else {
+ User.findById(req.user.id, function(err, user) {
+ if (err) { return done(err); }
+
+ user.facebook = profile.id;
+ user.tokens.push({
+ kind: 'facebook',
+ accessToken: accessToken
+ });
+
+ user.profile.name = user.profile.name || profile.displayName;
+ user.profile.gender = user.profile.gender || profile._json.gender;
+
+ user.profile.picture =
+ user.profile.picture ||
+ 'https://graph.facebook.com/' +
+ profile.id +
+ '/picture?type=large';
+
+ user.save(function(err) {
+ if (err) { return done(err); }
+
+ req.flash(
+ 'info', { msg: 'Facebook account has been linked.' });
+ done(null, user);
+ });
+ });
+ }
});
- } else {
+ } else {
User.findOne({ facebook: profile.id }, function(err, existingUser) {
- if (existingUser) return done(null, existingUser);
- User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
- var user = existingEmailUser || new User;
- user.email = user.email || profile._json.email;
- user.facebook = profile.id;
- user.tokens.push({ kind: 'facebook', accessToken: accessToken });
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.gender = user.profile.gender || profile._json.gender;
- user.profile.picture = user.profile.picture || 'https://graph.facebook.com/' + profile.id + '/picture?type=large';
- user.profile.location = user.profile.location || (profile._json.location) ? profile._json.location.name : '';
- user.challengesComplete = user.challengesCompleted || [];
- user.save(function(err) {
- done(err, user);
- });
+ if (err) { return done(err); }
+
+ if (existingUser) { return done(null, existingUser); }
+
+ User.findOne(
+ { email: profile._json.email }, function(err, existingEmailUser) {
+ if (err) { return done(err); }
+
+ var user = existingEmailUser || new User();
+ user.email = user.email || profile._json.email;
+ user.facebook = profile.id;
+ user.tokens.push({
+ kind: 'facebook',
+ accessToken: accessToken
+ });
+ user.profile.name = user.profile.name || profile.displayName;
+
+ user.profile.gender =
+ user.profile.gender || profile._json.gender;
+
+ user.profile.picture =
+ user.profile.picture ||
+ 'https://graph.facebook.com/' +
+ profile.id +
+ '/picture?type=large';
+
+ user.profile.location =
+ user.profile.location ||
+ (profile._json.location) ? profile._json.location.name : '';
+
+ user.challengesComplete = user.challengesCompleted || [];
+ user.save(function(err) {
+ if (err) { return done(err); }
+ done(null, user);
+ });
});
});
- }
+ }
}));
// Sign in with GitHub.
-passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refreshToken, profile, done) {
- if (req.user) {
+passport.use(
+ new GitHubStrategy(
+ secrets.github, function(req, accessToken, refreshToken, profile, done) {
+ if (req.user) {
User.findOne({ github: profile.id }, function(err, existingUser) {
- if (existingUser) {
- req.flash('errors', { msg: 'There is already a GitHub account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
- done(err);
- } else {
- User.findById(req.user.id, function(err, user) {
- user.github = profile.id;
- user.tokens.push({ kind: 'github', accessToken: accessToken });
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.picture = user.profile.picture || profile._json.avatar_url;
- user.profile.location = user.profile.location || profile._json.location;
- user.profile.website = user.profile.website || profile._json.blog;
- user.save(function(err) {
- req.flash('info', { msg: 'GitHub account has been linked.' });
- done(err, user);
- });
- });
- }
- });
- } else {
- User.findOne({ github: profile.id }, function(err, existingUser) {
- if (existingUser) return done(null, existingUser);
- User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
- var user = existingEmailUser || new User;
- user.email = user.email || profile._json.email;
- user.github = profile.id;
- user.tokens.push({ kind: 'github', accessToken: accessToken });
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.picture = user.profile.picture || profile._json.avatar_url;
- user.profile.location = user.profile.location || profile._json.location;
- user.profile.website = user.profile.website || profile._json.blog;
- user.save(function(err) {
- done(err, user);
- });
+ if (err) { return done(err); }
+
+ if (existingUser) {
+ req.flash('errors', {
+ msg: [
+ 'There is already a GitHub account that belongs to you.',
+ 'Sign in with that account or delete it, then link it with',
+ 'your current account.'
+ ].join(' ')
});
+ done();
+ } else {
+ User.findById(req.user.id, function(err, user) {
+ if (err) { return done(err); }
+
+ user.github = profile.id;
+ user.tokens.push({ kind: 'github', accessToken: accessToken });
+ user.profile.name = user.profile.name || profile.displayName;
+
+ user.profile.picture =
+ user.profile.picture || profile._json.avatar_url;
+
+ user.profile.location =
+ user.profile.location || profile._json.location;
+
+ user.profile.website =
+ user.profile.website || profile._json.blog;
+
+ user.save(function(err) {
+ if (err) { return done(err); }
+
+ req.flash('info', { msg: 'GitHub account has been linked.' });
+ done(null, user);
+ });
+ });
+ }
});
- }
+ } else {
+ User.findOne({ github: profile.id }, function(err, existingUser) {
+ if (err) { return done(err); }
+
+ if (existingUser) { return done(null, existingUser); }
+ User.findOne(
+ { email: profile._json.email }, function(err, existingEmailUser) {
+ if (err) { return done(err); }
+
+ var user = existingEmailUser || new User();
+ user.email = user.email || profile._json.email;
+ user.github = profile.id;
+ user.tokens.push({
+ kind: 'github',
+ accessToken: accessToken
+ });
+ user.profile.name = user.profile.name || profile.displayName;
+
+ user.profile.picture =
+ user.profile.picture || profile._json.avatar_url;
+
+ user.profile.location =
+ user.profile.location || profile._json.location;
+
+ user.profile.website =
+ user.profile.website || profile._json.blog;
+
+ user.save(function(err) {
+ if (err) { return done(err); }
+ done(null, user);
+ });
+ });
+ });
+ }
}));
// Sign in with Google.
-passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refreshToken, profile, done) {
- if (req.user) {
+passport.use(
+ new GoogleStrategy(
+ secrets.google, function(req, accessToken, refreshToken, profile, done) {
+ if (req.user) {
User.findOne({ google: profile.id }, function(err, existingUser) {
- if (existingUser) {
- req.flash('errors', { msg: 'There is already a Google account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
- done(err);
- } else {
- User.findById(req.user.id, function(err, user) {
- user.google = profile.id;
- user.tokens.push({ kind: 'google', accessToken: accessToken });
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.gender = user.profile.gender || profile._json.gender;
- user.profile.picture = user.profile.picture || profile._json.picture;
- user.save(function(err) {
- req.flash('info', { msg: 'Google account has been linked.' });
- done(err, user);
- });
- });
- }
- });
- } else {
- User.findOne({ google: profile.id }, function(err, existingUser) {
- if (existingUser) return done(null, existingUser);
- User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
- var user = existingEmailUser || new User;
- user.email = user.email || profile._json.email;
- user.google = profile.id;
- user.tokens.push({ kind: 'google', accessToken: accessToken });
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.gender = user.profile.gender || profile._json.gender;
- user.profile.picture = user.profile.picture || profile._json.picture;
- //if (!existingEmailUser) {
- // sendWelcomeEmail(user);
- //}
- user.save(function(err) {
- done(err, user);
- });
+ if (err) { return done(err); }
+
+ if (existingUser) {
+ req.flash('errors', {
+ msg: [
+ 'There is already a Google account that belongs to you.',
+ 'Sign in with that account or delete it,',
+ 'then link it with your current account.'
+ ].join(' ')
});
+ done();
+ } else {
+ User.findById(req.user.id, function(err, user) {
+ if (err) { return done(err); }
+
+ user.google = profile.id;
+ user.tokens.push({
+ kind: 'google',
+ accessToken: accessToken
+ });
+
+ user.profile.name = user.profile.name || profile.displayName;
+
+ user.profile.gender =
+ user.profile.gender || profile._json.gender;
+
+ user.profile.picture =
+ user.profile.picture || profile._json.picture;
+
+ user.save(function(err) {
+ if (err) { return done(err); }
+ req.flash('info', { msg: 'Google account has been linked.' });
+ done(null, user);
+ });
+ });
+ }
});
- }
+ } else {
+ User.findOne({ google: profile.id }, function(err, existingUser) {
+ if (err) { return done(err); }
+
+ if (existingUser) { return done(null, existingUser); }
+ User.findOne(
+ { email: profile._json.email }, function(err, existingEmailUser) {
+ if (err) { return done(err); }
+
+ var user = existingEmailUser || new User();
+ user.email = user.email || profile._json.email;
+ user.google = profile.id;
+ user.tokens.push({
+ kind: 'google',
+ accessToken: accessToken
+ });
+
+ user.profile.name =
+ user.profile.name || profile.displayName;
+
+ user.profile.gender =
+ user.profile.gender || profile._json.gender;
+
+ user.profile.picture =
+ user.profile.picture || profile._json.picture;
+
+ //TODO: Greeting email
+ //if (!existingEmailUser) {
+ // sendWelcomeEmail(user);
+ //}
+ user.save(function(err) {
+ if (err) { return done(err); }
+ done(null, user);
+ });
+ });
+ });
+ }
}));
-// Login Required middleware.
-
-module.exports = {
- isAuthenticated: isAuthenticated,
- isAuthorized: isAuthorized
-};
function isAuthenticated(req, res, next) {
if (req.isAuthenticated()) return next();
@@ -307,113 +518,8 @@ function isAuthorized(req, res, next) {
var provider = req.path.split('/').slice(-1)[0];
if (_.find(req.user.tokens, { kind: provider })) {
- next();
+ next();
} else {
- res.redirect('/auth/' + provider);
+ res.redirect('/auth/' + provider);
}
}
-
-/*
-passport.use(new InstagramStrategy(secrets.instagram,function(req, accessToken, refreshToken, profile, done) {
- if (req.user) {
- User.findOne({ instagram: profile.id }, function(err, existingUser) {
- if (existingUser) {
- req.flash('errors', { msg: 'There is already an Instagram account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
- done(err);
- } else {
- User.findById(req.user.id, function(err, user) {
- user.instagram = profile.id;
- user.tokens.push({ kind: 'instagram', accessToken: accessToken });
- user.profile.name = user.profile.name || profile.displayName;
- user.profile.picture = user.profile.picture || profile._json.data.profile_picture;
- user.profile.website = user.profile.website || profile._json.data.website;
- user.save(function(err) {
- req.flash('info', { msg: 'Instagram account has been linked.' });
- done(err, user);
- });
- });
- }
- });
- } else {
- User.findOne({ instagram: profile.id }, function(err, existingUser) {
- if (existingUser) return done(null, existingUser);
-
- var user = new User();
- user.instagram = profile.id;
- user.tokens.push({ kind: 'instagram', accessToken: accessToken });
- user.profile.name = profile.displayName;
- // Similar to Twitter API, assigns a temporary e-mail address
- // to get on with the registration process. It can be changed later
- // to a valid e-mail address in Profile Management.
- user.email = profile.username + "@instagram.com";
- user.profile.website = profile._json.data.website;
- user.profile.picture = profile._json.data.profile_picture;
- user.save(function(err) {
- done(err, user);
- });
- });
- }
-}));
-
-
-// Tumblr API setup.
-
-passport.use('tumblr', new OAuthStrategy({
- requestTokenURL: 'http://www.tumblr.com/oauth/request_token',
- accessTokenURL: 'http://www.tumblr.com/oauth/access_token',
- userAuthorizationURL: 'http://www.tumblr.com/oauth/authorize',
- consumerKey: secrets.tumblr.consumerKey,
- consumerSecret: secrets.tumblr.consumerSecret,
- callbackURL: secrets.tumblr.callbackURL,
- passReqToCallback: true
- },
- function(req, token, tokenSecret, profile, done) {
- User.findById(req.user._id, function(err, user) {
- user.tokens.push({ kind: 'tumblr', accessToken: token, tokenSecret: tokenSecret });
- user.save(function(err) {
- done(err, user);
- });
- });
- }
-));
-
-// Foursquare API setup.
-
-passport.use('foursquare', new OAuth2Strategy({
- authorizationURL: 'https://foursquare.com/oauth2/authorize',
- tokenURL: 'https://foursquare.com/oauth2/access_token',
- clientID: secrets.foursquare.clientId,
- clientSecret: secrets.foursquare.clientSecret,
- callbackURL: secrets.foursquare.redirectUrl,
- passReqToCallback: true
- },
- function(req, accessToken, refreshToken, profile, done) {
- User.findById(req.user._id, function(err, user) {
- user.tokens.push({ kind: 'foursquare', accessToken: accessToken });
- user.save(function(err) {
- done(err, user);
- });
- });
- }
-));
-
-// Venmo API setup.
-
-passport.use('venmo', new OAuth2Strategy({
- authorizationURL: 'https://api.venmo.com/v1/oauth/authorize',
- tokenURL: 'https://api.venmo.com/v1/oauth/access_token',
- clientID: secrets.venmo.clientId,
- clientSecret: secrets.venmo.clientSecret,
- callbackURL: secrets.venmo.redirectUrl,
- passReqToCallback: true
- },
- function(req, accessToken, refreshToken, profile, done) {
- User.findById(req.user._id, function(err, user) {
- user.tokens.push({ kind: 'venmo', accessToken: accessToken });
- user.save(function(err) {
- done(err, user);
- });
- });
- }
-));
-*/
diff --git a/controllers/api.js b/controllers/api.js
deleted file mode 100644
index b395a756a6..0000000000
--- a/controllers/api.js
+++ /dev/null
@@ -1,602 +0,0 @@
-var secrets = require('../config/secrets');
-var User = require('../models/User');
-var querystring = require('querystring');
-var validator = require('validator');
-var async = require('async');
-var cheerio = require('cheerio');
-var request = require('request');
-var graph = require('fbgraph');
-var LastFmNode = require('lastfm').LastFmNode;
-var tumblr = require('tumblr.js');
-var foursquare = require('node-foursquare')({ secrets: secrets.foursquare });
-var Github = require('github-api');
-var Twit = require('twit');
-var stripe = require('stripe')(secrets.stripe.secretKey);
-var twilio = require('twilio')(secrets.twilio.sid, secrets.twilio.token);
-var Linkedin = require('node-linkedin')(secrets.linkedin.clientID, secrets.linkedin.clientSecret, secrets.linkedin.callbackURL);
-var clockwork = require('clockwork')({key: secrets.clockwork.apiKey});
-var ig = require('instagram-node').instagram();
-var Y = require('yui/yql');
-var _ = require('lodash');
-
-/**
- * GET /api
- * List of API examples.
- */
-
-exports.getApi = function(req, res) {
- res.render('api/index', {
- title: 'API Examples'
- });
-};
-
-/**
- * GET /api/foursquare
- * Foursquare API example.
- */
-
-exports.getFoursquare = function(req, res, next) {
- var token = _.find(req.user.tokens, { kind: 'foursquare' });
- async.parallel({
- trendingVenues: function(callback) {
- foursquare.Venues.getTrending('40.7222756', '-74.0022724', { limit: 50 }, token.accessToken, function(err, results) {
- callback(err, results);
- });
- },
- venueDetail: function(callback) {
- foursquare.Venues.getVenue('49da74aef964a5208b5e1fe3', token.accessToken, function(err, results) {
- callback(err, results);
- });
- },
- userCheckins: function(callback) {
- foursquare.Users.getCheckins('self', null, token.accessToken, function(err, results) {
- callback(err, results);
- });
- }
- },
- function(err, results) {
- if (err) return next(err);
- res.render('api/foursquare', {
- title: 'Foursquare API',
- trendingVenues: results.trendingVenues,
- venueDetail: results.venueDetail,
- userCheckins: results.userCheckins
- });
- });
-};
-
-/**
- * GET /api/tumblr
- * Tumblr API example.
- */
-
-exports.getTumblr = function(req, res, next) {
- var token = _.find(req.user.tokens, { kind: 'tumblr' });
- var client = tumblr.createClient({
- consumer_key: secrets.tumblr.consumerKey,
- consumer_secret: secrets.tumblr.consumerSecret,
- token: token.accessToken,
- token_secret: token.tokenSecret
- });
- client.posts('withinthisnightmare.tumblr.com', { type: 'photo' }, function(err, data) {
- if (err) return next(err);
- res.render('api/tumblr', {
- title: 'Tumblr API',
- blog: data.blog,
- photoset: data.posts[0].photos
- });
- });
-};
-
-/**
- * GET /api/facebook
- * Facebook API example.
- */
-
-exports.getFacebook = function(req, res, next) {
- var token = _.find(req.user.tokens, { kind: 'facebook' });
- graph.setAccessToken(token.accessToken);
- async.parallel({
- getMe: function(done) {
- graph.get(req.user.facebook, function(err, me) {
- done(err, me);
- });
- },
- getMyFriends: function(done) {
- graph.get(req.user.facebook + '/friends', function(err, friends) {
- done(err, friends.data);
- });
- }
- },
- function(err, results) {
- if (err) return next(err);
- res.render('api/facebook', {
- title: 'Facebook API',
- me: results.getMe,
- friends: results.getMyFriends
- });
- });
-};
-
-/**
- * GET /api/scraping
- * Web scraping example using Cheerio library.
- */
-
-exports.getScraping = function(req, res, next) {
- request.get('https://news.ycombinator.com/', function(err, request, body) {
- if (err) return next(err);
- var $ = cheerio.load(body);
- var links = [];
- $('.title a[href^="http"], a[href^="https"]').each(function() {
- links.push($(this));
- });
- res.render('api/scraping', {
- title: 'Web Scraping',
- links: links
- });
- });
-};
-
-/**
- * GET /api/github
- * GitHub API Example.
- */
-
-exports.getGithub = function(req, res, next) {
- var token = _.find(req.user.tokens, { kind: 'github' });
- var github = new Github({ token: token.accessToken });
- var repo = github.getRepo('sahat', 'requirejs-library');
- repo.show(function(err, repo) {
- if (err) return next(err);
- res.render('api/github', {
- title: 'GitHub API',
- repo: repo
- });
- });
-
-};
-
-/**
- * GET /api/aviary
- * Aviary image processing example.
- */
-
-exports.getAviary = function(req, res) {
- res.render('api/aviary', {
- title: 'Aviary API'
- });
-};
-
-/**
- * GET /api/nyt
- * New York Times API example.
- */
-
-exports.getNewYorkTimes = function(req, res, next) {
- var query = querystring.stringify({ 'api-key': secrets.nyt.key, 'list-name': 'young-adult' });
- var url = 'http://api.nytimes.com/svc/books/v2/lists?' + query;
- request.get(url, function(err, request, body) {
- if (err) return next(err);
- if (request.statusCode === 403) return next(Error('Missing or Invalid New York Times API Key'));
- var bestsellers = JSON.parse(body);
- res.render('api/nyt', {
- title: 'New York Times API',
- books: bestsellers.results
- });
- });
-};
-
-/**
- * GET /api/lastfm
- * Last.fm API example.
- */
-
-exports.getLastfm = function(req, res, next) {
- var lastfm = new LastFmNode(secrets.lastfm);
- async.parallel({
- artistInfo: function(done) {
- lastfm.request('artist.getInfo', {
- artist: 'The Pierces',
- handlers: {
- success: function(data) {
- done(null, data);
- },
- error: function(err) {
- done(err);
- }
- }
- });
- },
- artistTopTracks: function(done) {
- lastfm.request('artist.getTopTracks', {
- artist: 'The Pierces',
- handlers: {
- success: function(data) {
- var tracks = [];
- _.each(data.toptracks.track, function(track) {
- tracks.push(track);
- });
- done(null, tracks.slice(0,10));
- },
- error: function(err) {
- done(err);
- }
- }
- });
- },
- artistTopAlbums: function(done) {
- lastfm.request('artist.getTopAlbums', {
- artist: 'The Pierces',
- handlers: {
- success: function(data) {
- var albums = [];
- _.each(data.topalbums.album, function(album) {
- albums.push(album.image.slice(-1)[0]['#text']);
- });
- done(null, albums.slice(0, 4));
- },
- error: function(err) {
- done(err);
- }
- }
- });
- }
- },
- function(err, results) {
- if (err) return next(err.message);
- var artist = {
- name: results.artistInfo.artist.name,
- image: results.artistInfo.artist.image.slice(-1)[0]['#text'],
- tags: results.artistInfo.artist.tags.tag,
- bio: results.artistInfo.artist.bio.summary,
- stats: results.artistInfo.artist.stats,
- similar: results.artistInfo.artist.similar.artist,
- topAlbums: results.artistTopAlbums,
- topTracks: results.artistTopTracks
- };
- res.render('api/lastfm', {
- title: 'Last.fm API',
- artist: artist
- });
- });
-};
-
-/**
- * GET /api/twitter
- * Twiter API example.
- */
-
-exports.getTwitter = function(req, res, next) {
- var token = _.find(req.user.tokens, { kind: 'twitter' });
- var T = new Twit({
- consumer_key: secrets.twitter.consumerKey,
- consumer_secret: secrets.twitter.consumerSecret,
- access_token: token.accessToken,
- access_token_secret: token.tokenSecret
- });
- T.get('search/tweets', { q: 'nodejs since:2013-01-01', geocode: '40.71448,-74.00598,5mi', count: 10 }, function(err, reply) {
- if (err) return next(err);
- res.render('api/twitter', {
- title: 'Twitter API',
- tweets: reply.statuses
- });
- });
-};
-
-/**
- * POST /api/twitter
- * Post a tweet.
- */
-
-exports.postTwitter = function(req, res, next) {
- req.assert('tweet', 'Tweet cannot be empty.').notEmpty();
- var errors = req.validationErrors();
- if (errors) {
- req.flash('errors', errors);
- return res.redirect('/api/twitter');
- }
- var token = _.find(req.user.tokens, { kind: 'twitter' });
- var T = new Twit({
- consumer_key: secrets.twitter.consumerKey,
- consumer_secret: secrets.twitter.consumerSecret,
- access_token: token.accessToken,
- access_token_secret: token.tokenSecret
- });
- T.post('statuses/update', { status: req.body.tweet }, function(err, data, response) {
- if (err) return next(err);
- req.flash('success', { msg: 'Tweet has been posted.'});
- res.redirect('/api/twitter');
- });
-};
-
-/**
- * GET /api/steam
- * Steam API example.
- */
-
-exports.getSteam = function(req, res, next) {
- var steamId = '76561197982488301';
- var query = { l: 'english', steamid: steamId, key: secrets.steam.apiKey };
- async.parallel({
- playerAchievements: function(done) {
- query.appid = '49520';
- var qs = querystring.stringify(query);
- request.get({ url: 'http://api.steampowered.com/ISteamUserStats/GetPlayerAchievements/v0001/?' + qs, json: true }, function(error, request, body) {
- if (request.statusCode === 401) return done(new Error('Missing or Invalid Steam API Key'));
- done(error, body);
- });
- },
- playerSummaries: function(done) {
- query.steamids = steamId;
- var qs = querystring.stringify(query);
- request.get({ url: 'http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?' + qs, json: true }, function(err, request, body) {
- if (request.statusCode === 401) return done(new Error('Missing or Invalid Steam API Key'));
- done(err, body);
- });
- },
- ownedGames: function(done) {
- query.include_appinfo = 1;
- query.include_played_free_games = 1;
- var qs = querystring.stringify(query);
- request.get({ url: 'http://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?' + qs, json: true }, function(err, request, body) {
- if (request.statusCode === 401) return done(new Error('Missing or Invalid Steam API Key'));
- done(err, body);
- });
- }
- },
- function(err, results) {
- if (err) return next(err);
- res.render('api/steam', {
- title: 'Steam Web API',
- ownedGames: results.ownedGames.response.games,
- playerAchievemments: results.playerAchievements.playerstats,
- playerSummary: results.playerSummaries.response.players[0]
- });
- });
-};
-
-/**
- * GET /api/stripe
- * Stripe API example.
- */
-
-exports.getStripe = function(req, res) {
- res.render('api/stripe', {
- title: 'Stripe API',
- publishableKey: secrets.stripe.publishableKey
- });
-};
-
-/**
- * POST /api/stripe
- * Make a payment.
- */
-
-exports.postStripe = function(req, res, next) {
- var stripeToken = req.body.stripeToken;
- var stripeEmail = req.body.stripeEmail;
- stripe.charges.create({
- amount: 395,
- currency: 'usd',
- card: stripeToken,
- description: stripeEmail
- }, function(err, charge) {
- if (err && err.type === 'StripeCardError') {
- req.flash('errors', { msg: 'Your card has been declined.' });
- res.redirect('/api/stripe');
- }
- req.flash('success', { msg: 'Your card has been charged successfully.' });
- res.redirect('/api/stripe');
- });
-};
-
-/**
- * GET /api/twilio
- * Twilio API example.
- */
-
-exports.getTwilio = function(req, res) {
- res.render('api/twilio', {
- title: 'Twilio API'
- });
-};
-
-/**
- * POST /api/twilio
- * Send a text message using Twilio.
- */
-
-exports.postTwilio = function(req, res, next) {
- req.assert('number', 'Phone number is required.').notEmpty();
- req.assert('message', 'Message cannot be blank.').notEmpty();
- var errors = req.validationErrors();
- if (errors) {
- req.flash('errors', errors);
- return res.redirect('/api/twilio');
- }
- var message = {
- to: req.body.number,
- from: '+13472235148',
- body: req.body.message
- };
- twilio.sendMessage(message, function(err, responseData) {
- if (err) return next(err.message);
- req.flash('success', { msg: 'Text sent to ' + responseData.to + '.'});
- res.redirect('/api/twilio');
- });
-};
-
-/**
- * GET /api/clockwork
- * Clockwork SMS API example.
- */
-
-exports.getClockwork = function(req, res) {
- res.render('api/clockwork', {
- title: 'Clockwork SMS API'
- });
-};
-
-/**
- * POST /api/clockwork
- * Send a text message using Clockwork SMS
- */
-
-exports.postClockwork = function(req, res, next) {
- var message = {
- To: req.body.telephone,
- From: 'Hackathon',
- Content: 'Hello from the Hackathon Starter'
- };
- clockwork.sendSms(message, function(err, responseData) {
- if (err) return next(err.errDesc);
- req.flash('success', { msg: 'Text sent to ' + responseData.responses[0].to });
- res.redirect('/api/clockwork');
- });
-};
-
-/**
- * GET /api/venmo
- * Venmo API example.
- */
-
-exports.getVenmo = function(req, res, next) {
- var token = _.find(req.user.tokens, { kind: 'venmo' });
- var query = querystring.stringify({ access_token: token.accessToken });
- async.parallel({
- getProfile: function(done) {
- request.get({ url: 'https://api.venmo.com/v1/me?' + query, json: true }, function(err, request, body) {
- done(err, body);
- });
- },
- getRecentPayments: function(done) {
- request.get({ url: 'https://api.venmo.com/v1/payments?' + query, json: true }, function(err, request, body) {
- done(err, body);
- });
- }
- },
- function(err, results) {
- if (err) return next(err);
- res.render('api/venmo', {
- title: 'Venmo API',
- profile: results.getProfile.data,
- recentPayments: results.getRecentPayments.data
- });
- });
-};
-
-/**
- * POST /api/venmo
- * Send money.
- */
-
-exports.postVenmo = function(req, res, next) {
- req.assert('user', 'Phone, Email or Venmo User ID cannot be blank').notEmpty();
- req.assert('note', 'Please enter a message to accompany the payment').notEmpty();
- req.assert('amount', 'The amount you want to pay cannot be blank').notEmpty();
- var errors = req.validationErrors();
- if (errors) {
- req.flash('errors', errors);
- return res.redirect('/api/venmo');
- }
- var token = _.find(req.user.tokens, { kind: 'venmo' });
- var formData = {
- access_token: token.accessToken,
- note: req.body.note,
- amount: req.body.amount
- };
- if (validator.isEmail(req.body.user)) {
- formData.email = req.body.user;
- } else if (validator.isNumeric(req.body.user) &&
- validator.isLength(req.body.user, 10, 11)) {
- formData.phone = req.body.user;
- } else {
- formData.user_id = req.body.user;
- }
- request.post('https://api.venmo.com/v1/payments', { form: formData }, function(err, request, body) {
- if (err) return next(err);
- if (request.statusCode !== 200) {
- req.flash('errors', { msg: JSON.parse(body).error.message });
- return res.redirect('/api/venmo');
- }
- req.flash('success', { msg: 'Venmo money transfer complete' });
- res.redirect('/api/venmo');
- });
-};
-
-/**
- * GET /api/linkedin
- * LinkedIn API example.
- */
-
-exports.getLinkedin = function(req, res, next) {
- var token = _.find(req.user.tokens, { kind: 'linkedin' });
- var linkedin = Linkedin.init(token.accessToken);
- linkedin.people.me(function(err, $in) {
- if (err) return next(err);
- res.render('api/linkedin', {
- title: 'LinkedIn API',
- profile: $in
- });
- });
-};
-
-/**
- * GET /api/instagram
- * Instagram API example.
- */
-
-exports.getInstagram = function(req, res, next) {
- var token = _.find(req.user.tokens, { kind: 'instagram' });
- ig.use({ client_id: secrets.instagram.clientID, client_secret: secrets.instagram.clientSecret });
- ig.use({ access_token: token.accessToken });
- async.parallel({
- searchByUsername: function(done) {
- ig.user_search('richellemead', function(err, users, limit) {
- done(err, users);
- });
- },
- searchByUserId: function(done) {
- ig.user('175948269', function(err, user) {
- done(err, user);
- });
- },
- popularImages: function(done) {
- ig.media_popular(function(err, medias) {
- done(err, medias);
- });
- },
- myRecentMedia: function(done) {
- ig.user_self_media_recent(function(err, medias, pagination, limit) {
- done(err, medias);
- });
- }
- }, function(err, results) {
- if (err) return next(err);
- res.render('api/instagram', {
- title: 'Instagram API',
- usernames: results.searchByUsername,
- userById: results.searchByUserId,
- popularImages: results.popularImages,
- myRecentMedia: results.myRecentMedia
- });
- });
-};
-
-/**
- * GET /api/yahoo
- * Yahoo API example.
- */
-
-exports.getYahoo = function(req, res) {
- Y.YQL('SELECT * FROM weather.forecast WHERE (location = 10007)', function(response) {
- var location = response.query.results.channel.location;
- var condition = response.query.results.channel.item.condition;
- res.render('api/yahoo', {
- title: 'Yahoo API',
- location: location,
- condition: condition
- });
- });
-};
\ No newline at end of file
diff --git a/controllers/challenges.js b/controllers/challenges.js
index 382aa369a5..841b4941e9 100644
--- a/controllers/challenges.js
+++ b/controllers/challenges.js
@@ -2,24 +2,29 @@
* GET /
* Challenges.
*/
-var Challenge = require('./../models/Challenge');
-var _ = require('lodash');
+var _ = require('lodash'),
+ debug = require('debug')('freecc:cntr:challenges'),
+ Challenge = require('./../models/Challenge');
exports.returnChallenge = function(req, res, next) {
- var challengeNumber = parseInt(req.params.challengeNumber) || 0;
- if (challengeNumber > 59) { challengeNumber = 0; }
- Challenge.findOne({challengeNumber: challengeNumber}, function (err, c) {
- if (err) {
- console.error('Challenge err: ', err);
- next(err);
- }
- res.render('challenges/show', {
- title: 'Challenge: ' + c.name,
- name: c.name,
- video: c.video,
- time: c.time,
- steps: c.steps,
- cc: req.user.challengesHash
- });
+ var challengeNumber = parseInt(req.params.challengeNumber) || 0;
+
+ if (challengeNumber > 59) {
+ challengeNumber = 0;
+ }
+
+ Challenge.findOne({ challengeNumber: challengeNumber }, function(err, c) {
+ if (err) {
+ debug('Challenge err: ', err);
+ return next(err);
+ }
+ res.render('challenges/show', {
+ title: 'Challenge: ' + c.name,
+ name: c.name,
+ video: c.video,
+ time: c.time,
+ steps: c.steps,
+ cc: req.user.challengesHash
});
-};
\ No newline at end of file
+ });
+};
diff --git a/controllers/contact.js b/controllers/contact.js
index a66424d6f8..c35b6e5d6b 100644
--- a/controllers/contact.js
+++ b/controllers/contact.js
@@ -1,5 +1,7 @@
-var secrets = require('../config/secrets');
-var nodemailer = require("nodemailer");
+var nodemailer = require('nodemailer'),
+ debug = require('debug')('freecc:cntr:contact'),
+ secrets = require('../config/secrets');
+
var transporter = nodemailer.createTransport({
service: 'Mandrill',
auth: {
@@ -22,9 +24,6 @@ exports.getContact = function(req, res) {
/**
* POST /contact
* Send a contact form via Nodemailer.
- * @param email
- * @param name
- * @param message
*/
exports.postContact = function(req, res) {
@@ -32,24 +31,17 @@ exports.postContact = function(req, res) {
req.assert('email', 'Email is not valid').isEmail();
req.assert('message', 'Message cannot be blank').notEmpty();
- var errors = req.validationErrors();
-
- if (errors) {
+ if (req.validationErrors()) {
req.flash('errors', errors);
return res.redirect('/nonprofits');
}
- var from = req.body.email;
- var name = req.body.name;
- var body = req.body.message;
- var to = 'team@freecodecamp.com';
- var subject = "CodeNonprofit Project Idea from " + name;
-
var mailOptions = {
- to: to,
- from: from,
- subject: subject,
- text: body
+ to: 'team@freecodecamp.com',
+ name: req.body.name,
+ from: req.body.email,
+ subject: 'CodeNonprofit Project Idea from ' + name,
+ text: req.body.message
};
transporter.sendMail(mailOptions, function(err) {
diff --git a/controllers/home.js b/controllers/home.js
index 049a710008..343a28a9c5 100644
--- a/controllers/home.js
+++ b/controllers/home.js
@@ -5,19 +5,19 @@
exports.index = function(req, res) {
if (req.user) {
- ch = req.user.challengesHash;
- if (req.user.challengesHash[0] > 0) {
- var max = Object.keys(ch).reduce(function(max,key){
- return (max === undefined || ch[key] > ch[max]) ? +key : max;
- });
- nextChallenge = max + 1;
- res.redirect("challenges/" + nextChallenge);
- } else {
- res.redirect("challenges/0");
- }
- } else {
- res.render('home', {
- title: 'Learn to Code and Become a Software Engineer'
+ ch = req.user.challengesHash;
+ if (req.user.challengesHash[0] > 0) {
+ var max = Object.keys(ch).reduce(function(max, key) {
+ return (max === undefined || ch[key] > ch[max]) ? +key : max;
});
+ nextChallenge = max + 1;
+ res.redirect('challenges/' + nextChallenge);
+ } else {
+ res.redirect('challenges/0');
+ }
+ } else {
+ res.render('home', {
+ title: 'Learn to Code and Become a Software Engineer'
+ });
}
-};
\ No newline at end of file
+};
diff --git a/controllers/resources.js b/controllers/resources.js
index e43d665bf9..016b3c7f13 100644
--- a/controllers/resources.js
+++ b/controllers/resources.js
@@ -1,271 +1,98 @@
+var User = require('../models/User'),
+ resources = require('./resources.json'),
+ questions = resources.questions,
+ steps = resources.steps;
-var User = require('../models/User');
-
-var totalUsers = User.count({}, function( err, count){
- count;
+//NOTE(BERKS): Async, total users may not available before it is used.
+var totalUsers = 0;
+User.count({}, function(err, count) {
+ totalUsers = count;
});
-//var usersOverTenChallenges = User.where: "this.challengesCompleted && this.challengesCompleted.length >= 10"
-
/**
* GET /
* Resources.
*/
-
-exports.learnToCode = function(req, res) {
- res.render('learn-to-code', {
- title: 'Learn to Code'
+//TODO: Stats view
+module.exports = {
+ learnToCode: function(req, res) {
+ res.render('learn-to-code', {
+ title: 'Learn to Code'
});
-}
+ },
-exports.privacy = function(req, res) {
- res.render('privacy', {
- title: 'Privacy'
+ privacy: function privacy(req, res) {
+ res.render('privacy', {
+ title: 'Privacy'
});
-}
+ },
-exports.statistics = function(req, res) {
- res.render('statistics', {
- title: 'Code Camper Statistics',
- //totalUsers: totalUsers,
- //usersOverTenChallenges: usersOverTenChallenges
+ statistics: function statistics(req, res) {
+ res.render('statistics', {
+ title: 'Code Camper Statistics'
+ //totalUsers: totalUsers,
+ //usersOverTenChallenges: usersOverTenChallenges
});
-}
+ },
-exports.chromebook = function(req, res) {
- res.render('chromebook', {
- title: 'Win a Chromebook'
+ chromebook: function chromebook(req, res) {
+ res.render('chromebook', {
+ title: 'Win a Chromebook'
});
-}
+ },
-exports.jqueryExercises = function(req, res) {
- res.render('jquery-exercises', {
- title: 'jQuery Exercises'
+ jqueryExercises: function jqueryExercises(req, res) {
+ res.render('jquery-exercises', {
+ title: 'jQuery Exercises'
});
-}
+ },
-exports.livePairProgramming = function(req, res) {
- res.render('live-pair-programming', {
- title: 'Live Pair Programming'
+ livePairProgramming: function(req, res) {
+ res.render('live-pair-programming', {
+ title: 'Live Pair Programming'
});
-}
+ },
-exports.javaScriptInYourInbox = function(req, res) {
- res.render('javascript-in-your-inbox', {
- title: 'JavaScript in your Inbox'
+ javaScriptInYourInbox: function(req, res) {
+ res.render('javascript-in-your-inbox', {
+ title: 'JavaScript in your Inbox'
});
-}
+ },
-exports.programmerInterviewQuestionsApp = function(req, res) {
- res.render('programmer-interview-questions-app', {
- title: 'Programmer Interview Questions App'
+ programmerInterviewQuestionsApp: function(req, res) {
+ res.render('programmer-interview-questions-app', {
+ title: 'Programmer Interview Questions App'
});
-}
+ },
-exports.pairProgramWithTeamViewer = function(req, res) {
+ pairProgramWithTeamViewer: function(req, res) {
res.render('pair-program-with-team-viewer', {
- title: 'Challenge: Pair Program with Team Viewer',
- name: "Pair Program with Team Viewer",
- video: '',
- time: 30,
- steps: ["In the spirit of keeping Free Code Camp 100% free, we've created directions for using a totally free pair programming tool called Team Viewer. It's not as user-friendly as Screen Hero, but it gets the job done (and works on Linux!).",
- "Go to http://www.teamviewer.com/en/index.aspx and download Team Viewer. Be sure not to download the beta version, which isn't compatible with the current stable version everyone else will be using.",
- "Install it and launch it",
- "Go to the chat room and ask if anyone wants to pair program with you on a CoderByte challenge.",
- "If someone is interested, they will be your \"pair\" - the person you pair programming with.",
- "First, you will pair program on your pair's computer. Ask your pair for his or her Team Viewer ID and password.",
- "Enter this id, and then password, to start the session.",
- "Once the Team Viewer session starts, look at the the top of the screen. You will see 6 buttons. If you hover your cursor over the buttons, they will slide down so that you can read them. Click the audio/video button. This will allow you to turn on Voice Over IP and unmute your microphone, opening up your voice channel.",
- "Note that you can now control your pair's keyboard and mouse, enabling you to step in and code yourself on your pair's computer when desired",
- "Now you can click the X to end the pair programming session.",
- "Next, you will pair program on your computer. Copy your Team Viewer ID and paste it into the private chat, so that your pair can use it to connect with you.",
- "You will need to share your randomly generated password with your pair as well.",
- "Once your pair connects, you will see a Team Viewer side menu. ",
- "Click the audio button that drops down.",
- "Click the headset icon and choose Voice over IP",
- "Click the microphone button to unmute your microphone. Once your pair does the same, you two will have an open voice channel.",
- "Now you can click the X to end the pair programming session.",
- "Now it's time to tackle CoderByte.",
- "Create a CoderByte account at http://coderbyte.com/sl/",
- "Now go to http://coderbyte.com/CodingArea/Challenges/#easyChals and start working through Coderbyte's easy algorithm scripting challenges using JavaScript.",
- "When you are finished pair programming, click the X to end the session.",
- "Congratulations! You have completed your first pair programming session.",
- "You should pair program with different Code Campers until you've completed all the Easy, Medium and Hard CoderByte challenges. This is a big time investment, but the JavaScript practice you'll get, along with the scripting and algorithm experience, are well worth it!",
- "You can complete CoderByte problems while you continue to work through Free Code Camp's challenges.",
- "Be sure to pair program on these challenges, and remember to apply the RSAP methodology.",
- "Click the button below to return to the Pair Programming challenge, then mark it complete."],
- cc: req.user.challengesHash
+ title: 'Challenge: Pair Program with Team Viewer',
+ name: 'Pair Program with Team Viewer',
+ video: '',
+ time: 30,
+ steps: steps,
+ cc: req.user.challengesHash
});
-}
-exports.about = function(req, res) {
+ },
+
+ about: function(req, res) {
res.render('about', {
- title: 'About Free Code Camp and Our Team of Volunteers'
+ title: 'About Free Code Camp and Our Team of Volunteers'
});
-}
+ },
-exports.doneWithFirst100Hours = function(req, res) {
+ doneWithFirst100Hours: function(req, res) {
res.render('done-with-first-100-hours', {
- title: 'Congratulations on finishing the first 100 hours of Free Code Camp!'
+ title:
+ 'Congratulations on finishing the first 100 hours of Free Code Camp!'
});
-}
+ },
+
+ interviewQuestions: function(req, res) {
+ res.json(questions);
+ }
+};
+
+
-exports.interviewQuestions = function(req, res) {
- res.json([
- {
- question: "Time Complexity of Accessing Array Index (int a = ARR[5];)",
- answer: "O(1)"
- },
- {
- question: "Time Complexity of Inserting a node in Linked List",
- answer: "O(1)"
- },
- {
- question: "Time Complexity of Pushing and Popping on Stack",
- answer: "O(1)"
- },
- {
- question: "Time Complexity of Insertion and Removal from Queue",
- answer: "O(1)"
- },
- {
- question: "Time Complexity of Finding out the parent or left/right child of a node in a tree stored in Array",
- answer: "O(1)"
- },
- {
- question: "Time Complexity of Jumping to Next/Previous element in Doubly Linked List",
- answer: "O(1)"
- },
- {
- question: "Time Complexity of Traversing an array",
- answer: "O(n)"
- },
- {
- question: "Time Complexity of Traversing a linked list",
- answer: "O(n)"
- },
- {
- question: "Time Complexity of Linear Search",
- answer: "O(n)"
- },
- {
- question: "Time Complexity of Deletion of a specific element in a Linked List (Not sorted)",
- answer: "O(n)"
- },
- {
- question: "Time Complexity of Comparing two strings",
- answer: "O(n)"
- },
- {
- question: "Time Complexity of Checking for Palindrome",
- answer: "O(n)"
- },
- {
- question: "Time Complexity of Counting/Bucket Sort",
- answer: "O(n)"
- },
- {
- question: "Time Complexity of Binary Search",
- answer: "O(log n)"
- },
- {
- question: "Time Complexity of Finding largest/smallest number in a binary search tree",
- answer: "O(log n)"
- },
- {
- question: "Time Complexity of Certain Divide and Conquer Algorithms based on Linear functionality",
- answer: "O(log n)"
- },
- {
- question: "Time Complexity of Calculating Fibonacci Numbers - Best Method",
- answer: "O(log n)"
- },
- {
- question: "Time Complexity of Merge Sort",
- answer: "O(nlogn)"
- },
- {
- question: "Time Complexity of Heap Sort",
- answer: "O(nlogn)"
- },
- {
- question: "Time Complexity of Quick Sort",
- answer: "O(nlogn)"
- },
- {
- question: "Time Complexity of Certain Divide and Conquer Algorithms based on optimizing O(n^2) algorithms",
- answer: "O(nlogn)"
- },
- {
- question: "Time Complexity of Bubble Sort",
- answer: "O(n^2)"
- },
- {
- question: "Time Complexity of Insertion Sort",
- answer: "O(n^2)"
- },
- {
- question: "Time Complexity of Selection Sort",
- answer: "O(n^2)"
- },
- {
- question: "Time Complexity of Traversing a simple 2D array",
- answer: "O(n^2)"
- },
- {
- question: "Latency of L1 cache reference",
- answer: "0.5 nanoseconds"
- },
- {
- question: "Latency of Branch mispredict",
- answer: "5 nanoseconds"
- },
- {
- question: "Latency of L2 cache reference",
- answer: "7 nanoseconds"
- },
- {
- question: "Latency of Mutex lock/unlock",
- answer: "25 nanoseconds"
- },
- {
- question: "Latency of Main memory reference",
- answer: "100 nanoseconds"
- },
- {
- question: "Latency of Compressing 1K bytes with Zippy",
- answer: "3,000 nanoseconds"
- },
- {
- question: "Latency of Sending 1K bytes over a 1 Gbps network",
- answer: "10,000 nanoseconds"
- },
- {
- question: "Latency of Reading 4K randomly from SSD",
- answer: "150,000 nanoseconds"
- },
- {
- question: "Latency of Reading 1 MB sequentially from memory",
- answer: "250,000 nanoseconds"
- },
- {
- question: "Latency of a Round trip within the same datacenter",
- answer: "500,000 nanoseconds"
- },
- {
- question: "Latency of Reading 1 MB sequentially from SSD",
- answer: "1,000,000 nanoseconds"
- },
- {
- question: "Latency of Disk seek",
- answer: "10,000,000 nanoseconds"
- },
- {
- question: "Latency of Reading 1 MB sequentially from disk",
- answer: "20,000,000 nanoseconds"
- },
- {
- question: "Latency of Sending a packet from California to the Netherlands and back",
- answer: "150,000,000 nanoseconds"
- }
- ]);
-};
\ No newline at end of file
diff --git a/controllers/resources.json b/controllers/resources.json
new file mode 100644
index 0000000000..3b8504c88a
--- /dev/null
+++ b/controllers/resources.json
@@ -0,0 +1,148 @@
+{
+ "questions": [{
+ "question": "Time Complexity of Accessing Array Index (int a = ARR[5];)",
+ "answer": "O(1)"
+ }, {
+ "question": "Time Complexity of Inserting a node in Linked List",
+ "answer": "O(1)"
+ }, {
+ "question": "Time Complexity of Pushing and Popping on Stack",
+ "answer": "O(1)"
+ }, {
+ "question": "Time Complexity of Insertion and Removal from Queue",
+ "answer": "O(1)"
+ }, {
+ "question": "Time Complexity of Finding out the parent or left/right child of a node in a tree stored in Array",
+ "answer": "O(1)"
+ }, {
+ "question": "Time Complexity of Jumping to Next/Previous element in Doubly Linked List",
+ "answer": "O(1)"
+ }, {
+ "question": "Time Complexity of Traversing an array",
+ "answer": "O(n)"
+ }, {
+ "question": "Time Complexity of Traversing a linked list",
+ "answer": "O(n)"
+ }, {
+ "question": "Time Complexity of Linear Search",
+ "answer": "O(n)"
+ }, {
+ "question": "Time Complexity of Deletion of a specific element in a Linked List (Not sorted)",
+ "answer": "O(n)"
+ }, {
+ "question": "Time Complexity of Comparing two strings",
+ "answer": "O(n)"
+ }, {
+ "question": "Time Complexity of Checking for Palindrome",
+ "answer": "O(n)"
+ }, {
+ "question": "Time Complexity of Counting/Bucket Sort",
+ "answer": "O(n)"
+ }, {
+ "question": "Time Complexity of Binary Search",
+ "answer": "O(log n)"
+ }, {
+ "question": "Time Complexity of Finding largest/smallest number in a binary search tree",
+ "answer": "O(log n)"
+ }, {
+ "question": "Time Complexity of Certain Divide and Conquer Algorithms based on Linear functionality",
+ "answer": "O(log n)"
+ }, {
+ "question": "Time Complexity of Calculating Fibonacci Numbers - Best Method",
+ "answer": "O(log n)"
+ }, {
+ "question": "Time Complexity of Merge Sort",
+ "answer": "O(nlogn)"
+ }, {
+ "question": "Time Complexity of Heap Sort",
+ "answer": "O(nlogn)"
+ }, {
+ "question": "Time Complexity of Quick Sort",
+ "answer": "O(nlogn)"
+ }, {
+ "question": "Time Complexity of Certain Divide and Conquer Algorithms based on optimizing O(n^2) algorithms",
+ "answer": "O(nlogn)"
+ }, {
+ "question": "Time Complexity of Bubble Sort",
+ "answer": "O(n^2)"
+ }, {
+ "question": "Time Complexity of Insertion Sort",
+ "answer": "O(n^2)"
+ }, {
+ "question": "Time Complexity of Selection Sort",
+ "answer": "O(n^2)"
+ }, {
+ "question": "Time Complexity of Traversing a simple 2D array",
+ "answer": "O(n^2)"
+ }, {
+ "question": "Latency of L1 cache reference",
+ "answer": "0.5 nanoseconds"
+ }, {
+ "question": "Latency of Branch mispredict",
+ "answer": "5 nanoseconds"
+ }, {
+ "question": "Latency of L2 cache reference",
+ "answer": "7 nanoseconds"
+ }, {
+ "question": "Latency of Mutex lock/unlock",
+ "answer": "25 nanoseconds"
+ }, {
+ "question": "Latency of Main memory reference",
+ "answer": "100 nanoseconds"
+ }, {
+ "question": "Latency of Compressing 1K bytes with Zippy",
+ "answer": "3,000 nanoseconds"
+ }, {
+ "question": "Latency of Sending 1K bytes over a 1 Gbps network",
+ "answer": "10,000 nanoseconds"
+ }, {
+ "question": "Latency of Reading 4K randomly from SSD",
+ "answer": "150,000 nanoseconds"
+ }, {
+ "question": "Latency of Reading 1 MB sequentially from memory",
+ "answer": "250,000 nanoseconds"
+ }, {
+ "question": "Latency of a Round trip within the same datacenter",
+ "answer": "500,000 nanoseconds"
+ }, {
+ "question": "Latency of Reading 1 MB sequentially from SSD",
+ "answer": "1,000,000 nanoseconds"
+ }, {
+ "question": "Latency of Disk seek",
+ "answer": "10,000,000 nanoseconds"
+ }, {
+ "question": "Latency of Reading 1 MB sequentially from disk",
+ "answer": "20,000,000 nanoseconds"
+ }, {
+ "question": "Latency of Sending a packet from California to the Netherlands and back",
+ "answer": "150,000,000 nanoseconds"
+ }],
+ "steps": [
+ "In the spirit of keeping Free Code Camp 100% free, we've created directions for using a totally free pair programming tool called Team Viewer. It's not as user-friendly as Screen Hero, but it gets the job done (and works on Linux!).",
+ "Go to http://www.teamviewer.com/en/index.aspx and download Team Viewer. Be sure not to download the beta version, which isn't compatible with the current stable version everyone else will be using.",
+ "Install it and launch it",
+ "Go to the chat room and ask if anyone wants to pair program with you on a CoderByte challenge.",
+ "If someone is interested, they will be your \"pair\" - the person you pair programming with.",
+ "First, you will pair program on your pair's computer. Ask your pair for his or her Team Viewer ID and password.",
+ "Enter this id, and then password, to start the session.",
+ "Once the Team Viewer session starts, look at the the top of the screen. You will see 6 buttons. If you hover your cursor over the buttons, they will slide down so that you can read them. Click the audio/video button. This will allow you to turn on Voice Over IP and unmute your microphone, opening up your voice channel.",
+ "Note that you can now control your pair's keyboard and mouse, enabling you to step in and code yourself on your pair's computer when desired",
+ "Now you can click the X to end the pair programming session.",
+ "Next, you will pair program on your computer. Copy your Team Viewer ID and paste it into the private chat, so that your pair can use it to connect with you.",
+ "You will need to share your randomly generated password with your pair as well.",
+ "Once your pair connects, you will see a Team Viewer side menu. ",
+ "Click the audio button that drops down.",
+ "Click the headset icon and choose Voice over IP",
+ "Click the microphone button to unmute your microphone. Once your pair does the same, you two will have an open voice channel.",
+ "Now you can click the X to end the pair programming session.",
+ "Now it's time to tackle CoderByte.",
+ "Create a CoderByte account at http://coderbyte.com/sl/",
+ "Now go to http://coderbyte.com/CodingArea/Challenges/#easyChals and start working through Coderbyte's easy algorithm scripting challenges using JavaScript.",
+ "When you are finished pair programming, click the X to end the session.",
+ "Congratulations! You have completed your first pair programming session.",
+ "You should pair program with different Code Campers until you've completed all the Easy, Medium and Hard CoderByte challenges. This is a big time investment, but the JavaScript practice you'll get, along with the scripting and algorithm experience, are well worth it!",
+ "You can complete CoderByte problems while you continue to work through Free Code Camp's challenges.",
+ "Be sure to pair program on these challenges, and remember to apply the RSAP methodology.",
+ "Click the button below to return to the Pair Programming challenge, then mark it complete."
+ ]
+}
diff --git a/controllers/user.js b/controllers/user.js
index 48048b0c7c..45dc0fc6a7 100644
--- a/controllers/user.js
+++ b/controllers/user.js
@@ -7,6 +7,7 @@ var User = require('../models/User');
var secrets = require('../config/secrets');
var moment = require('moment');
+//TODO(Berks): Refactor to use module.exports = {} pattern.
/**
* GET /login
* Login page.
@@ -22,8 +23,6 @@ exports.getLogin = function(req, res) {
/**
* POST /login
* Sign in using email and password.
- * @param email
- * @param password
*/
exports.postLogin = function(req, res, next) {
@@ -88,14 +87,13 @@ exports.getEmailSignup = function(req, res) {
/**
* POST /email-signup
* Create a new local account.
- * @param email
- * @param password
*/
exports.postEmailSignup = function(req, res, next) {
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password must be at least 4 characters long').len(4);
- req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
+ req.assert('confirmPassword', 'Passwords do not match')
+ .equals(req.body.password);
var errors = req.validationErrors();
@@ -110,14 +108,19 @@ exports.postEmailSignup = function(req, res, next) {
});
User.findOne({ email: req.body.email }, function(err, existingUser) {
+ if (err) { return next(err); }
+
if (existingUser) {
- req.flash('errors', { msg: 'Account with that email address already exists.' });
+ req.flash('errors', {
+ msg: 'Account with that email address already exists.'
+ });
return res.redirect('/email-signup');
}
user.save(function(err) {
- if (err) return next(err);
+ if (err) { return next(err); }
+
req.logIn(user, function(err) {
- if (err) return next(err);
+ if (err) { return next(err); }
res.redirect('/email-signup');
});
});
@@ -188,12 +191,12 @@ exports.postUpdateProfile = function(req, res, next) {
/**
* POST /account/password
* Update current password.
- * @param password
*/
exports.postUpdatePassword = function(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);
+ req.assert('confirmPassword', 'Passwords do not match')
+ .equals(req.body.password);
var errors = req.validationErrors();
@@ -203,12 +206,13 @@ exports.postUpdatePassword = function(req, res, next) {
}
User.findById(req.user.id, function(err, user) {
- if (err) return next(err);
+ if (err) { return next(err); }
user.password = req.body.password;
user.save(function(err) {
- if (err) return next(err);
+ if (err) { return next(err); }
+
req.flash('success', { msg: 'Password has been changed.' });
res.redirect('/account');
});
@@ -222,7 +226,7 @@ exports.postUpdatePassword = function(req, res, next) {
exports.postDeleteAccount = function(req, res, next) {
User.remove({ _id: req.user.id }, function(err) {
- if (err) return next(err);
+ if (err) { return next(err); }
req.logout();
req.flash('info', { msg: 'Your account has been deleted.' });
res.redirect('/');
@@ -232,19 +236,21 @@ exports.postDeleteAccount = function(req, res, next) {
/**
* GET /account/unlink/:provider
* Unlink OAuth provider.
- * @param provider
*/
exports.getOauthUnlink = function(req, res, next) {
var provider = req.params.provider;
User.findById(req.user.id, function(err, user) {
- if (err) return next(err);
+ if (err) { return next(err); }
user[provider] = undefined;
- user.tokens = _.reject(user.tokens, function(token) { return token.kind === provider; });
+ user.tokens =
+ _.reject(user.tokens, function(token) {
+ return token.kind === provider;
+ });
user.save(function(err) {
- if (err) return next(err);
+ if (err) { return next(err); }
req.flash('info', { msg: provider + ' account has been unlinked.' });
res.redirect('/account');
});
@@ -264,8 +270,11 @@ exports.getReset = function(req, res) {
.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.' });
+ req.flash('errors', {
+ msg: 'Password reset token is invalid or has expired.'
+ });
return res.redirect('/forgot');
}
res.render('account/reset', {
@@ -277,7 +286,6 @@ exports.getReset = function(req, res) {
/**
* POST /reset/:token
* Process the reset password request.
- * @param token
*/
exports.postReset = function(req, res, next) {
@@ -297,8 +305,11 @@ exports.postReset = function(req, res, next) {
.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.' });
+ req.flash('errors', {
+ msg: 'Password reset token is invalid or has expired.'
+ });
return res.redirect('back');
}
@@ -307,7 +318,7 @@ exports.postReset = function(req, res, next) {
user.resetPasswordExpires = undefined;
user.save(function(err) {
- if (err) return next(err);
+ if (err) { return done(err); }
req.logIn(user, function(err) {
done(err, user);
});
@@ -326,16 +337,25 @@ exports.postReset = function(req, res, next) {
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'
+ 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) {
- req.flash('success', { msg: 'Success! Your password has been changed.' });
- done(err);
+ if (err) { return done(err); }
+ req.flash('success', {
+ msg: 'Success! Your password has been changed.'
+ });
+ done();
});
}
], function(err) {
- if (err) return next(err);
+ if (err) { return next(err); }
res.redirect('/');
});
};
@@ -357,7 +377,6 @@ exports.getForgot = function(req, res) {
/**
* POST /forgot
* Create a random token, then the send user an email with a reset link.
- * @param email
*/
exports.postForgot = function(req, res, next) {
@@ -373,14 +392,20 @@ exports.postForgot = function(req, res, next) {
async.waterfall([
function(done) {
crypto.randomBytes(16, function(err, buf) {
+ if (err) { return done(err); }
var token = buf.toString('hex');
- done(err, token);
+ done(null, token);
});
},
function(token, done) {
- User.findOne({ email: req.body.email.toLowerCase() }, function(err, user) {
+ 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.' });
+ req.flash('errors', {
+ msg: 'No account with that email address exists.'
+ });
return res.redirect('/forgot');
}
@@ -388,7 +413,8 @@ exports.postForgot = function(req, res, next) {
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
user.save(function(err) {
- done(err, token, user);
+ if (err) { return done(err); }
+ done(null, token, user);
});
});
},
@@ -404,18 +430,32 @@ exports.postForgot = function(req, res, next) {
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) requested we reset your Free Code Camp account's password.\n\n" +
- 'Please click on the following link, or paste this into your 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 your password will remain unchanged.\n'
+ text: [
+ 'You are receiving this email because you (or someone else)',
+ 'requested we reset your Free Code Camp account\'s password.\n\n',
+ 'Please click on the following link, or paste this into your',
+ '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',
+ 'your password will remain unchanged.\n'
+ ].join(' ')
};
transporter.sendMail(mailOptions, function(err) {
- req.flash('info', { msg: 'An e-mail has been sent to ' + user.email + ' with further instructions.' });
- done(err, 'done');
+ 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);
+ if (err) { return next(err); }
res.redirect('/forgot');
});
};
diff --git a/gulpfile.js b/gulpfile.js
index faf61f7339..cc488c5d05 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -9,8 +9,9 @@ var gulp = require('gulp'),
var paths = {
server: './app.js',
- serverIgnore: [],
-}
+ serverIgnore: []
+};
+
gulp.task('inject', function() {
gulp.src('views/home.jade')
.pipe(inject(gulp.src(bower()), {
@@ -51,15 +52,12 @@ gulp.task('serve', function(cb) {
gulp.task('sync', ['serve'], function() {
sync.init(null, {
- proxy: 'http://localhost:3000',
- logLeval: 'debug',
- files: [
- 'public/**/*',
- ],
- port: 3001,
- open: true,
- browser: ['safari', 'google chrome'],
- reloadDelay: reloadDelay
+ proxy: 'http://localhost:3000',
+ logLeval: 'debug',
+ files: ['public/**/*'],
+ port: 3001,
+ open: true,
+ reloadDelay: reloadDelay
});
});
diff --git a/models/Challenge.js b/models/Challenge.js
index 28759a81ad..63e2f85ef5 100644
--- a/models/Challenge.js
+++ b/models/Challenge.js
@@ -2,12 +2,15 @@ var mongoose = require('mongoose');
var secrets = require('../config/secrets');
var challengeSchema = new mongoose.Schema({
- name: { type: String, unique: true },
- link: String,
- time: String,
- challengeNumber: Number,
- video: String,
- steps: Array
+ name: {
+ type: String,
+ unique: true
+ },
+ link: String,
+ time: String,
+ challengeNumber: Number,
+ video: String,
+ steps: Array
});
-var Challenge = module.exports = mongoose.model('Challenge', challengeSchema);
\ No newline at end of file
+module.exports = mongoose.model('Challenge', challengeSchema);
diff --git a/models/User.js b/models/User.js
index 27794012a5..5d789e0e9c 100644
--- a/models/User.js
+++ b/models/User.js
@@ -3,7 +3,12 @@ var crypto = require('crypto');
var mongoose = require('mongoose');
var userSchema = new mongoose.Schema({
- //email: { type: String, unique: true, lowercase: true, match: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/ },
+ /*email: {
+ type: String,
+ unique: true,
+ lowercase: true,
+ match: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/
+ },*/
email: String,
password: String,
@@ -14,77 +19,277 @@ var userSchema = new mongoose.Schema({
instagram: String,
linkedin: String,
tokens: Array,
- points: { type: Number, default: 0 },
+ points: {
+ type: Number,
+ default: 0
+ },
challengesCompleted: { type: Array, default: [] },
challengesHash: {
- 0: { type: Number, default: 0 },
- 1: { type: Number, default: 0 },
- 2: { type: Number, default: 0 },
- 3: { type: Number, default: 0 },
- 4: { type: Number, default: 0 },
- 5: { type: Number, default: 0 },
- 6: { type: Number, default: 0 },
- 7: { type: Number, default: 0 },
- 8: { type: Number, default: 0 },
- 9: { type: Number, default: 0 },
- 10: { type: Number, default: 0 },
- 11: { type: Number, default: 0 },
- 12: { type: Number, default: 0 },
- 13: { type: Number, default: 0 },
- 14: { type: Number, default: 0 },
- 15: { type: Number, default: 0 },
- 16: { type: Number, default: 0 },
- 17: { type: Number, default: 0 },
- 18: { type: Number, default: 0 },
- 19: { type: Number, default: 0 },
- 20: { type: Number, default: 0 },
- 21: { type: Number, default: 0 },
- 22: { type: Number, default: 0 },
- 23: { type: Number, default: 0 },
- 24: { type: Number, default: 0 },
- 25: { type: Number, default: 0 },
- 26: { type: Number, default: 0 },
- 27: { type: Number, default: 0 },
- 28: { type: Number, default: 0 },
- 29: { type: Number, default: 0 },
- 30: { type: Number, default: 0 },
- 31: { type: Number, default: 0 },
- 32: { type: Number, default: 0 },
- 33: { type: Number, default: 0 },
- 34: { type: Number, default: 0 },
- 35: { type: Number, default: 0 },
- 36: { type: Number, default: 0 },
- 37: { type: Number, default: 0 },
- 38: { type: Number, default: 0 },
- 39: { type: Number, default: 0 },
- 40: { type: Number, default: 0 },
- 41: { type: Number, default: 0 },
- 42: { type: Number, default: 0 },
- 43: { type: Number, default: 0 },
- 44: { type: Number, default: 0 },
- 45: { type: Number, default: 0 },
- 46: { type: Number, default: 0 },
- 47: { type: Number, default: 0 },
- 48: { type: Number, default: 0 },
- 49: { type: Number, default: 0 },
- 50: { type: Number, default: 0 },
- 51: { type: Number, default: 0 },
- 52: { type: Number, default: 0 },
- 53: { type: Number, default: 0 },
- 54: { type: Number, default: 0 },
- 55: { type: Number, default: 0 },
- 56: { type: Number, default: 0 },
- 57: { type: Number, default: 0 },
- 58: { type: Number, default: 0 },
- 59: { type: Number, default: 0 }
+ 0: {
+ type: Number,
+ default: 0
+ },
+ 1: {
+ type: Number,
+ default: 0
+ },
+ 2: {
+ type: Number,
+ default: 0
+ },
+ 3: {
+ type: Number,
+ default: 0
+ },
+ 4: {
+ type: Number,
+ default: 0
+ },
+ 5: {
+ type: Number,
+ default: 0
+ },
+ 6: {
+ type: Number,
+ default: 0
+ },
+ 7: {
+ type: Number,
+ default: 0
+ },
+ 8: {
+ type: Number,
+ default: 0
+ },
+ 9: {
+ type: Number,
+ default: 0
+ },
+ 10: {
+ type: Number,
+ default: 0
+ },
+ 11: {
+ type: Number,
+ default: 0
+ },
+ 12: {
+ type: Number,
+ default: 0
+ },
+ 13: {
+ type: Number,
+ default: 0
+ },
+ 14: {
+ type: Number,
+ default: 0
+ },
+ 15: {
+ type: Number,
+ default: 0
+ },
+ 16: {
+ type: Number,
+ default: 0
+ },
+ 17: {
+ type: Number,
+ default: 0
+ },
+ 18: {
+ type: Number,
+ default: 0
+ },
+ 19: {
+ type: Number,
+ default: 0
+ },
+ 20: {
+ type: Number,
+ default: 0
+ },
+ 21: {
+ type: Number,
+ default: 0
+ },
+ 22: {
+ type: Number,
+ default: 0
+ },
+ 23: {
+ type: Number,
+ default: 0
+ },
+ 24: {
+ type: Number,
+ default: 0
+ },
+ 25: {
+ type: Number,
+ default: 0
+ },
+ 26: {
+ type: Number,
+ default: 0
+ },
+ 27: {
+ type: Number,
+ default: 0
+ },
+ 28: {
+ type: Number,
+ default: 0
+ },
+ 29: {
+ type: Number,
+ default: 0
+ },
+ 30: {
+ type: Number,
+ default: 0
+ },
+ 31: {
+ type: Number,
+ default: 0
+ },
+ 32: {
+ type: Number,
+ default: 0
+ },
+ 33: {
+ type: Number,
+ default: 0
+ },
+ 34: {
+ type: Number,
+ default: 0
+ },
+ 35: {
+ type: Number,
+ default: 0
+ },
+ 36: {
+ type: Number,
+ default: 0
+ },
+ 37: {
+ type: Number,
+ default: 0
+ },
+ 38: {
+ type: Number,
+ default: 0
+ },
+ 39: {
+ type: Number,
+ default: 0
+ },
+ 40: {
+ type: Number,
+ default: 0
+ },
+ 41: {
+ type: Number,
+ default: 0
+ },
+ 42: {
+ type: Number,
+ default: 0
+ },
+ 43: {
+ type: Number,
+ default: 0
+ },
+ 44: {
+ type: Number,
+ default: 0
+ },
+ 45: {
+ type: Number,
+ default: 0
+ },
+ 46: {
+ type: Number,
+ default: 0
+ },
+ 47: {
+ type: Number,
+ default: 0
+ },
+ 48: {
+ type: Number,
+ default: 0
+ },
+ 49: {
+ type: Number,
+ default: 0
+ },
+ 50: {
+ type: Number,
+ default: 0
+ },
+ 51: {
+ type: Number,
+ default: 0
+ },
+ 52: {
+ type: Number,
+ default: 0
+ },
+ 53: {
+ type: Number,
+ default: 0
+ },
+ 54: {
+ type: Number,
+ default: 0
+ },
+ 55: {
+ type: Number,
+ default: 0
+ },
+ 56: {
+ type: Number,
+ default: 0
+ },
+ 57: {
+ type: Number,
+ default: 0
+ },
+ 58: {
+ type: Number,
+ default: 0
+ },
+ 59: {
+ type: Number,
+ default: 0
+ }
},
profile: {
- name: { type: String, default: '' },
- gender: { type: String, default: '' },
- location: { type: String, default: '' },
- website: { type: String, default: '' },
- picture: { type: String, default: '' }
- //username: { type: String, default: '', unique: true, match: /^[a-zA-Z0-9_]+$/ }
+ name: {
+ type: String, default: ''
+ },
+ gender: {
+ type: String, default: ''
+ },
+ location: {
+ type: String, default: ''
+ },
+ website: {
+ type: String,
+ default: ''
+ },
+ picture: {
+ type: String,
+ default: ''
+ }
+ /*username: {
+ type: String,
+ default: '',
+ unique: true,
+ match: /^[a-zA-Z0-9_]+$/
+ }*/
},
resetPasswordToken: String,
@@ -133,7 +338,11 @@ userSchema.methods.gravatar = function(size) {
return 'https://gravatar.com/avatar/?s=' + size + '&d=retro';
}
- var md5 = crypto.createHash('md5').update(this.email).digest('hex');
+ var md5 = crypto
+ .createHash('md5')
+ .update(this.email)
+ .digest('hex');
+
return 'https://gravatar.com/avatar/' + md5 + '?s=' + size + '&d=retro';
};
diff --git a/public/js/main.js b/public/js/main.js
index c4d98b043d..f1d9507b1b 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -1,40 +1,44 @@
$(document).ready(function() {
- var CSRF_HEADER = 'X-CSRF-Token';
+ var CSRF_HEADER = 'X-CSRF-Token';
- var setCSRFToken = function(securityToken) {
- jQuery.ajaxPrefilter(function(options, _, xhr) {
- if ( !xhr.crossDomain )
- xhr.setRequestHeader(CSRF_HEADER, securityToken);
- });
- };
-
- setCSRFToken($('meta[name="csrf-token"]').attr('content'));
-
- $('.start-challenge').on("click", function() {
- $(this).parent().remove();
- $('.challenge-content').removeClass('hidden-element').addClass('animated fadeInDown');
+ var setCSRFToken = function(securityToken) {
+ jQuery.ajaxPrefilter(function(options, _, xhr) {
+ if (!xhr.crossDomain) {
+ xhr.setRequestHeader(CSRF_HEADER, securityToken);
+ }
});
+ };
- $('.completed-challenge').on("click", function() {
- $('#complete-dialog').modal('show');
- l = location.pathname.split('/');
- cn = l[l.length - 1]
- console.log(cn);
- $.ajax({
- type: 'POST',
- data: {challengeNumber: cn},
- url: '/completed_challenge/'
- });
- });
+ setCSRFToken($('meta[name="csrf-token"]').attr('content'));
- $('.skip-challenge').on("click", function() {
- $('#skip-dialog').modal('show');
- });
+ $('.start-challenge').on('click', function() {
+ $(this).parent().remove();
+ $('.challenge-content')
+ .removeClass('hidden-element')
+ .addClass('animated fadeInDown');
+ });
- $('.next-button').on("click", function() {
- l = location.pathname.split('/');
- window.location = "/challenges/" + (parseInt(l[l.length-1]) + 1)
- });
+ $('.completed-challenge').on('click', function() {
+ $('#complete-dialog').modal('show');
+
+ l = location.pathname.split('/');
+ cn = l[l.length - 1];
+ console.log(cn);
+ $.ajax({
+ type: 'POST',
+ data: {challengeNumber: cn},
+ url: '/completed_challenge/'
+ });
+ });
+
+ $('.skip-challenge').on('click', function() {
+ $('#skip-dialog').modal('show');
+ });
+
+ $('.next-button').on('click', function() {
+ l = location.pathname.split('/');
+ window.location = '/challenges/' + (parseInt(l[l.length - 1]) + 1);
+ });
});
diff --git a/seed_data/seed_challenges.js b/seed_data/seed_challenges.js
index b54e9c2f68..3d1e9897a3 100644
--- a/seed_data/seed_challenges.js
+++ b/seed_data/seed_challenges.js
@@ -1,15 +1,22 @@
-var Challenge = require('../models/Challenge.js')
-var mongoose = require('mongoose');
-var secrets = require('../config/secrets');
-var challenges = require('./challenges.json');
+var Challenge = require('../models/Challenge.js'),
+ mongoose = require('mongoose'),
+ secrets = require('../config/secrets'),
+ challenges = require('./challenges.json');
mongoose.connect(secrets.db);
-Challenge.remove({}, function(err, data){
- if (err) console.log(err);
- else console.log('Deleted ', data );
+
+Challenge.remove({}, function(err, data) {
+ if (err) {
+ console.error(err);
+ } else {
+ console.log('Deleted ', data);
+ }
Challenge.create(challenges, function(err, data) {
- if (err) console.log(err);
- else console.log('Saved ', data );
+ if (err) {
+ console.log(err);
+ } else {
+ console.log('Saved ', data);
+ }
process.exit(0);
});
});
diff --git a/setup.js b/setup.js
index bc0456afd3..775853162e 100644
--- a/setup.js
+++ b/setup.js
@@ -50,7 +50,8 @@ var footer = blessed.text({
fg: 'white',
bg: 'blue',
tags: true,
- content: ' {cyan-fg}{/cyan-fg} moves | {cyan-fg}{/cyan-fg} selects | {cyan-fg}{/cyan-fg} exits'
+ content: ' {cyan-fg}{/cyan-fg} moves |' +
+ ' {cyan-fg}{/cyan-fg} selects | {cyan-fg}{/cyan-fg} exits'
});
var inner = blessed.form({
@@ -97,7 +98,8 @@ var clusterText = blessed.text({
bg: 'red',
fg: 'white',
tags: true,
- content: 'Take advantage of multi-core systems using built-in {underline}cluster{/underline} module.'
+ content: 'Take advantage of multi-core systems' +
+ ' using built-in {underline}cluster{/underline} module.'
});
@@ -186,38 +188,54 @@ var authForm = blessed.form({
});
authForm.on('submit', function() {
- var passportConfig = fs.readFileSync('config/passport.js').toString().split(os.EOL);
- var loginTemplate = fs.readFileSync('views/account/login.jade').toString().split(os.EOL);
- var profileTemplate = fs.readFileSync('views/account/profile.jade').toString().split(os.EOL);
+ var passportConfig = fs.readFileSync('config/passport.js')
+ .toString().split(os.EOL);
+ var loginTemplate = fs.readFileSync('views/account/login.jade')
+ .toString().split(os.EOL);
+ var profileTemplate = fs.readFileSync('views/account/profile.jade')
+ .toString().split(os.EOL);
var userModel = fs.readFileSync('models/User.js').toString().split(os.EOL);
var app = fs.readFileSync('app.js').toString().split(os.EOL);
var secrets = fs.readFileSync('config/secrets.js').toString().split(os.EOL);
- var index = passportConfig.indexOf("var FacebookStrategy = require('passport-facebook').Strategy;");
+ var index = passportConfig
+ .indexOf('var FacebookStrategy = require("passport-facebook").Strategy;');
if (facebookCheckbox.checked && index !== -1) {
passportConfig.splice(index, 1);
index = passportConfig.indexOf('// Sign in with Facebook.');
passportConfig.splice(index, 47);
fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
- index = loginTemplate.indexOf(" a.btn.btn-block.btn-facebook.btn-social(href='/auth/facebook')");
+ index = loginTemplate.indexOf(
+ ' a.btn.btn-block.btn-facebook.btn-social(href="/auth/facebook")'
+ );
loginTemplate.splice(index, 3);
fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
- index = profileTemplate.indexOf(" if user.facebook");
+ index = profileTemplate.indexOf(' if user.facebook');
profileTemplate.splice(index - 1, 5);
- fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL));
+ fs.writeFileSync(
+ 'views/account/profile.jade',
+ profileTemplate.join(os.EOL)
+ );
index = userModel.indexOf(' facebook: String,');
userModel.splice(index, 1);
fs.writeFileSync('models/User.js', userModel.join(os.EOL));
- index = app.indexOf("app.get('/auth/facebook', passport.authenticate('facebook', { scope: ['email', 'user_location'] }));");
+ index = app.indexOf([
+ 'app.get("/auth/facebook"',
+ 'passport.authenticate("facebook",',
+ ' { scope: ["email", "user_location"] }));'
+ ].join(' '));
+
app.splice(index, 4);
fs.writeFileSync('app.js', app.join(os.EOL));
}
- index = passportConfig.indexOf("var GitHubStrategy = require('passport-github').Strategy;");
+ index = passportConfig.indexOf(
+ "var GitHubStrategy = require('passport-github').Strategy;"
+ );
if (githubCheckbox.checked && index !== -1) {
console.log(index);
passportConfig.splice(index, 1);
@@ -225,111 +243,162 @@ authForm.on('submit', function() {
passportConfig.splice(index, 48);
fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
- index = loginTemplate.indexOf(" a.btn.btn-block.btn-github.btn-social(href='/auth/github')");
+ index = loginTemplate.indexOf(
+ " a.btn.btn-block.btn-github.btn-social(href='/auth/github')"
+ );
loginTemplate.splice(index, 3);
fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
index = profileTemplate.indexOf(' if user.github');
profileTemplate.splice(index - 1, 5);
- fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL));
+ fs.writeFileSync(
+ 'views/account/profile.jade',
+ profileTemplate.join(os.EOL)
+ );
index = userModel.indexOf(' github: String,');
userModel.splice(index, 1);
fs.writeFileSync('models/User.js', userModel.join(os.EOL));
- index = app.indexOf("app.get('/auth/github', passport.authenticate('github'));");
+ index = app.indexOf(
+ 'app.get("/auth/github", passport.authenticate("github"));'
+ );
app.splice(index, 4);
fs.writeFileSync('app.js', app.join(os.EOL));
}
- index = passportConfig.indexOf("var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;");
+ index = passportConfig.indexOf(
+ 'var GoogleStrategy = require("passport-google-oauth").OAuth2Strategy;'
+ );
if (googleCheckbox.checked && index !== -1) {
passportConfig.splice(index, 1);
index = passportConfig.indexOf('// Sign in with Google.');
passportConfig.splice(index, 46);
fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
- index = loginTemplate.indexOf(" a.btn.btn-block.btn-google-plus.btn-social(href='/auth/google')");
+ index = loginTemplate.indexOf(
+ " a.btn.btn-block.btn-google-plus.btn-social(href='/auth/google')"
+ );
loginTemplate.splice(index, 3);
fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
index = profileTemplate.indexOf(' if user.google');
profileTemplate.splice(index - 1, 5);
- fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL));
+ fs.writeFileSync(
+ 'views/account/profile.jade',
+ profileTemplate.join(os.EOL)
+ );
index = userModel.indexOf(' google: String,');
userModel.splice(index, 1);
fs.writeFileSync('models/User.js', userModel.join(os.EOL));
- index = app.indexOf("app.get('/auth/google', passport.authenticate('google', { scope: 'profile email' }));");
+ index = app.indexOf(
+ 'app.get("/auth/google",' +
+ ' passport.authenticate("google", { scope: "profile email" }));'
+ );
app.splice(index, 4);
fs.writeFileSync('app.js', app.join(os.EOL));
}
- index = passportConfig.indexOf("var TwitterStrategy = require('passport-twitter').Strategy;");
+ index = passportConfig.indexOf(
+ 'var TwitterStrategy = require("passport-twitter").Strategy;'
+ );
if (twitterCheckbox.checked && index !== -1) {
passportConfig.splice(index, 1);
index = passportConfig.indexOf('// Sign in with Twitter.');
passportConfig.splice(index, 43);
fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
- index = loginTemplate.indexOf(" a.btn.btn-block.btn-twitter.btn-social(href='/auth/twitter')");
+ index = loginTemplate.indexOf(
+ ' a.btn.btn-block.btn-twitter.btn-social(href="/auth/twitter")'
+ );
loginTemplate.splice(index, 3);
fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
index = profileTemplate.indexOf(' if user.twitter');
profileTemplate.splice(index - 1, 5);
- fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL));
+ fs.writeFileSync(
+ 'views/account/profile.jade', profileTemplate.join(os.EOL)
+ );
index = userModel.indexOf(' twitter: String,');
userModel.splice(index, 1);
fs.writeFileSync('models/User.js', userModel.join(os.EOL));
- index = app.indexOf("app.get('/auth/twitter', passport.authenticate('twitter'));");
+ index = app.indexOf(
+ "app.get('/auth/twitter', passport.authenticate('twitter'));"
+ );
app.splice(index, 4);
fs.writeFileSync('app.js', app.join(os.EOL));
}
- index = passportConfig.indexOf("var LinkedInStrategy = require('passport-linkedin-oauth2').Strategy;");
+ index = passportConfig.indexOf(
+ 'var LinkedInStrategy = require("passport-linkedin-oauth2").Strategy;'
+ );
if (linkedinCheckbox.checked && index !== -1) {
passportConfig.splice(index, 1);
index = passportConfig.indexOf('// Sign in with LinkedIn.');
passportConfig.splice(index, 47);
fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
- index = loginTemplate.indexOf(" a.btn.btn-block.btn-linkedin.btn-social(href='/auth/linkedin')");
+ index = loginTemplate.indexOf(
+ ' a.btn.btn-block.btn-linkedin.btn-social(href="/auth/linkedin")'
+ );
loginTemplate.splice(index, 3);
fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
index = profileTemplate.indexOf(' if user.linkedin');
profileTemplate.splice(index - 1, 5);
- fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL));
+ fs.writeFileSync(
+ 'views/account/profile.jade',
+ profileTemplate.join(os.EOL)
+ );
index = userModel.indexOf(' linkedin: String,');
userModel.splice(index, 1);
fs.writeFileSync('models/User.js', userModel.join(os.EOL));
- index = app.indexOf("app.get('/auth/linkedin', passport.authenticate('linkedin', { state: 'SOME STATE' }));");
+ index = app.indexOf(
+ 'app.get("/auth/linkedin",',
+ ' passport.authenticate("linkedin", { state: "SOME STATE" }));'
+ );
app.splice(index, 4);
fs.writeFileSync('app.js', app.join(os.EOL));
}
- index = passportConfig.indexOf("var InstagramStrategy = require('passport-instagram').Strategy;");
+ index = passportConfig.indexOf(
+ 'var InstagramStrategy = require("passport-instagram").Strategy;'
+ );
if (instagramCheckbox.checked && index !== -1) {
passportConfig.splice(index, 1);
index = passportConfig.indexOf('// Sign in with Instagram.');
passportConfig.splice(index, 43);
- fs.writeFileSync('config/passport.js', passportConfig.join(os.EOL));
+ fs.writeFileSync(
+ 'config/passport.js',
+ passportConfig.join(os.EOL)
+ );
- index = loginTemplate.indexOf(" a.btn.btn-block.btn-instagram.btn-social(href='/auth/instagram')");
+ index = loginTemplate.indexOf(
+ ' a.btn.btn-block.btn-instagram.btn-social(href="/auth/instagram")'
+ );
loginTemplate.splice(index, 3);
- fs.writeFileSync('views/account/login.jade', loginTemplate.join(os.EOL));
+
+ fs.writeFileSync(
+ 'views/account/login.jade',
+ loginTemplate.join(os.EOL)
+ );
index = profileTemplate.indexOf(' if user.instagram');
profileTemplate.splice(index - 1, 5);
- fs.writeFileSync('views/account/profile.jade', profileTemplate.join(os.EOL));
+ fs.writeFileSync(
+ 'views/account/profile.jade',
+ profileTemplate.join(os.EOL)
+ );
- index = app.indexOf("app.get('/auth/instagram', passport.authenticate('instagram'));");
+ index = app.indexOf(
+ 'app.get("/auth/instagram", passport.authenticate("instagram"));'
+ );
app.splice(index, 4);
fs.writeFileSync('app.js', app.join(os.EOL));
@@ -339,14 +408,18 @@ authForm.on('submit', function() {
home.remove(authForm);
home.append(success);
- success.setContent('Selected authentication providers have been removed from passportConfig.js, User.js, app.js, login.jade and profile.jade!');
+ success.setContent(
+ 'Selected authentication providers have been removed' +
+ 'from passportConfig.js, User.js, app.js, login.jade and profile.jade!'
+ );
success.focus();
screen.render();
});
var authText = blessed.text({
parent: authForm,
- content: 'Selecting a checkbox removes an authentication provider. If authentication provider is already removed, no action will be taken.',
+ content: 'Selecting a checkbox removes an authentication provider.' +
+ ' If authentication provider is already removed, no action will be taken.',
padding: 1,
bg: 'magenta',
fg: 'white'
@@ -460,8 +533,10 @@ var emailForm = blessed.form({
});
emailForm.on('submit', function() {
- var contactCtrl = fs.readFileSync('controllers/contact.js').toString().split(os.EOL);
- var userCtrl = fs.readFileSync('controllers/user.js').toString().split(os.EOL);
+ var contactCtrl = fs.readFileSync('controllers/contact.js')
+ .toString().split(os.EOL);
+ var userCtrl = fs.readFileSync('controllers/user.js')
+ .toString().split(os.EOL);
var choice = null;
if (sendgridRadio.checked) {
@@ -472,20 +547,57 @@ emailForm.on('submit', function() {
choice = 'Mandrill';
}
- var index = contactCtrl.indexOf('var transporter = nodemailer.createTransport({');
+ var index = contactCtrl.indexOf(
+ 'var transporter = nodemailer.createTransport({'
+ );
contactCtrl.splice(index + 1, 1, " service: '" + choice + "',");
- contactCtrl.splice(index + 3, 1, ' user: secrets.' + choice.toLowerCase() +'.user,');
- contactCtrl.splice(index + 4, 1, ' pass: secrets.' + choice.toLowerCase() + '.password');
+ contactCtrl.splice(
+ index + 3,
+ 1,
+ ' user: secrets.' + choice.toLowerCase() + '.user,'
+ );
+ contactCtrl.splice(
+ index + 4,
+ 1,
+ ' pass: secrets.' + choice.toLowerCase() + '.password'
+ );
fs.writeFileSync('controllers/contact.js', contactCtrl.join(os.EOL));
- index = userCtrl.indexOf(' var transporter = nodemailer.createTransport({');
+ index = userCtrl.indexOf(
+ ' var transporter = nodemailer.createTransport({'
+ );
userCtrl.splice(index + 1, 1, " service: '" + choice + "',");
- userCtrl.splice(index + 3, 1, ' user: secrets.' + choice.toLowerCase() + '.user,');
- userCtrl.splice(index + 4, 1, ' pass: secrets.' + choice.toLowerCase() + '.password');
- index = userCtrl.indexOf(' var transporter = nodemailer.createTransport({', (index + 1));
- userCtrl.splice(index + 1, 1, " service: '" + choice + "',");
- userCtrl.splice(index + 3, 1, ' user: secrets.' + choice.toLowerCase() + '.user,');
- userCtrl.splice(index + 4, 1, ' pass: secrets.' + choice.toLowerCase() + '.password');
+ userCtrl.splice(
+ index + 3,
+ 1,
+ ' user: secrets.' + choice.toLowerCase() + '.user,'
+ );
+ userCtrl.splice(
+ index + 4,
+ 1,
+ ' pass: secrets.' + choice.toLowerCase() + '.password'
+ );
+
+ index = userCtrl.indexOf(
+ ' var transporter = nodemailer.createTransport({',
+ (index + 1)
+ );
+ userCtrl.splice(
+ index + 1,
+ 1,
+ ' service: "' + choice + '",'
+ );
+
+ userCtrl.splice(
+ index + 3,
+ 1,
+ ' user: secrets.' + choice.toLowerCase() + '.user,'
+ );
+ userCtrl.splice(
+ index + 4,
+ 1,
+ ' pass: secrets.' + choice.toLowerCase() + '.password'
+ );
fs.writeFileSync('controllers/user.js', userCtrl.join(os.EOL));
home.remove(emailForm);
@@ -497,7 +609,9 @@ emailForm.on('submit', function() {
var emailText = blessed.text({
parent: emailForm,
- content: 'Select one of the following email service providers for {underline}contact form{/underline} and {underline}password reset{/underline}.',
+ content: 'Select one of the following email service providers ' +
+ 'for {underline}contact form{/underline}' +
+ ' and {underline}password reset{/underline}.',
padding: 1,
bg: 'red',
fg: 'white',
@@ -592,7 +706,12 @@ home.on('select', function(child, index) {
case 2:
addClusterSupport();
home.append(success);
- success.setContent('New file {underline}cluster_app.js{/underline} has been created. Your app is now able to use more than 1 CPU by running {underline}node cluster_app.js{/underline}, which in turn spawns multiple instances of {underline}app.js{/underline}');
+ success.setContent([
+ 'New file {underline}cluster_app.js{/underline} has been created.',
+ 'Your app is now able to use more than 1 CPU by running',
+ '{underline}node cluster_app.js{/underline}, which in turn',
+ 'spawns multiple instances of {underline}app.js{/underline}'
+ ].join(' '));
success.focus();
screen.render();
break;
diff --git a/test/app.js b/test/app.js
index 18bf953a71..324fa4291f 100644
--- a/test/app.js
+++ b/test/app.js
@@ -47,4 +47,4 @@ describe('GET /random-url', function() {
.get('/reset')
.expect(404, done);
});
-});
\ No newline at end of file
+});
diff --git a/test/models.js b/test/models.js
index 7fc38b03e0..b4666b5e6e 100644
--- a/test/models.js
+++ b/test/models.js
@@ -11,7 +11,7 @@ describe('User Model', function() {
user.save(function(err) {
if (err) return done(err);
done();
- })
+ });
});
it('should not create a user with the unique email', function(done) {