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

Conflicts:
	server/boot/randomAPIs.js
	server/views/coursewares/showBonfire.jade
	server/views/partials/navbar.jade
This commit is contained in:
Quincy Larson
2015-06-21 15:53:16 -07:00
12 changed files with 365 additions and 370 deletions

View File

@ -70,6 +70,7 @@
"node-slack": "0.0.7", "node-slack": "0.0.7",
"node-uuid": "^1.4.3", "node-uuid": "^1.4.3",
"nodemailer": "~1.3.0", "nodemailer": "~1.3.0",
"object.assign": "^3.0.0",
"passport-facebook": "^2.0.0", "passport-facebook": "^2.0.0",
"passport-google-oauth2": "^0.1.6", "passport-google-oauth2": "^0.1.6",
"passport-linkedin-oauth2": "^1.2.1", "passport-linkedin-oauth2": "^1.2.1",

View File

@ -1,3 +1,7 @@
$(document).ready(function() {
$('#reset-button').on('click', resetEditor);
});
var widgets = []; var widgets = [];
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), { var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("codeEditor"), {
lineNumbers: true, lineNumbers: true,
@ -41,73 +45,90 @@ editor.setOption("extraKeys", {
/* /*
Local Storage Update System By Andrew Cay(Resto) Local Storage Update System By Andrew Cay(Resto)
localBonfire: singleton object that contains properties and methods related to codeStorage: singleton object that contains properties and methods related to
dealing with the localStorage system. dealing with the localStorage system.
The keys work off of the variable challenge_name to make unique identifiers per bonfire The keys work off of the variable challenge_name to make unique identifiers per bonfire
Two extra functionalities: Two extra functionalities:
Added anonymous version checking system incase of future updates to the system Added anonymous version checking system incase of future updates to the system
Added keyup listener to editor(myCodeMirror) so the last update has been saved to storage Added keyup listener to editor(myCodeMirror) so the last update has been saved to storage
*/ */
var localBonfire = { var codeStorage = {
version: 0.01, version: 0.01,
keyVersion:"saveVersion", keyVersion:"saveVersion",
keyStamp: challenge_Name + 'Stamp', keyValue: null,//where the value of the editor is saved
keyValue: challenge_Name + 'Val', updateWait: 2000,// 2 seconds
stampExpireTime: (1000 *60) *60 *24, updateTimeoutId: null,
updateWait: 1500,// 1.5 seconds eventArray: []//for firing saves
updateTimeoutId: null
}; };
localBonfire.getEditorValue = function(){ // Returns true if the editor code was saved since last key press (use this if you want to make a "saved" notification somewhere")
return localStorage.getItem(localBonfire.keyValue); codeStorage.hasSaved = function(){
return ( updateTimeoutId === null );
}; };
localBonfire.getStampTime = function(){ codeStorage.onSave = function(func){
//localstorage always saves as strings. codeStorage.eventArray.push(func);
return Number.parseInt( localStorage.getItem(localBonfire.keyStamp) );
}; };
localBonfire.isAlive = function(){// returns true if IDE was edited within expire time codeStorage.setSaveKey = function(key){
return ( Date.now() - localBonfire.getStampTime() < localBonfire.stampExpireTime ); codeStorage.keyValue = key + 'Val';
}; };
localBonfire.updateStorage = function(){ codeStorage.getEditorValue = function(){
if(typeof(Storage) !== undefined) { return ('' + localStorage.getItem(codeStorage.keyValue));
var stamp = Date.now(),
value = editor.getValue();
localStorage.setItem(localBonfire.keyValue, value);
localStorage.setItem(localBonfire.keyStamp, stamp);
} else {
if( debugging ){
console.log('no web storage');
}
}
localBonfire.updateTimeoutId = null;
}; };
// ANONYMOUS 1 TIME UPDATE VERSION
codeStorage.isAlive = function() {
var val = this.getEditorValue()
return val !== 'null' &&
val !== 'undefined' &&
(val && val.length > 0);
}
codeStorage.updateStorage = function(){
if(typeof(Storage) !== undefined) {
var value = editor.getValue();
localStorage.setItem(codeStorage.keyValue, value);
} else {
var debugging = false;
if( debugging ){
console.log('no web storage');
}
}
codeStorage.updateTimeoutId = null;
codeStorage.eventArray.forEach(function(func){
func();
});
};
//Update Version
(function(){ (function(){
var savedVersion = localStorage.getItem('saveVersion'); var savedVersion = localStorage.getItem('saveVersion');
if( savedVersion === null ){ if( savedVersion === null ){
localStorage.setItem(localBonfire.keyVersion, localBonfire.version);//just write current version localStorage.setItem(codeStorage.keyVersion, codeStorage.version);//just write current version
}else{ }else{
//do checking if not current version if( savedVersion !== codeStorage.version ){
if( savedVersion !== localBonfire.version ){ //Update version
//update version }
} }
}
})(); })();
editor.on('keyup', function(codMir, event){
window.clearTimeout(localBonfire.updateTimeoutId);
localBonfire.updateTimeoutId = window.setTimeout(localBonfire.updateStorage, localBonfire.updateWait); ///Set everything up one page
/// Update local save when editor has changed
codeStorage.setSaveKey(challenge_Name);
editor.on('keyup', function(){
window.clearTimeout(codeStorage.updateTimeoutId);
codeStorage.updateTimeoutId = window.setTimeout(codeStorage.updateStorage, codeStorage.updateWait);
}); });
var attempts = 0; var attempts = 0;
if (attempts) { if (attempts) {
attempts = 0; attempts = 0;
} }
var resetEditor = function() { var resetEditor = function resetEditor() {
editor.setValue(allSeeds); editor.setValue(allSeeds);
localBonfire.updateStorage(); codeStorage.updateStorage();
}; };
var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), { var codeOutput = CodeMirror.fromTextArea(document.getElementById("codeOutput"), {
@ -141,11 +162,11 @@ var tests = tests || [];
var allSeeds = ''; var allSeeds = '';
(function() { (function() {
challengeSeed.forEach(function(elem) { challengeSeed.forEach(function(elem) {
allSeeds += elem + '\n'; allSeeds += elem + '\n';
}); });
})(); })();
editorValue = (localBonfire.isAlive())? localBonfire.getEditorValue() : allSeeds; editorValue = (codeStorage.isAlive())? codeStorage.getEditorValue() : allSeeds;
myCodeMirror.setValue(editorValue); myCodeMirror.setValue(editorValue);

View File

@ -271,6 +271,7 @@ $(document).ready(function() {
$('#story-submit').on('click', storySubmitButtonHandler); $('#story-submit').on('click', storySubmitButtonHandler);
var commentSubmitButtonHandler = function commentSubmitButtonHandler() { var commentSubmitButtonHandler = function commentSubmitButtonHandler() {
$('#comment-button').unbind('click'); $('#comment-button').unbind('click');
var data = $('#comment-box').val(); var data = $('#comment-box').val();

View File

@ -31,8 +31,15 @@
*/ */
var R = require('ramda'), var R = require('ramda'),
Rx = require('rx'),
assign = require('object.assign'),
debug = require('debug')('freecc:challenges'),
utils = require('../utils'), utils = require('../utils'),
// this would be so much cleaner with destructering...
saveUser = require('../utils/rx').saveUser, saveUser = require('../utils/rx').saveUser,
observableQueryFromModel = require('../utils/rx').observableQueryFromModel,
userMigration = require('../utils/middleware').userMigration, userMigration = require('../utils/middleware').userMigration,
ifNoUserRedirectTo = require('../utils/middleware').ifNoUserRedirectTo; ifNoUserRedirectTo = require('../utils/middleware').ifNoUserRedirectTo;
@ -42,6 +49,28 @@ var challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames();
var getMDNLinks = utils.getMDNLinks; var getMDNLinks = utils.getMDNLinks;
var challangesRegex = /^(bonfire|waypoint|zipline|basejump)/i;
function dasherize(name) {
return ('' + name)
.toLowerCase()
.replace(/\s/g, '-')
.replace(/[^a-z0-9\-\.]/gi, '');
}
function unDasherize(name) {
return ('' + name).replace(/\-/g, ' ');
}
function updateUserProgress(user, challengeId, completedChallenge) {
var index = user.uncompletedChallenges.indexOf(challengeId);
if (index > -1) {
user.progressTimestamps.push(Date.now());
user.uncompletedChallenges.splice(index, 1);
}
user.completedChallenges.push(completedChallenge);
return user;
}
module.exports = function(app) { module.exports = function(app) {
var router = app.loopback.Router(); var router = app.loopback.Router();
var Challenge = app.models.Challenge; var Challenge = app.models.Challenge;
@ -56,7 +85,7 @@ module.exports = function(app) {
router.get('/map', challengeMap); router.get('/map', challengeMap);
router.get( router.get(
'/challenges/next-challenge', '/challenges/next-challenge',
ifNoUserRedirectTo('../challenges/learn-how-free-code-camp-works'), ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works'),
returnNextChallenge returnNextChallenge
); );
@ -64,7 +93,7 @@ module.exports = function(app) {
router.get( router.get(
'/challenges/', '/challenges/',
ifNoUserRedirectTo('../challenges/learn-how-free-code-camp-works'), ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works'),
returnCurrentChallenge returnCurrentChallenge
); );
@ -114,7 +143,7 @@ module.exports = function(app) {
function() {}, function() {},
next, next,
function() { function() {
res.redirect('../challenges/' + nextChallengeName); res.redirect('/challenges/' + nextChallengeName);
} }
); );
} }
@ -130,6 +159,7 @@ module.exports = function(app) {
return elem; return elem;
} }
}); });
if (!req.user.currentChallenge) { if (!req.user.currentChallenge) {
req.user.currentChallenge = {}; req.user.currentChallenge = {};
req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0]; req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0];
@ -146,40 +176,53 @@ module.exports = function(app) {
function() {}, function() {},
next, next,
function() { function() {
res.redirect('../challenges/' + nameString); res.redirect('/challenges/' + nameString);
} }
); );
} }
function returnIndividualChallenge(req, res, next) { function returnIndividualChallenge(req, res, next) {
var dashedName = req.params.challengeName; var origChallengeName = req.params.challengeName;
var unDashedName = unDasherize(origChallengeName);
var challengeName = challangesRegex.test(unDashedName) ?
// remove first word if matches
unDashedName.split(' ').slice(1).join(' ') :
unDashedName;
debug('looking for ', challengeName);
Challenge.findOne( Challenge.findOne(
{ where: { dashedName: dashedName }}, { where: { name: { like: challengeName, options: 'i' } } },
function(err, challenge) { function(err, challenge) {
if (err) { return next(err); } if (err) { return next(err); }
// Handle not found // Handle not found
if (!challenge) { if (!challenge) {
debug('did not find challenge for ' + origChallengeName);
req.flash('errors', { req.flash('errors', {
msg: msg:
'404: We couldn\'t find a challenge with the name `' + '404: We couldn\'t find a challenge with the name `' +
dashedName + origChallengeName +
'` Please double check the name.' '` Please double check the name.'
}); });
return res.redirect('/challenges'); return res.redirect('/challenges');
} }
// Redirect to full name if the user only entered a partial // Redirect to full name if the user only entered a partial
if (dasherize(challenge.name) !== origChallengeName) {
debug('redirecting to fullname');
return res.redirect('/challenges/' + dasherize(challenge.name));
}
if (req.user) { if (req.user) {
req.user.currentChallenge = { req.user.currentChallenge = {
challengeId: challenge.id, challengeId: challenge.id,
challengeName: challenge.name, challengeName: challenge.name,
dashedName: challenge.dashedName, dashedName: challenge.dashedName,
challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds). challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds)
map(function (key) { .map(function (key) {
return challengeMapWithIds[key] return challengeMapWithIds[key]
.filter(function (elem) { .filter(function (elem) {
return String(elem) === challenge.id; return elem === ('' + challenge.id);
}) })
.map(function () { .map(function () {
return key; return key;
@ -191,7 +234,7 @@ module.exports = function(app) {
var commonLocals = { var commonLocals = {
title: challenge.name, title: challenge.name,
dashedName: dashedName, dashedName: origChallengeName,
name: challenge.name, name: challenge.name,
details: challenge.description.slice(1), details: challenge.description.slice(1),
tests: challenge.tests, tests: challenge.tests,
@ -235,143 +278,123 @@ module.exports = function(app) {
} }
function completedBonfire(req, res, next) { function completedBonfire(req, res, next) {
var isCompletedWith = req.body.challengeInfo.completedWith || ''; debug('compltedBonfire');
var isCompletedDate = Math.round(+new Date()); var completedWith = req.body.challengeInfo.completedWith || false;
var challengeId = req.body.challengeInfo.challengeId; var challengeId = req.body.challengeInfo.challengeId;
var isSolution = req.body.challengeInfo.solution;
var challengeName = req.body.challengeInfo.challengeName;
if (isCompletedWith) { var challengeData = {
User.find({ id: challengeId,
where: { 'profile.username': isCompletedWith.toLowerCase() }, name: req.body.challengeInfo.challengeName,
limit: 1 completedDate: Math.round(+new Date()),
}, function (err, pairedWith) { solution: req.body.challengeInfo.solution,
if (err) { return next(err); } challengeType: 5
};
var index = req.user.uncompletedChallenges.indexOf(challengeId); observableQueryFromModel(
if (index > -1) { User,
req.user.progressTimestamps.push(Date.now() || 0); 'findOne',
req.user.uncompletedChallenges.splice(index, 1); { where: { username: ('' + completedWith).toLowerCase() } }
} )
pairedWith = pairedWith.pop(); .doOnNext(function(pairedWith) {
debug('paired with ', pairedWith);
if (pairedWith) { if (pairedWith) {
updateUserProgress(
index = pairedWith.uncompletedChallenges.indexOf(challengeId); pairedWith,
if (index > -1) { challengeId,
pairedWith.progressTimestamps.push(Date.now() || 0); assign({ completedWith: req.user.id }, challengeData)
pairedWith.uncompletedChallenges.splice(index, 1); );
}
pairedWith.completedChallenges.push({
id: challengeId,
name: challengeName,
completedWith: req.user.id,
completedDate: isCompletedDate,
solution: isSolution,
challengeType: 5
});
req.user.completedChallenges.push({
id: challengeId,
name: challengeName,
completedWith: pairedWith.id,
completedDate: isCompletedDate,
solution: isSolution,
challengeType: 5
});
} }
// User said they paired, but pair wasn't found })
req.user.completedChallenges.push({ .withLatestFrom(
id: challengeId, Rx.Observable.just(req.user),
name: challengeName, function(pairedWith, user) {
completedWith: null, debug('yo');
completedDate: isCompletedDate, return {
solution: isSolution, user: user,
challengeType: 5 pairedWith: pairedWith
}); };
}
req.user.save(function (err, user) { )
if (err) { return next(err); } // side effects should always be done in do's and taps
.doOnNext(function(dats) {
if (pairedWith) { updateUserProgress(
pairedWith.save(function (err, paired) { dats.user,
if (err) { challengeId,
return next(err); dats.pairedWith ?
} // paired programmer found and adding to data
if (user && paired) { assign({ completedWith: dats.pairedWith.id }, challengeData) :
return res.send(true); // user said they paired, but pair wasn't found
} challengeData
}); );
} else if (user) { })
res.send(true); // not iterate users
.flatMap(function(dats) {
debug('flatmap');
return Rx.Observable.from([dats.user, dats.pairedWith]);
})
// save user
.flatMap(function(user) {
// save user will do nothing if user is falsey
return saveUser(user);
})
.subscribe(
function(user) {
debug('onNext');
if (user) {
debug('user %s saved', user.username);
} }
}); },
}); next,
} else { function() {
req.user.completedChallenges.push({ debug('completed');
id: challengeId, return res.status(200).send(true);
name: challengeName, }
completedWith: null, );
completedDate: isCompletedDate,
solution: isSolution,
challengeType: 5
});
var index = req.user.uncompletedChallenges.indexOf(challengeId);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedChallenges.splice(index, 1);
}
req.user.save(function (err) {
if (err) { return next(err); }
res.send(true);
});
}
} }
function completedChallenge(req, res, next) { function completedChallenge(req, res, next) {
var isCompletedDate = Math.round(+new Date()); var completedDate = Math.round(+new Date());
var challengeId = req.body.challengeInfo.challengeId; var challengeId = req.body.challengeInfo.challengeId;
req.user.completedChallenges.push({ updateUserProgress(
id: challengeId, req.user,
completedDate: isCompletedDate, challengeId,
name: req.body.challengeInfo.challengeName, {
solution: null, id: challengeId,
githubLink: null, completedDate: completedDate,
verified: true name: req.body.challengeInfo.challengeName,
}); solution: null,
var index = req.user.uncompletedChallenges.indexOf(challengeId); githubLink: null,
verified: true
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedChallenges.splice(index, 1);
}
req.user.save(function (err, user) {
if (err) {
return next(err);
} }
if (user) { );
res.sendStatus(200);
} saveUser(req.user)
}); .subscribe(
function() { },
next,
function() {
res.sendStatus(200);
}
);
} }
function completedZiplineOrBasejump(req, res, next) { function completedZiplineOrBasejump(req, res, next) {
var isCompletedWith = req.body.challengeInfo.completedWith || false; var completedWith = req.body.challengeInfo.completedWith || false;
var isCompletedDate = Math.round(+new Date()); var completedDate = Math.round(+new Date());
var challengeId = req.body.challengeInfo.challengeId; var challengeId = req.body.challengeInfo.challengeId;
var solutionLink = req.body.challengeInfo.publicURL; var solutionLink = req.body.challengeInfo.publicURL;
var githubLink = req.body.challengeInfo.challengeType === '4'
? req.body.challengeInfo.githubURL : true; var githubLink = req.body.challengeInfo.challengeType === '4' ?
req.body.challengeInfo.githubURL :
true;
var challengeType = req.body.challengeInfo.challengeType === '4' ? var challengeType = req.body.challengeInfo.challengeType === '4' ?
4 : 3; 4 :
3;
if (!solutionLink || !githubLink) { if (!solutionLink || !githubLink) {
req.flash('errors', { req.flash('errors', {
msg: 'You haven\'t supplied the necessary URLs for us to inspect ' + msg: 'You haven\'t supplied the necessary URLs for us to inspect ' +
@ -380,93 +403,64 @@ module.exports = function(app) {
return res.sendStatus(403); return res.sendStatus(403);
} }
if (isCompletedWith) { var challengeData = {
User.find({ id: challengeId,
where: { 'profile.username': isCompletedWith.toLowerCase() }, name: req.body.challengeInfo.challengeName,
limit: 1 completedDate: completedDate,
}, function (err, pairedWithFromMongo) { solution: solutionLink,
if (err) { return next(err); } githubLink: githubLink,
var index = req.user.uncompletedChallenges.indexOf(challengeId); challengeType: challengeType,
if (index > -1) { verified: false
req.user.progressTimestamps.push(Date.now() || 0); };
req.user.uncompletedChallenges.splice(index, 1);
observableQueryFromModel(
User,
'findOne',
{ where: { username: completedWith.toLowerCase() } }
)
.doOnNext(function(pairedWith) {
if (pairedWith) {
updateUserProgress(
pairedWith,
challengeId,
assign({ completedWith: req.user.id }, challengeData)
);
} }
var pairedWith = pairedWithFromMongo.pop(); })
.withLatestFrom(Rx.Observable.just(req.user), function(user, pairedWith) {
req.user.completedChallenges.push({ return {
id: challengeId, user: user,
name: req.body.challengeInfo.challengeName, pairedWith: pairedWith
completedWith: pairedWith.id, };
completedDate: isCompletedDate, })
solution: solutionLink, .doOnNext(function(dats) {
githubLink: githubLink, updateUserProgress(
challengeType: challengeType, dats.user,
verified: false challengeId,
}); dats.pairedWith ?
assign({ completedWith: dats.pairedWith.id }, challengeData) :
req.user.save(function (err, user) { challengeData
if (err) { return next(err); } );
})
if (req.user.id.toString() === pairedWith.id.toString()) { .flatMap(function(dats) {
return res.sendStatus(200); return Rx.Observable.from([dats.user, dats.pairedWith]);
})
// save users
.flatMap(function(user) {
// save user will do nothing if user is falsey
return saveUser(user);
})
.subscribe(
function(user) {
if (user) {
debug('user %s saved', user.username);
} }
index = pairedWith.uncompletedChallenges.indexOf(challengeId); },
if (index > -1) { next,
pairedWith.progressTimestamps.push(Date.now() || 0); function() {
pairedWith.uncompletedChallenges.splice(index, 1); return res.status(200).send(true);
}
pairedWith.completedChallenges.push({
id: challengeId,
name: req.body.challengeInfo.coursewareName,
completedWith: req.user.id,
completedDate: isCompletedDate,
solution: solutionLink,
githubLink: githubLink,
challengeType: challengeType,
verified: false
});
pairedWith.save(function (err, paired) {
if (err) {
return next(err);
}
if (user && paired) {
return res.sendStatus(200);
}
});
});
});
} else {
req.user.completedChallenges.push({
id: challengeId,
name: req.body.challengeInfo.challengeName,
completedWith: null,
completedDate: isCompletedDate,
solution: solutionLink,
githubLink: githubLink,
challengeType: challengeType,
verified: false
});
var index = req.user.uncompletedChallenges.indexOf(challengeId);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedChallenges.splice(index, 1);
}
req.user.save(function (err, user) {
if (err) {
return next(err);
} }
// NOTE(berks): under certain conditions this will not close );
// the response.
if (user) {
return res.sendStatus(200);
}
});
}
} }
function challengeMap(req, res, next) { function challengeMap(req, res, next) {

View File

@ -13,7 +13,7 @@ module.exports = function(app) {
var router = app.loopback.Router(); var router = app.loopback.Router();
var User = app.models.User; var User = app.models.User;
var Challenge = app.models.Challenge; var Challenge = app.models.Challenge;
var Story = app.models.Store; var Story = app.models.Story;
var FieldGuide = app.models.FieldGuide; var FieldGuide = app.models.FieldGuide;
var Nonprofit = app.models.Nonprofit; var Nonprofit = app.models.Nonprofit;
@ -76,15 +76,15 @@ module.exports = function(app) {
users: function(callback) { users: function(callback) {
User.find( User.find(
{ {
where: { 'profile.username': { nlike: '' } }, where: { username: { nlike: '' } },
fields: { 'profile.username': true } fields: { username: true }
}, },
function(err, users) { function(err, users) {
if (err) { if (err) {
debug('User err: ', err); debug('User err: ', err);
callback(err); callback(err);
} else { } else {
Rx.Observable.from(users) Rx.Observable.from(users, null, null, Rx.Scheduler.default)
.map(function(user) { .map(function(user) {
return user.username; return user.username;
}) })
@ -107,7 +107,7 @@ module.exports = function(app) {
debug('Challenge err: ', err); debug('Challenge err: ', err);
callback(err); callback(err);
} else { } else {
Rx.Observable.from(challenges) Rx.Observable.from(challenges, null, null, Rx.Scheduler.default)
.map(function(challenge) { .map(function(challenge) {
return challenge.name; return challenge.name;
}) })
@ -127,7 +127,7 @@ module.exports = function(app) {
debug('Story err: ', err); debug('Story err: ', err);
callback(err); callback(err);
} else { } else {
Rx.Observable.from(stories) Rx.Observable.from(stories, null, null, Rx.Scheduler.default)
.map(function(story) { .map(function(story) {
return story.link; return story.link;
}) })
@ -148,7 +148,7 @@ module.exports = function(app) {
debug('User err: ', err); debug('User err: ', err);
callback(err); callback(err);
} else { } else {
Rx.Observable.from(nonprofits) Rx.Observable.from(nonprofits, null, null, Rx.Scheduler.default)
.map(function(nonprofit) { .map(function(nonprofit) {
return nonprofit.name; return nonprofit.name;
}) })
@ -168,7 +168,12 @@ module.exports = function(app) {
debug('User err: ', err); debug('User err: ', err);
callback(err); callback(err);
} else { } else {
Rx.Observable.from(fieldGuides) Rx.Observable.from(
fieldGuides,
null,
null,
Rx.Scheduler.default
)
.map(function(fieldGuide) { .map(function(fieldGuide) {
return fieldGuide.name; return fieldGuide.name;
}) })
@ -184,7 +189,7 @@ module.exports = function(app) {
if (err) { if (err) {
return next(err); return next(err);
} }
setTimeout(function() { process.nextTick(function() {
res.header('Content-Type', 'application/xml'); res.header('Content-Type', 'application/xml');
res.render('resources/sitemap', { res.render('resources/sitemap', {
appUrl: appUrl, appUrl: appUrl,
@ -195,11 +200,15 @@ module.exports = function(app) {
nonprofits: results.nonprofits, nonprofits: results.nonprofits,
fieldGuides: results.fieldGuides fieldGuides: results.fieldGuides
}); });
}, 0); });
} }
); );
} }
function chat(req, res) {
res.redirect('//gitter.im/FreeCodeCamp/FreeCodeCamp');
}
function bootcampCalculator(req, res) { function bootcampCalculator(req, res) {
res.render('resources/calculator', { res.render('resources/calculator', {
title: 'Coding Bootcamp Cost Calculator', title: 'Coding Bootcamp Cost Calculator',
@ -260,7 +269,7 @@ module.exports = function(app) {
} }
function unsubscribe(req, res, next) { function unsubscribe(req, res, next) {
User.findOne({ email: req.params.email }, function(err, user) { User.findOne({ where: { email: req.params.email } }, function(err, user) {
if (user) { if (user) {
if (err) { if (err) {
return next(err); return next(err);

View File

@ -4,7 +4,7 @@ var _ = require('lodash'),
crypto = require('crypto'), crypto = require('crypto'),
nodemailer = require('nodemailer'), nodemailer = require('nodemailer'),
moment = require('moment'), moment = require('moment'),
//debug = require('debug')('freecc:cntr:userController'), // debug = require('debug')('freecc:cntr:userController'),
secrets = require('../../config/secrets'); secrets = require('../../config/secrets');

View File

@ -17,4 +17,4 @@ module.exports = {
clientID: process.env.GITHUB_ID, clientID: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET clientSecret: process.env.GITHUB_SECRET
} }
}; };

View File

@ -3,8 +3,8 @@ var secrets = require('../config/secrets');
module.exports = { module.exports = {
db: { db: {
connector: 'mongodb', connector: 'mongodb',
connectionTimeout: 15000, connectionTimeout: 5000,
url: process.env.MONGOHQ_URL url: secrets.db
}, },
mail: { mail: {
connector: 'mail', connector: 'mail',

25
server/utils/rx.js Normal file
View File

@ -0,0 +1,25 @@
var Rx = require('rx');
var debug = require('debug')('freecc:rxUtils');
exports.saveUser = function saveUser(user) {
return new Rx.Observable.create(function(observer) {
if (!user || typeof user.save !== 'function') {
debug('no user or save method');
observer.onNext();
return observer.onCompleted();
}
user.save(function(err, savedUser) {
if (err) {
return observer.onError(err);
}
debug('user saved');
observer.onNext(savedUser);
observer.onCompleted();
});
});
};
exports.observableQueryFromModel =
function observableQueryFromModel(Model, method, query) {
return Rx.Observable.fromNodeCallback(Model[method], Model)(query);
};

View File

@ -47,88 +47,32 @@ block content
.background-svg.img-center .background-svg.img-center
.points-on-top .points-on-top
= "[ " + (progressTimestamps.length) + " ]" = "[ " + (progressTimestamps.length) + " ]"
.row
.col-xs-12
if (website1Title && website1Link && website1Image)
.row
.col-xs-12.col-md-5
img.img-center.img-responsive.portfolio-image(src=website1Image, alt="@#{username}'s #{website1Title}")
.col-xs-12.col-md-7
h3.text-center.wrappable.flat-top= website1Title
a.btn.btn-lg.btn-block.btn-info(href=website1Link, target='_blank')
i.fa.icon-beaker
| Try it out
br
if (website1Title && website1Link && !website1Image)
.col-xs-12.col-md-12
h3.text-center.wrappable.flat-top= website1Title
a.btn.btn-lg.btn-block.btn-info(href=website1Link, target='_blank')
i.fa.icon-beaker
| Try it out
br
if (website2Title && website2Link && website2Image)
.row
.col-xs-12.col-md-5
img.img-responsive.portfolio-image.img-center(src=website2Image, alt="@#{username}'s #{website2Title}")
.col-xs-12.col-md-7
h3.text-center.wrappable.flat-top= website2Title
a.btn.btn-lg.btn-block.btn-info(href=website2Link, target='_blank')
i.fa.icon-beaker
| Try it out
br
if (website2Title && website2Link && !website2Image)
.col-xs-12.col-md-12
h3.text-center.wrappable.flat-top= website2Title
a.btn.btn-lg.btn-block.btn-info(href=website2Link, target='_blank')
i.fa.icon-beaker
| Try it out
br
if (website3Title && website3Link && website3Image)
.row
.col-xs-12.col-md-5
img.img-responsive.portfolio-image.img-center(src=website3Image, alt="@#{username}'s #{website1Title}")
.col-xs-12.col-md-7
h3.text-center.wrappable.flat-top= website3Title
a.btn.btn-lg.btn-block.btn-info(href=website3Link, target='_blank')
i.fa.icon-beaker
| Try it out
if (website3Title && website3Link && !website3Image)
.col-xs-12.col-md-12
h3.text-center.wrappable.flat-top= website3Title
a.btn.btn-lg.btn-block.btn-info(href=website3Link, target='_blank')
i.fa.icon-beaker
| Try it out
.spacer .spacer
.hidden-xs.hidden-sm.col-md-12 .hidden-xs.hidden-sm.col-md-12
#cal-heatmap.d3-centered #cal-heatmap.d3-centered
script. script.
$(document).ready(function () { $(document).ready(function () {
setTimeout(function () { var cal = new CalHeatMap();
var cal = new CalHeatMap(); var calendar = !{JSON.stringify(calender)};
var calendar = !{JSON.stringify(calender)}; cal.init({
cal.init({ itemSelector: "#cal-heatmap",
itemSelector: "#cal-heatmap", domain: "month",
domain: "month", subDomain: "x_day",
subDomain: "x_day", domainGutter: 10,
domainGutter: 10, data: calendar,
data: calendar, cellSize: 15,
cellSize: 15, align: 'center',
align: 'center', cellRadius: 3,
cellRadius: 3, cellPadding: 2,
cellPadding: 2, tooltip: true,
tooltip: true, range: 6,
range: 6, start: new Date().setDate(new Date().getDate() - 150),
start: new Date().setDate(new Date().getDate() - 150), legendColors: ["#cccccc", "#215f1e"],
legendColors: ["#cccccc", "#215f1e"], legend: [1, 2, 3],
legend: [1, 2, 3], label: {
label: { position: "top"
position: "top" }
} });
});
}, 300);
}); });
.row .row
.hidden-xs.col-sm-12.text-center .hidden-xs.col-sm-12.text-center

View File

@ -84,20 +84,20 @@ block content
label.negative-10.btn.btn-primary.btn-block#submitButton label.negative-10.btn.btn-primary.btn-block#submitButton
i.fa.fa-play i.fa.fa-play
| &nbsp; Run code (ctrl + enter) | &nbsp; Run code (ctrl + enter)
.button-spacer .button-spacer
.btn-group.input-group.btn-group-justified .btn-group.input-group.btn-group-justified
label.btn.btn-success#resetButton label.btn.btn-success#trigger-reset-modal
i.fa.fa-refresh i.fa.fa-refresh
| &nbsp; Reset | &nbsp; Reset
label.btn.btn-success#trigger-help-modal label.btn.btn-success#trigger-help-modal
i.fa.fa-medkit i.fa.fa-medkit
| &nbsp; Help | &nbsp; Help
label.btn.btn-success#trigger-pair-modal label.btn.btn-success#trigger-pair-modal
i.fa.fa-user-plus i.fa.fa-user-plus
| &nbsp; Pair | &nbsp; Pair
label.btn.btn-success#trigger-issue-modal label.btn.btn-success#trigger-issue-modal
i.fa.fa-bug i.fa.fa-bug
| &nbsp; Bug | &nbsp; Bug
.button-spacer .button-spacer
form.code form.code
.form-group.codeMirrorView .form-group.codeMirrorView

View File

@ -14,7 +14,7 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height
li li
a(href='/map') Map a(href='/map') Map
li li
a(href='gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') Chat a(href='//gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') Chat
li li
a(href='/stories') News a(href='/stories') News
li li