Merge branch 'staging' of github.com:FreeCodeCamp/freecodecamp into staging

This commit is contained in:
Quincy Larson
2015-06-16 06:08:18 -07:00
61 changed files with 220 additions and 17301 deletions

View File

@@ -12,6 +12,7 @@ module.exports = function(app) {
var User = app.models.User;
var UserIdentity = app.models.UserIdentity;
var UserCredential = app.models.UserCredential;
var Email = app.models.Email;
User.observe('before delete', function(ctx, next) {
debug('removing user', ctx.where);
var id = ctx.where && ctx.where.id ? ctx.where.id : null;
@@ -41,4 +42,51 @@ module.exports = function(app) {
}
);
});
// set email varified false on user email signup
// should not be set with oauth signin methods
User.beforeRemote('create', function(ctx, user, next) {
var body = ctx.req.body;
if (body) {
body.emailVerified = false;
}
next();
});
// send welcome email to new camper
User.afterRemote('create', function(ctx, user, next) {
debug('user created, sending email');
if (!user.email) { return next(); }
var mailOptions = {
type: 'email',
to: user.email,
from: 'Team@freecodecamp.com',
subject: 'Welcome to Free Code Camp!',
redirect: '/',
text: [
'Greetings from San Francisco!\n\n',
'Thank you for joining our community.\n',
'Feel free to email us at this address if you have ',
'any questions about Free Code Camp.\n',
'And if you have a moment, check out our blog: ',
'blog.freecodecamp.com.\n',
'Good luck with the challenges!\n\n',
'- the Free Code Camp Volunteer Team'
].join('')
};
debug('sending welcome email');
Email.send(mailOptions, function(err) {
if (err) { return next(err); }
ctx.req.logIn(user, function(err) {
if (err) { return next(err); }
ctx.req.flash('success', {
msg: [ 'thanks for joining freecodecamp!' ]
});
ctx.res.redirect('/');
});
});
});
};

View File

@@ -7,15 +7,15 @@
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -31,12 +31,13 @@
*/
var R = require('ramda'),
utils = require('../utils'),
userMigration = require('../utils/middleware').userMigration,
MDNlinks = require('../../seed/bonfireMDNlinks');
utils = require('../utils'),
userMigration = require('../utils/middleware').userMigration,
MDNlinks = require('../../seed/bonfireMDNlinks');
var challengeMapWithNames = utils.getChallengeMapWithNames();
var challengeMapWithIds = utils.getChallengeMapWithIds();
var challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames();
function getMDNlinks(links) {
@@ -79,7 +80,7 @@ module.exports = function(app) {
function returnNextChallenge(req, res, next) {
if (!req.user) {
return res.redirect('../challenges/learn-how-free-code-camp-works');
return res.redirect('../challenges/learn-how-free-code-camp-works');
}
var completed = req.user.completedChallenges.map(function (elem) {
return elem.id;
@@ -108,32 +109,28 @@ module.exports = function(app) {
if (indexOfChallenge + 1
< challengeMapWithIds[challengeBlock].length) {
nextChallengeName =
challengeMapWithNames[challengeBlock][++indexOfChallenge];
challengeMapWithDashedNames[challengeBlock][++indexOfChallenge];
} else if (typeof challengeMapWithIds[++challengeBlock] !== 'undefined') {
nextChallengeName = R.head(challengeMapWithNames[challengeBlock]);
nextChallengeName = R.head(challengeMapWithDashedNames[challengeBlock]);
} else {
req.flash('errors', {
msg: 'It looks like you have finished all of our challenges.' +
' Great job! Now on to helping nonprofits!'
});
nextChallengeName = R.head(challengeMapWithNames[0].challenges);
nextChallengeName = R.head(challengeMapWithDashedNames[0].challenges);
}
var nameString = nextChallengeName.trim()
.toLowerCase()
.replace(/\s/g, '-');
req.user.save(function(err) {
if (err) {
return next(err);
}
return res.redirect('../challenges/' + nameString);
return res.redirect('../challenges/' + nextChallengeName);
});
}
function returnCurrentChallenge(req, res, next) {
if (!req.user) {
return res.redirect('../challenges/learn-how-free-code-camp-works');
return res.redirect('../challenges/learn-how-free-code-camp-works');
}
var completed = req.user.completedChallenges.map(function (elem) {
return elem.id;
@@ -150,16 +147,12 @@ module.exports = function(app) {
req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0];
req.user.currentChallenge.challengeName = challengeMapWithNames['0'][0];
req.user.currentChallenge.challengeBlock = '0';
req.user.save(function(err) {
if (err) {
return next(err);
}
});
req.user.currentChallenge.dashedName =
challengeMapWithDashedNames['0'][0];
}
var nameString = req.user.currentChallenge.challengeName.trim()
.toLowerCase()
.replace(/\s/g, '-')
.replace(/[^a-z0-9\-\/.]/gi, '');
var nameString = req.user.currentChallenge.dashedName;
req.user.save(function(err) {
if (err) {
return next(err);
@@ -171,40 +164,25 @@ module.exports = function(app) {
function returnIndividualChallenge(req, res, next) {
var dashedName = req.params.challengeName;
var challengeName =
(/^(bonfire|waypoint|zipline|basejump)/i).test(dashedName) ?
dashedName
.replace(/\-/g, ' ')
.split(' ')
.slice(1)
.join(' ') :
dashedName.replace(/\-/g, ' ');
Challenge.find(
{ where: { name: new RegExp(challengeName, 'i') } },
function(err, challengeFromMongo) {
Challenge.findOne(
{ where: { dashedName: dashedName }},
function(err, challenge) {
if (err) { return next(err); }
// Handle not found
if (challengeFromMongo.length < 1) {
if (!challenge) {
req.flash('errors', {
msg: '404: We couldn\'t find a challenge with that name. ' +
'Please double check the name.'
});
return res.redirect('/challenges');
}
var challenge = challengeFromMongo.pop();
// Redirect to full name if the user only entered a partial
var dashedNameFull = challenge.name
.toLowerCase()
.replace(/\s/g, '-')
.replace(/[^a-z0-9\-\.]/gi, '');
if (dashedNameFull !== dashedName) {
return res.redirect('../challenges/' + dashedNameFull);
} else if (req.user) {
if (req.user) {
req.user.currentChallenge = {
challengeId: challenge.id,
challengeName: challenge.name,
dashedName: challenge.dashedName,
challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds).
map(function (key) {
return challengeMapWithIds[key]

View File

@@ -29,7 +29,7 @@ module.exports = function(app) {
var completedChallengeList = noDuplicatedChallenges
.map(function(challenge) {
return challenge.id;
return (challenge.id || challenge._id); // backwards compatibility
});
var challengeList = utils.
getChallengeMapForDisplay(completedChallengeList);
@@ -37,7 +37,8 @@ module.exports = function(app) {
Object.keys(challengeList).forEach(function(key) {
challengeList[key].completed = challengeList[key]
.challenges.filter(function(elem) {
return completedChallengeList.indexOf(elem.id) > -1;
return completedChallengeList.indexOf(elem.id || elem._id) > -1;
//backwards compatibility hack
});
});

View File

@@ -1,6 +1,6 @@
var R = require('ramda'),
// Rx = require('rx'),
// debug = require('debug')('freecc:fieldguides'),
debug = require('debug')('freecc:fieldguides'),
utils = require('../utils');
module.exports = function(app) {
@@ -102,6 +102,11 @@ module.exports = function(app) {
function(err, fieldGuide) {
if (err) { return next(err); }
if (!fieldGuide) {
debug('bad juju in field guide %s',
req.user.uncompletedFieldGuides[0]);
return res.redirect('../field-guide/how-do-i-use-this-guide');
}
return res.redirect('../field-guide/' + fieldGuide.dashedName);
});
}

View File

@@ -4,7 +4,7 @@ var _ = require('lodash'),
crypto = require('crypto'),
nodemailer = require('nodemailer'),
moment = require('moment'),
debug = require('debug')('freecc:cntr:userController'),
//debug = require('debug')('freecc:cntr:userController'),
secrets = require('../../config/secrets');
@@ -28,6 +28,7 @@ module.exports = function(app) {
router.post('/reset/:token', postReset);
router.get('/email-signup', getEmailSignup);
router.get('/email-signin', getEmailSignin);
router.get('/account/api', getAccountAngular);
router.post('/account/profile', postUpdateProfile);
router.post('/account/password', postUpdatePassword);
@@ -97,6 +98,9 @@ module.exports = function(app) {
*/
function getAccount (req, res) {
if (!req.user) {
return res.redirect('/');
}
res.render('account/account', {
title: 'Manage your Free Code Camp Account'
});
@@ -108,7 +112,7 @@ module.exports = function(app) {
function getAccountAngular (req, res) {
res.json({
user: req.user
user: req.user || {}
});
}
@@ -119,15 +123,12 @@ module.exports = function(app) {
*/
function returnUser (req, res, next) {
debug(req.params.username);
User.findOne(
{ where: { 'username': req.params.username.toLowerCase() } },
function(err, user) {
if (err) {
debug('Username err: ', err);
return next(err);
}
debug(user);
if (user) {
user.progressTimestamps =
user.progressTimestamps.sort(function(a, b) {

View File

@@ -1,6 +1,6 @@
{
"restApiRoot": "/api",
"host": "0.0.0.0",
"host": "127.0.0.1",
"port": 3000,
"legacyExplorer": false,
"remoting": {

View File

@@ -1,5 +1,20 @@
var globalConfig = require('../common/config.global');
module.exports = {
restApiRoot: globalConfig.restApi
};
restApiRoot: globalConfig.restApi,
sessionSecret: process.env.SESSION_SECRET,
trello: {
key: process.env.TRELLO_KEY,
secret: process.env.TRELLO_SECRET
},
blogger: {
key: process.env.BLOGGER_KEY
},
github: {
clientID: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET
}
};

View File

@@ -0,0 +1,3 @@
module.exports = {
host: process.env.HOST || 'localhost'
};

View File

@@ -96,6 +96,7 @@ app.use(function(req, res, next) {
var trusted = [
"'self'",
'blob:',
'104.236.218.15',
'*.freecodecamp.com',
'http://www.freecodecamp.com',
'ws://freecodecamp.com/',

View File

@@ -16,7 +16,8 @@ var path = require('path'),
*/
var allFieldGuideIds, allFieldGuideNames, allNonprofitNames,
challengeMap, challengeMapForDisplay, challengeMapWithIds,
challengeMapWithNames, allChallengeIds, allChallenges;
challengeMapWithNames, allChallengeIds, allChallenges,
challengeMapWithDashedNames;
/**
* GET /
@@ -98,17 +99,6 @@ module.exports = {
return allChallengeIds;
},
allChallenges: function () {
if (!allChallenges) {
allChallenges = [];
Object.keys(this.getChallengeMapWithNames()).forEach(function (key) {
allChallenges.push(challengeMap[key].challenges);
});
allChallenges = R.flatten(allChallenges);
}
return allChallenges;
},
getChallengeMapWithNames: function () {
if (!challengeMapWithNames) {
challengeMapWithNames = {};
@@ -123,6 +113,20 @@ module.exports = {
return challengeMapWithNames;
},
getChallengeMapWithDashedNames: function() {
if (!challengeMapWithDashedNames) {
challengeMapWithDashedNames = {};
Object.keys(challengeMap).
forEach(function (key) {
var onlyNames = challengeMap[key].challenges.map(function (elem) {
return elem.dashedName;
});
challengeMapWithDashedNames[key] = onlyNames;
});
}
return challengeMapWithDashedNames;
},
randomPhrase: function () {
return resources.phrases[

View File

@@ -83,12 +83,12 @@ block content
.col-xs-12
ol
for challenge in challengeBlock.challenges
if completedChallengeList.indexOf(challenge.id) > -1
if completedChallengeList.indexOf(challenge.id || challenge._id) > -1
.row
.hidden-xs.col-sm-3.col-md-2.text-primary.ion-checkmark-circled.padded-ionic-icon.text-center.large-p.negative-10
.col-xs-12.col-sm-9.col-md-10
li.faded.large-p.negative-10
a(href="/challenges/#{challenge.name}")= challenge.name
a(href="/challenges/#{challenge.dashedName}")= challenge.name
else
.row
@@ -96,7 +96,7 @@ block content
span.negative-10
.col-xs-12.col-sm-9.col-md-10
li.large-p.negative-10
a(href="/challenges/#{challenge.name}")= challenge.name
a(href="/challenges/#{challenge.dashedName}")= challenge.name
#announcementModal.modal(tabindex='-1')
.modal-dialog.animated.fadeInUp.fast-animation