Merge pull request #256 from terakilobyte/ux-improvements

User will now get a point for news/stories interactions
This commit is contained in:
Quincy Larson
2015-03-28 06:12:18 -07:00
8 changed files with 1131 additions and 1025 deletions

3
app.js
View File

@ -398,6 +398,8 @@ app.post(
app.all('/account', passportConf.isAuthenticated);
app.get('/account/api', userController.getAccountAngular);
app.get('/user/streak', userController.getStreak);
/**
* API routes
*/
@ -449,7 +451,6 @@ app.post('/account/password', userController.postUpdatePassword);
app.post('/account/delete', userController.postDeleteAccount);
app.get('/account/unlink/:provider', userController.getOauthUnlink);
app.get('/sitemap.xml', resourcesController.sitemap);
/**
* OAuth sign-in routes.
*/

View File

@ -236,6 +236,7 @@ exports.completedBonfire = function (req, res) {
} else {
var index = req.user.uncompletedBonfires.indexOf(bonfireHash);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedBonfires.splice(index, 1)
}
@ -243,6 +244,7 @@ exports.completedBonfire = function (req, res) {
index = pairedWith.uncompletedBonfires.indexOf(bonfireHash);
if (index > -1) {
pairedWith.progressTimestamps.push(Date.now() || 0);
pairedWith.uncompletedBonfires.splice(index, 1);
@ -285,6 +287,7 @@ exports.completedBonfire = function (req, res) {
var index = req.user.uncompletedBonfires.indexOf(bonfireHash);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedBonfires.splice(index, 1)
}

View File

@ -1,271 +1,263 @@
var _ = require('lodash'),
debug = require('debug')('freecc:cntr:courseware'),
Courseware = require('./../models/Courseware'),
User = require('./../models/User'),
resources = require('./resources'),
R = require('ramda');
debug = require('debug')('freecc:cntr:courseware'),
Courseware = require('./../models/Courseware'),
User = require('./../models/User'),
resources = require('./resources'),
R = require('ramda'),
moment = require('moment');
/**
* Courseware controller
*/
exports.showAllCoursewares = function(req, res) {
var completedCoursewares = req.user.completedCoursewares.map(function(elem) {
return elem._id;
});
var completedCoursewares = req.user.completedCoursewares.map(function(elem) {
return elem._id;
});
var noDuplicatedCoursewares = R.uniq(completedCoursewares);
var data = {};
data.coursewareList = resources.allCoursewareNames();
data.completedList = noDuplicatedCoursewares;
res.send(data);
var noDuplicatedCoursewares = R.uniq(completedCoursewares);
var data = {};
data.coursewareList = resources.allCoursewareNames();
data.completedList = noDuplicatedCoursewares;
res.send(data);
};
exports.returnNextCourseware = function(req, res) {
if (!req.user) {
return res.redirect('../challenges/learn-how-free-code-camp-works');
if (!req.user) {
return res.redirect('../challenges/learn-how-free-code-camp-works');
}
var completed = req.user.completedCoursewares.map(function (elem) {
return elem._id;
});
req.user.uncompletedCoursewares = resources.allCoursewareIds().filter(function (elem) {
if (completed.indexOf(elem) === -1) {
return elem;
}
var completed = req.user.completedCoursewares.map(function (elem) {
return elem._id;
});
});
req.user.save();
req.user.uncompletedCoursewares = resources.allCoursewareIds().filter(function (elem) {
if (completed.indexOf(elem) === -1) {
return elem;
}
});
req.user.save();
var uncompletedCoursewares = req.user.uncompletedCoursewares.shift();
var uncompletedCoursewares = req.user.uncompletedCoursewares.shift();
var displayedCoursewares = Courseware.find({'_id': uncompletedCoursewares});
displayedCoursewares.exec(function(err, courseware) {
if (err) {
next(err);
}
var displayedCoursewares = Courseware.find({'_id': uncompletedCoursewares});
displayedCoursewares.exec(function(err, courseware) {
if (err) {
next(err);
}
courseware = courseware.pop();
if (courseware === undefined) {
req.flash('errors', {
msg: "It looks like you've completed all the courses we have available. Good job!"
});
return res.redirect('../challenges/learn-how-free-code-camp-works');
}
nameString = courseware.name.toLowerCase().replace(/\s/g, '-');
return res.redirect('../challenges/' + nameString);
});
courseware = courseware.pop();
if (courseware === undefined) {
req.flash('errors', {
msg: "It looks like you've completed all the courses we have available. Good job!"
});
return res.redirect('../challenges/learn-how-free-code-camp-works');
}
nameString = courseware.name.toLowerCase().replace(/\s/g, '-');
return res.redirect('../challenges/' + nameString);
});
};
exports.returnIndividualCourseware = function(req, res, next) {
var dashedName = req.params.coursewareName;
var dashedName = req.params.coursewareName;
coursewareName = dashedName.replace(/\-/g, ' ');
coursewareName = dashedName.replace(/\-/g, ' ');
Courseware.find({"name" : new RegExp(coursewareName, 'i')}, function(err, courseware) {
if (err) {
next(err);
}
// Handle not found
if (courseware.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a challenge with that name. Please double check the name."
Courseware.find({"name" : new RegExp(coursewareName, 'i')}, function(err, courseware) {
if (err) {
next(err);
}
// Handle not found
if (courseware.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a challenge with that name. Please double check the name."
});
return res.redirect('/challenges');
}
courseware = courseware.pop();
// Redirect to full name if the user only entered a partial
var dashedNameFull = courseware.name.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull != dashedName) {
return res.redirect('../challenges/' + dashedNameFull);
}
var challengeType = {
0 : function() {
res.render('coursewares/showHTML', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
brief: courseware.description[0],
details: courseware.description.slice(1),
tests: courseware.tests,
challengeSeed: courseware.challengeSeed,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
environment: resources.whichEnvironment()
});
return res.redirect('/challenges');
}
courseware = courseware.pop();
},
// Redirect to full name if the user only entered a partial
var dashedNameFull = courseware.name.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull != dashedName) {
return res.redirect('../challenges/' + dashedNameFull);
}
1 : function() {
res.render('coursewares/showJS', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
brief: courseware.description[0],
details: courseware.description.slice(1),
tests: courseware.tests,
challengeSeed: courseware.challengeSeed,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
var challengeType = {
0 : function() {
res.render('coursewares/showHTML', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
brief: courseware.description[0],
details: courseware.description.slice(1),
tests: courseware.tests,
challengeSeed: courseware.challengeSeed,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
environment: resources.whichEnvironment()
});
},
});
},
1 : function() {
res.render('coursewares/showJS', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
brief: courseware.description[0],
details: courseware.description.slice(1),
tests: courseware.tests,
challengeSeed: courseware.challengeSeed,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
2: function() {
res.render('coursewares/showVideo', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
tests: courseware.tests,
video: courseware.challengeSeed[0],
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
challengeType: 'video'
});
},
});
},
3: function() {
res.render('coursewares/showVideo', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
tests: courseware.tests,
video: courseware.challengeSeed[0],
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
challengeType: 'zipline'
});
},
2: function() {
res.render('coursewares/showVideo', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
tests: courseware.tests,
video: courseware.challengeSeed[0],
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
challengeType: 'video'
});
},
4: function() {
res.render('coursewares/showVideo', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
tests: courseware.tests,
video: courseware.challengeSeed[0],
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
challengeType: 'basejump'
});
}
};
3: function() {
res.render('coursewares/showVideo', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
tests: courseware.tests,
video: courseware.challengeSeed[0],
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
challengeType: 'zipline'
});
},
return challengeType[courseware.challengeType]();
4: function() {
res.render('coursewares/showVideo', {
title: courseware.name,
dashedName: dashedName,
name: courseware.name,
details: courseware.description,
tests: courseware.tests,
video: courseware.challengeSeed[0],
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewareHash: courseware._id,
challengeType: 'basejump'
});
}
};
return challengeType[courseware.challengeType]();
});
});
};
exports.testCourseware = function(req, res) {
var coursewareName = req.body.name,
coursewareTests = req.body.tests,
coursewareDifficulty = req.body.difficulty,
coursewareDescription = req.body.description,
coursewareEntryPoint = req.body.challengeEntryPoint,
coursewareChallengeSeed = req.body.challengeSeed;
coursewareTests = coursewareTests.split('\r\n');
coursewareDescription = coursewareDescription.split('\r\n');
coursewareTests.filter(getRidOfEmpties);
coursewareDescription.filter(getRidOfEmpties);
coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', '');
res.render('courseware/show', {
completedWith: null,
title: coursewareName,
name: coursewareName,
difficulty: +coursewareDifficulty,
brief: coursewareDescription[0],
details: coursewareDescription.slice(1),
tests: coursewareTests,
challengeSeed: coursewareChallengeSeed,
challengeEntryPoint: coursewareEntryPoint,
cc: req.user ? req.user.coursewaresHash : undefined,
progressTimestamps: req.user ? req.user.progressTimestamps : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewares: [],
coursewareHash: "test"
});
var coursewareName = req.body.name,
coursewareTests = req.body.tests,
coursewareDifficulty = req.body.difficulty,
coursewareDescription = req.body.description,
coursewareEntryPoint = req.body.challengeEntryPoint,
coursewareChallengeSeed = req.body.challengeSeed;
coursewareTests = coursewareTests.split('\r\n');
coursewareDescription = coursewareDescription.split('\r\n');
coursewareTests.filter(getRidOfEmpties);
coursewareDescription.filter(getRidOfEmpties);
coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', '');
res.render('courseware/show', {
completedWith: null,
title: coursewareName,
name: coursewareName,
difficulty: +coursewareDifficulty,
brief: coursewareDescription[0],
details: coursewareDescription.slice(1),
tests: coursewareTests,
challengeSeed: coursewareChallengeSeed,
challengeEntryPoint: coursewareEntryPoint,
cc: req.user ? req.user.coursewaresHash : undefined,
progressTimestamps: req.user ? req.user.progressTimestamps : undefined,
verb: resources.randomVerb(),
phrase: resources.randomPhrase(),
compliment: resources.randomCompliment(),
coursewares: [],
coursewareHash: "test"
});
};
function getRidOfEmpties(elem) {
if (elem.length > 0) {
return elem;
}
if (elem.length > 0) {
return elem;
}
};
exports.publicGenerator = function(req, res) {
res.render('courseware/public-generator');
res.render('courseware/public-generator');
};
exports.generateChallenge = function(req, res) {
var coursewareName = req.body.name,
coursewareTests = req.body.tests,
coursewareDifficulty = req.body.difficulty,
coursewareDescription = req.body.description,
coursewareEntryPoint = req.body.challengeEntryPoint,
coursewareChallengeSeed = req.body.challengeSeed;
coursewareTests = coursewareTests.split('\r\n');
coursewareDescription = coursewareDescription.split('\r\n');
coursewareTests.filter(getRidOfEmpties);
coursewareDescription.filter(getRidOfEmpties);
coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', '');
var coursewareName = req.body.name,
coursewareTests = req.body.tests,
coursewareDifficulty = req.body.difficulty,
coursewareDescription = req.body.description,
coursewareEntryPoint = req.body.challengeEntryPoint,
coursewareChallengeSeed = req.body.challengeSeed;
coursewareTests = coursewareTests.split('\r\n');
coursewareDescription = coursewareDescription.split('\r\n');
coursewareTests.filter(getRidOfEmpties);
coursewareDescription.filter(getRidOfEmpties);
coursewareChallengeSeed = coursewareChallengeSeed.replace('\r', '');
var response = {
_id: randomString(),
name: coursewareName,
difficulty: coursewareDifficulty,
description: coursewareDescription,
challengeEntryPoint: coursewareEntryPoint,
challengeSeed: coursewareChallengeSeed,
tests: coursewareTests
};
res.send(response);
var response = {
_id: randomString(),
name: coursewareName,
difficulty: coursewareDifficulty,
description: coursewareDescription,
challengeEntryPoint: coursewareEntryPoint,
challengeSeed: coursewareChallengeSeed,
tests: coursewareTests
};
res.send(response);
};
exports.completedCourseware = function (req, res, next) {
var isCompletedDate = Math.round(+new Date());
var coursewareHash = req.body.coursewareInfo.coursewareHash;
var isCompletedDate = Math.round(+new Date());
var coursewareHash = req.body.coursewareInfo.coursewareHash;
debug('this is the coursewarehash we got', coursewareHash);
debug('this is the coursewarehash we got', coursewareHash);
req.user.completedCoursewares.push({
_id: coursewareHash,
completedDate: isCompletedDate,
name: req.body.coursewareInfo.coursewareName
});
req.user.completedCoursewares.push({
_id: coursewareHash,
completedDate: isCompletedDate,
name: req.body.coursewareInfo.coursewareName
});
var index = req.user.completedCoursewares.indexOf(coursewareHash);
var index = req.user.completedCoursewares.indexOf(coursewareHash);
if (index === -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedCoursewares.splice(index, 1);
}
if (index === -1) {
req.user.save(function (err, user) {
if (err) {
return next(err);
}
if (user) {
res.send(true);
}
});
};
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedCoursewares.splice(index, 1);
}
exports.completedBasejump = function (req, res, next) {
var isCompletedWith = req.body.bonfireInfo.completedWith || undefined;
@ -275,76 +267,94 @@ exports.completedBasejump = function (req, res, next) {
if(!solutionLink) {
// flash error and redirect
}
if (user) {
res.send(true);
}
};
};
if (isCompletedWith) {
var paired = User.find({"profile.username": isCompletedWith.toLowerCase()}).limit(1);
paired.exec(function (err, pairedWith) {
if (err) {
return err;
} else {
var index = req.user.uncompletedBonfires.indexOf(bonfireHash);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedBonfires.splice(index, 1)
}
pairedWith = pairedWith.pop();
exports.completedZiplineOrBasejump = function (req, res, next) {
var isCompletedWith = req.body.bonfireInfo.completedWith || false;
var isCompletedDate = Math.round(+new Date());
var coursewareHash = req.body.coursewareInfo.coursewareHash;
var solutionLink = req.body.coursewareInfo.solutionLink;
if (!solutionLink) {
// flash error and redirect
return next(new Error('No solution provided'));
}
index = pairedWith.uncompletedBonfires.indexOf(bonfireHash);
if (index > -1) {
pairedWith.progressTimestamps.push(Date.now() || 0);
pairedWith.uncompletedBonfires.splice(index, 1);
}
pairedWith.completedBonfires.push({
_id: bonfireHash,
completedWith: req.user._id,
completedDate: isCompletedDate,
solution: isSolution
});
req.user.completedBonfires.push({
_id: bonfireHash,
completedWith: pairedWith._id,
completedDate: isCompletedDate,
solution: isSolution
});
req.user.save(function (err, user) {
pairedWith.save(function (err, paired) {
if (err) {
throw err;
}
if (user && paired) {
res.send(true);
}
})
});
}
})
} else {
req.user.completedBonfires.push({
_id: bonfireHash,
completedWith: null,
completedDate: isCompletedDate,
solution: isSolution
});
var index = req.user.uncompletedCourse.indexOf(bonfireHash);
if (isCompletedWith) {
var paired = User.find({'profile.username': isCompletedWith.toLowerCase()}).limit(1);
paired.exec(function (err, pairedWith) {
if (err) {
return next(err);
} else {
var index = req.user.uncompletedCoursewares.indexOf(coursewareHash);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedBonfires.splice(index, 1)
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedCoursewares.splice(index, 1);
}
pairedWith = pairedWith.pop();
index = pairedWith.uncompletedCoursewares.indexOf(coursewareHash);
if (index > -1) {
pairedWith.progressTimestamps.push(Date.now() || 0);
pairedWith.uncompletedCoursewares.splice(index, 1);
}
req.user.save(function (err, user) {
if (err) {
throw err;
}
if (user) {
debug('Saving user');
res.send(true)
}
pairedWith.completedCoursewares.push({
_id: coursewareHash,
completedWith: req.user._id,
completedDate: isCompletedDate,
solution: solutionLink
});
req.user.completedCoursewares.push({
_id: coursewareHash,
completedWith: pairedWith._id,
completedDate: isCompletedDate,
solution: solutionLink
});
req.user.save(function (err, user) {
if (err) {
return next(err);
}
pairedWith.save(function (err, paired) {
if (err) {
return next(err);
}
if (user && paired) {
return res.send(true);
}
});
});
}
});
} else {
req.user.completedCoursewares.push({
_id: coursewareHash,
completedWith: null,
completedDate: isCompletedDate,
solution: solutionLink
});
var index = req.user.uncompletedCourse.indexOf(coursewareHash);
if (index > -1) {
req.user.progressTimestamps.push(Date.now() || 0);
req.user.uncompletedCoursewares.splice(index, 1);
}
req.user.save(function (err, user) {
if (err) {
return next(err);
}
if (user) {
debug('Saving user');
return res.send(true);
}
});
}
};

View File

@ -1,20 +1,20 @@
var async = require('async'),
User = require('../models/User'),
Challenge = require('./../models/Challenge'),
Bonfire = require('./../models/Bonfire'),
Story = require('./../models/Story'),
Comment = require('./../models/Comment'),
resources = require('./resources.json'),
steps = resources.steps,
secrets = require('./../config/secrets'),
bonfires = require('../seed_data/bonfires.json'),
coursewares = require('../seed_data/coursewares.json'),
moment = require('moment'),
https = require('https'),
debug = require('debug')('freecc:cntr:resources'),
cheerio = require('cheerio'),
request = require('request'),
R = require('ramda');
User = require('../models/User'),
Challenge = require('./../models/Challenge'),
Bonfire = require('./../models/Bonfire'),
Story = require('./../models/Story'),
Comment = require('./../models/Comment'),
resources = require('./resources.json'),
steps = resources.steps,
secrets = require('./../config/secrets'),
bonfires = require('../seed_data/bonfires.json'),
coursewares = require('../seed_data/coursewares.json'),
moment = require('moment'),
https = require('https'),
debug = require('debug')('freecc:cntr:resources'),
cheerio = require('cheerio'),
request = require('request'),
R = require('ramda');
/**
* GET /
@ -26,370 +26,381 @@ Array.zip = function(left, right, combinerFunction) {
results = [];
for (counter = 0; counter < Math.min(left.length, right.length); counter++) {
results.push(combinerFunction(left[counter],right[counter]));
results.push(combinerFunction(left[counter], right[counter]));
}
return results;
};
module.exports = {
privacy: function privacy(req, res) {
res.render('resources/privacy', {
title: 'Privacy'
});
},
privacy: function privacy(req, res) {
res.render('resources/privacy', {
title: 'Privacy'
});
},
sitemap: function sitemap(req, res, next) {
var appUrl = 'http://www.freecodecamp.com';
var now = moment(new Date()).format('YYYY-MM-DD');
sitemap: function sitemap(req, res, next) {
var appUrl = 'http://www.freecodecamp.com';
var now = moment(new Date()).format('YYYY-MM-DD');
User.find({'profile.username': {'$ne': '' }}, function(err, users) {
User.find({'profile.username': {'$ne': '' }}, function(err, users) {
if (err) {
debug('User err: ', err);
return next(err);
}
Challenge.find({}, function (err, challenges) {
if (err) {
debug('User err: ', err);
return next(err);
}
Bonfire.find({}, function (err, bonfires) {
if (err) {
debug('User err: ', err);
return next(err);
}
Story.find({}, function (err, stories) {
if (err) {
debug('User err: ', err);
return next(err);
debug('User err: ', err);
return next(err);
}
Challenge.find({}, function (err, challenges) {
if (err) {
debug('User err: ', err);
return next(err);
}
Bonfire.find({}, function (err, bonfires) {
if (err) {
debug('User err: ', err);
return next(err);
}
Story.find({}, function (err, stories) {
if (err) {
debug('User err: ', err);
return next(err);
}
res.header('Content-Type', 'application/xml');
res.render('resources/sitemap', {
appUrl: appUrl,
now: now,
users: users,
challenges: challenges,
bonfires: bonfires,
stories: stories
});
});
});
res.header('Content-Type', 'application/xml');
res.render('resources/sitemap', {
appUrl: appUrl,
now: now,
users: users,
challenges: challenges,
bonfires: bonfires,
stories: stories
});
});
});
},
});
});
},
deployAWebsite: function deployAWebsite(req, res) {
res.render('resources/deploy-a-website', {
title: 'Deploy a Dynamic Website in 7 Minutes'
});
},
deployAWebsite: function deployAWebsite(req, res) {
res.render('resources/deploy-a-website', {
title: 'Deploy a Dynamic Website in 7 Minutes'
});
},
chat: function chat(req, res) {
res.render('resources/chat', {
title: "Enter Free Code Camp's Chat Rooms"
});
},
chat: function chat(req, res) {
res.render('resources/chat', {
title: "Enter Free Code Camp's Chat Rooms"
});
},
nonprofitProjectInstructions: function nonprofitProjectInstructions(req, res) {
res.render('resources/nonprofit-project-instructions', {
title: 'Nonprofit Project Instructions'
});
},
nonprofitProjectInstructions: function nonprofitProjectInstructions(req, res) {
res.render('resources/nonprofit-project-instructions', {
title: 'Nonprofit Project Instructions'
});
},
gmailShortcuts: function gmailShortcuts(req, res) {
res.render('resources/gmail-shortcuts', {
title: 'These Gmail Shortcuts will save you Hours'
});
},
gmailShortcuts: function gmailShortcuts(req, res) {
res.render('resources/gmail-shortcuts', {
title: 'These Gmail Shortcuts will save you Hours'
});
},
guideToOurNonprofitProjects: function guideToOurNonprofitProjects(req, res) {
res.render('resources/guide-to-our-nonprofit-projects', {
title: 'A guide to our Nonprofit Projects'
});
},
guideToOurNonprofitProjects: function guideToOurNonprofitProjects(req, res) {
res.render('resources/guide-to-our-nonprofit-projects', {
title: 'A guide to our Nonprofit Projects'
});
},
controlShortcuts: function controlShortcuts(req, res) {
res.render('resources/control-shortcuts', {
title: 'These Control Shortcuts will save you Hours'
});
},
controlShortcuts: function controlShortcuts(req, res) {
res.render('resources/control-shortcuts', {
title: 'These Control Shortcuts will save you Hours'
});
},
chromebook: function chromebook(req, res) {
res.render('resources/chromebook', {
title: 'Win a Chromebook'
});
},
chromebook: function chromebook(req, res) {
res.render('resources/chromebook', {
title: 'Win a Chromebook'
});
},
jqueryExercises: function jqueryExercises(req, res) {
res.render('resources/jquery-exercises', {
title: 'jQuery Exercises'
});
},
jqueryExercises: function jqueryExercises(req, res) {
res.render('resources/jquery-exercises', {
title: 'jQuery Exercises'
});
},
livePairProgramming: function(req, res) {
res.render('resources/live-pair-programming', {
title: 'Live Pair Programming'
});
},
livePairProgramming: function(req, res) {
res.render('resources/live-pair-programming', {
title: 'Live Pair Programming'
});
},
installScreenHero: function(req, res) {
res.render('resources/install-screenhero', {
title: 'Install ScreenHero'
});
},
installScreenHero: function(req, res) {
res.render('resources/install-screenhero', {
title: 'Install ScreenHero'
});
},
javaScriptInYourInbox: function(req, res) {
res.render('resources/javascript-in-your-inbox', {
title: 'JavaScript in your Inbox'
});
},
javaScriptInYourInbox: function(req, res) {
res.render('resources/javascript-in-your-inbox', {
title: 'JavaScript in your Inbox'
});
},
nodeSchoolChallenges: function(req, res) {
res.render('resources/nodeschool-challenges', {
title: 'NodeSchool Challenges'
});
},
nodeSchoolChallenges: function(req, res) {
res.render('resources/nodeschool-challenges', {
title: 'NodeSchool Challenges'
});
},
githubCalls: function(req, res) {
var githubHeaders = {headers: {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1521.3 Safari/537.36'}, port:80 };
request('https://api.github.com/repos/freecodecamp/freecodecamp/pulls?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function(err, status1, pulls) {
pulls = pulls ? Object.keys(JSON.parse(pulls)).length : "Can't connect to github";
request('https://api.github.com/repos/freecodecamp/freecodecamp/issues?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function (err, status2, issues) {
issues = ((pulls === parseInt(pulls)) && issues) ? Object.keys(JSON.parse(issues)).length - pulls : "Can't connect to GitHub";
res.send({"issues": issues, "pulls" : pulls});
});
});
},
githubCalls: function(req, res) {
var githubHeaders = {headers: {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1521.3 Safari/537.36'}, port:80 };
request('https://api.github.com/repos/freecodecamp/freecodecamp/pulls?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function(err, status1, pulls) {
pulls = pulls ? Object.keys(JSON.parse(pulls)).length : "Can't connect to github";
request('https://api.github.com/repos/freecodecamp/freecodecamp/issues?client_id=' + secrets.github.clientID + '&client_secret=' + secrets.github.clientSecret, githubHeaders, function (err, status2, issues) {
issues = ((pulls === parseInt(pulls)) && issues) ? Object.keys(JSON.parse(issues)).length - pulls : "Can't connect to GitHub";
res.send({"issues": issues, "pulls" : pulls});
});
});
},
about: function(req, res, next) {
if (req.user) {
if (!req.user.profile.picture || req.user.profile.picture === "https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png") {
req.user.profile.picture = "https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png";
req.user.save();
}
}
var date1 = new Date('10/15/2014');
var date2 = new Date();
var progressTimestamps = req.user.progressTimestamps;
var now = Date.now() || 0;
if (req.user.pointsNeedMigration) {
var challengesHash = req.user.challengesHash;
for (var key in challengesHash) {
if (challengesHash[key] > 0) {
req.user.progressTimestamps.push(challengesHash[key]);
}
}
var timeStamps = [];
R.keys(req.user.challengesHash).forEach(function(key) {
"use strict";
var timeStamp = parseInt(challengesHash[key], 10);
timeStamps.push({timeStamp: timeStamp.length !== 13 ? (+timeStamp) : (+timeStamp * 1000)});
});
req.user.completedCoursewares = Array.zip(timeStamps, coursewares,
function(left, right) {
"use strict";
return ({
completedDate: left.timeStamp,
_id: right._id,
name: right.name
});
}).filter(function(elem) {
"use strict";
return elem.completedDate !== 0;
});
req.user.pointsNeedMigration = false;
req.user.save();
}
if (progressTimestamps[progressTimestamps.length - 1] <= (now - 43200)) {
req.user.progressTimestamps.push(now);
}
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24));
var announcements = resources.announcements;
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
User.count({}, function (err, c3) {
if (err) {
debug('User err: ', err);
return next(err);
}
User.count({'points': {'$gt': 53}}, function (err, all) {
if (err) {
debug('User err: ', err);
return next(err);
}
res.render('resources/learn-to-code', {
title: 'About Free Code Camp and Our Team of Volunteers',
daysRunning: daysRunning,
c3: numberWithCommas(c3),
all: all,
announcements: announcements
});
});
});
},
randomPhrase: function() {
var phrases = resources.phrases;
return phrases[Math.floor(Math.random() * phrases.length)];
},
randomVerb: function() {
var verbs = resources.verbs;
return verbs[Math.floor(Math.random() * verbs.length)];
},
randomCompliment: function() {
var compliments = resources.compliments;
return compliments[Math.floor(Math.random() * compliments.length)];
},
allBonfireIds: function() {
return bonfires.map(function(elem) {
return {
_id: elem._id,
difficulty: elem.difficulty
}
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map(function(elem) {
return elem._id;
});
},
allBonfireNames: function() {
return bonfires.map(function(elem) {
return {
name: elem.name,
difficulty: elem.difficulty,
_id: elem._id
}
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map (function(elem) {
return {
name : elem.name,
_id: elem._id
}
});
},
getAllCourses: function() {
"use strict";
return coursewares;
},
allCoursewareIds: function() {
return coursewares.map(function(elem) {
return {
_id: elem._id,
difficulty: elem.difficulty
};
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map(function(elem) {
return elem._id;
});
},
allCoursewareNames: function() {
return coursewares.map(function(elem) {
return {
name: elem.name,
difficulty: elem.difficulty,
_id: elem._id
};
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map (function(elem) {
return {
name: elem.name,
_id: elem._id
};
});
},
whichEnvironment: function() {
return process.env.NODE_ENV;
},
getURLTitle: function(url, callback) {
(function () {
var result = {title: '', image: '', url: '', description: ''};
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var $ = cheerio.load(body);
var metaDescription = $("meta[name='description']");
var metaImage = $("meta[property='og:image']");
var urlImage = metaImage.attr('content') ? metaImage.attr('content') : '';
var description = metaDescription.attr('content') ? metaDescription.attr('content') : '';
result.title = $('title').text().length < 141 ? $('title').text() : $('title').text().slice(0, 137) + " ...";
result.image = urlImage;
result.description = description;
callback(null, result);
} else {
callback('failed');
}
});
})();
},
updateUserStoryPictures: function(userId, picture, username, cb) {
var counter = 0,
foundStories,
foundComments;
Story.find({'author.userId': userId}, function(err, stories) {
if (err) {
return cb(err);
}
foundStories = stories;
counter++;
saveStoriesAndComments();
});
Comment.find({'author.userId': userId}, function(err, comments) {
if (err) {
return cb(err);
}
foundComments = comments;
counter++;
saveStoriesAndComments();
});
function saveStoriesAndComments() {
if (counter !== 2) {
return;
}
var tasks = [];
R.forEach(function(comment) {
comment.author.picture = picture;
comment.author.username = username;
comment.markModified('author');
tasks.push(function(cb) {
comment.save(cb);
});
}, foundComments);
R.forEach(function(story) {
story.author.picture = picture;
story.author.username = username;
story.markModified('author');
tasks.push(function(cb) {
story.save(cb);
});
}, foundStories);
async.parallel(tasks, function(err) {
if (err) { return cb(err); }
cb();
});
}
about: function(req, res, next) {
if (req.user) {
if (!req.user.profile.picture || req.user.profile.picture === "https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png") {
req.user.profile.picture = "https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png";
req.user.save();
}
}
var date1 = new Date('10/15/2014');
var date2 = new Date();
var progressTimestamps = req.user.progressTimestamps;
var now = Date.now() || 0;
if (req.user.pointsNeedMigration) {
var challengesHash = req.user.challengesHash;
for (var key in challengesHash) {
if (challengesHash[key] > 0) {
req.user.progressTimestamps.push(challengesHash[key]);
}
}
var oldChallengeKeys = R.keys(req.user.challengesHash);
var updatedTimesFromOldChallenges = oldChallengeKeys.map(function(timeStamp) {
if (timeStamp.toString().length !== 13) {
timeStamp *= 1000;
}
return timeStamp;
});
var newTimeStamps = R.map(function(timeStamp) {
if (timeStamp.toString().length !== 13) {
timeStamp *= 1000;
}
return timeStamp;
}, req.user.progressTimestamps);
req.user.progressTimestamps = newTimeStamps;
req.user.completedCoursewares = Array.zip(updatedTimesFromOldChallenges, coursewares,
function(left, right) {
return ({
completedDate: left.timeStamp,
_id: right._id,
name: right.name
});
}).filter(function(elem) {
return elem.completedDate !== 0;
});
req.user.pointsNeedMigration = false;
req.user.save();
}
if (progressTimestamps[progressTimestamps.length - 1] <= (now - 43200)) {
req.user.progressTimestamps.push(now);
}
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24));
var announcements = resources.announcements;
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
User.count({}, function (err, c3) {
if (err) {
debug('User err: ', err);
return next(err);
}
User.count({'points': {'$gt': 53}}, function (err, all) {
if (err) {
debug('User err: ', err);
return next(err);
}
res.render('resources/learn-to-code', {
title: 'About Free Code Camp and Our Team of Volunteers',
daysRunning: daysRunning,
c3: numberWithCommas(c3),
all: all,
announcements: announcements
});
});
});
},
randomPhrase: function() {
var phrases = resources.phrases;
return phrases[Math.floor(Math.random() * phrases.length)];
},
randomVerb: function() {
var verbs = resources.verbs;
return verbs[Math.floor(Math.random() * verbs.length)];
},
randomCompliment: function() {
var compliments = resources.compliments;
return compliments[Math.floor(Math.random() * compliments.length)];
},
allBonfireIds: function() {
return bonfires.map(function(elem) {
return {
_id: elem._id,
difficulty: elem.difficulty
}
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map(function(elem) {
return elem._id;
});
},
allBonfireNames: function() {
return bonfires.map(function(elem) {
return {
name: elem.name,
difficulty: elem.difficulty,
_id: elem._id
}
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map (function(elem) {
return {
name : elem.name,
_id: elem._id
}
});
},
getAllCourses: function() {
"use strict";
return coursewares;
},
allCoursewareIds: function() {
return coursewares.map(function(elem) {
return {
_id: elem._id,
difficulty: elem.difficulty
};
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map(function(elem) {
return elem._id;
});
},
allCoursewareNames: function() {
return coursewares.map(function(elem) {
return {
name: elem.name,
difficulty: elem.difficulty,
_id: elem._id
};
})
.sort(function(a, b) {
return a.difficulty - b.difficulty;
})
.map (function(elem) {
return {
name: elem.name,
_id: elem._id
};
});
},
whichEnvironment: function() {
return process.env.NODE_ENV;
},
getURLTitle: function(url, callback) {
(function () {
var result = {title: '', image: '', url: '', description: ''};
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
var $ = cheerio.load(body);
var metaDescription = $("meta[name='description']");
var metaImage = $("meta[property='og:image']");
var urlImage = metaImage.attr('content') ? metaImage.attr('content') : '';
var description = metaDescription.attr('content') ? metaDescription.attr('content') : '';
result.title = $('title').text().length < 141 ? $('title').text() : $('title').text().slice(0, 137) + " ...";
result.image = urlImage;
result.description = description;
callback(null, result);
} else {
callback('failed');
}
});
})();
},
updateUserStoryPictures: function(userId, picture, username, cb) {
var counter = 0,
foundStories,
foundComments;
Story.find({'author.userId': userId}, function(err, stories) {
if (err) {
return cb(err);
}
foundStories = stories;
counter++;
saveStoriesAndComments();
});
Comment.find({'author.userId': userId}, function(err, comments) {
if (err) {
return cb(err);
}
foundComments = comments;
counter++;
saveStoriesAndComments();
});
function saveStoriesAndComments() {
if (counter !== 2) {
return;
}
var tasks = [];
R.forEach(function(comment) {
comment.author.picture = picture;
comment.author.username = username;
comment.markModified('author');
tasks.push(function(cb) {
comment.save(cb);
});
}, foundComments);
R.forEach(function(story) {
story.author.picture = picture;
story.author.username = username;
story.markModified('author');
tasks.push(function(cb) {
story.save(cb);
});
}, foundStories);
async.parallel(tasks, function(err) {
if (err) { return cb(err); }
cb();
});
}
}
};

View File

@ -1,431 +1,447 @@
/* eslint-disable no-catch-shadow, no-unused-vars */
var R = require('ramda'),
debug = require('debug')('freecc:cntr:story'),
Story = require('./../models/Story'),
Comment = require('./../models/Comment'),
User = require('./../models/User'),
moment = require('../public/js/lib/moment/moment.js'),
resources = require('./resources'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient,
secrets = require('../config/secrets'),
sanitizeHtml = require('sanitize-html');
debug = require('debug')('freecc:cntr:story'),
Story = require('./../models/Story'),
Comment = require('./../models/Comment'),
User = require('./../models/User'),
moment = require('../public/js/lib/moment/moment.js'),
resources = require('./resources'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient,
secrets = require('../config/secrets'),
sanitizeHtml = require('sanitize-html');
function hotRank(timeValue, rank) {
/*
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
* tMS = postedOnDate - foundationTime;
* Ranking...
* f(ts, 1, rank) = log(10)z + (ts)/45000;
*/
var hotness;
var z = Math.log(rank) / Math.log(10);
hotness = z + (timeValue / 115200000);
return hotness;
/*
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
* tMS = postedOnDate - foundationTime;
* Ranking...
* f(ts, 1, rank) = log(10)z + (ts)/45000;
*/
var hotness;
var z = Math.log(rank) / Math.log(10);
hotness = z + (timeValue / 115200000);
return hotness;
}
exports.hotJSON = function(req, res) {
var story = Story.find({}).sort({'timePosted': -1}).limit(1000);
story.exec(function(err, stories) {
if (err) {
res.send(500);
return next(err);
}
exports.hotJSON = function(req, res, next) {
var story = Story.find({}).sort({'timePosted': -1}).limit(1000);
story.exec(function(err, stories) {
if (err) {
return next(err);
}
var foundationDate = 1413298800000;
var foundationDate = 1413298800000;
var sliceVal = stories.length >= 100 ? 100 : stories.length;
return res.json(stories.map(function(elem) {
return elem;
}).sort(function(a, b) {
return hotRank(b.timePosted - foundationDate, b.rank, b.headline) - hotRank(a.timePosted - foundationDate, a.rank, a.headline);
}).slice(0, sliceVal));
var sliceVal = stories.length >= 100 ? 100 : stories.length;
return res.json(stories.map(function(elem) {
return elem;
}).sort(function(a, b) {
return hotRank(b.timePosted - foundationDate, b.rank, b.headline)
- hotRank(a.timePosted - foundationDate, a.rank, a.headline);
}).slice(0, sliceVal));
});
});
};
exports.recentJSON = function(req, res, next) {
var story = Story.find({}).sort({'timePosted': -1}).limit(100);
story.exec(function(err, stories) {
if (err) {
res.status(500);
return next(err);
}
res.json(stories);
});
var story = Story.find({}).sort({'timePosted': -1}).limit(100);
story.exec(function(err, stories) {
if (err) {
return next(err);
}
return res.json(stories);
});
};
exports.hot = function(req, res) {
res.render('stories/index', {
title: 'Hot stories currently trending on Camper News',
page: 'hot'
});
return res.render('stories/index', {
title: 'Hot stories currently trending on Camper News',
page: 'hot'
});
};
exports.submitNew = function(req, res) {
res.render('stories/index', {
title: 'Submit a new story to Camper News',
page: 'submit'
});
return res.render('stories/index', {
title: 'Submit a new story to Camper News',
page: 'submit'
});
};
exports.search = function(req, res) {
res.render('stories/index', {
title: 'Search the archives of Camper News',
page: 'search'
});
return res.render('stories/index', {
title: 'Search the archives of Camper News',
page: 'search'
});
};
exports.recent = function(req, res) {
res.render('stories/index', {
title: 'Recently submitted stories on Camper News',
page: 'recent'
});
return res.render('stories/index', {
title: 'Recently submitted stories on Camper News',
page: 'recent'
});
};
exports.preSubmit = function(req, res) {
var data = req.query;
var cleanData = sanitizeHtml(data.url, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;;/g, '"');
if (data.url.replace(/&/g, '&amp;') !== cleanData) {
var data = req.query;
var cleanData = sanitizeHtml(data.url, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;;/g, '"');
if (data.url.replace(/&/g, '&amp;') !== cleanData) {
req.flash('errors', {
msg: 'The data for this post is malformed'
});
return res.render('stories/index', {
page: 'stories/submit'
});
}
var title = data.title || '';
var image = data.image || '';
var description = data.description || '';
return res.render('stories/index', {
title: "Confirm your Camper News story submission",
page: 'storySubmission',
storyURL: data.url,
storyTitle: title,
storyImage: image,
storyMetaDescription: description
req.flash('errors', {
msg: 'The data for this post is malformed'
});
return res.render('stories/index', {
page: 'stories/submit'
});
}
var title = data.title || '';
var image = data.image || '';
var description = data.description || '';
return res.render('stories/index', {
title: 'Confirm your Camper News story submission',
page: 'storySubmission',
storyURL: data.url,
storyTitle: title,
storyImage: image,
storyMetaDescription: description
});
};
exports.returnIndividualStory = function(req, res, next) {
var dashedName = req.params.storyName;
var dashedName = req.params.storyName;
var storyName = dashedName.replace(/\-/g, ' ');
var storyName = dashedName.replace(/\-/g, ' ');
Story.find({'storyLink' : new RegExp(storyName, 'i')}, function(err, story) {
if (err) {
next(err);
}
Story.find({'storyLink': new RegExp(storyName, 'i')}, function(err, story) {
if (err) {
return next(err);
}
if (story.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a story with that name. Please double check the name."
});
if (story.length < 1) {
req.flash('errors', {
msg: "404: We couldn't find a story with that name. Please double check the name."
});
return res.redirect('/stories/');
}
return res.redirect('/stories/');
}
story = story.pop();
var dashedNameFull = story.storyLink.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull !== dashedName) {
return res.redirect('../stories/' + dashedNameFull);
}
story = story.pop();
var dashedNameFull = story.storyLink.toLowerCase().replace(/\s/g, '-');
if (dashedNameFull !== dashedName) {
return res.redirect('../stories/' + dashedNameFull);
}
var userVoted = false;
try {
var votedObj = story.upVotes.filter(function(a){
return a['upVotedByUsername'] === req.user['profile']['username'];
})
if (votedObj.length > 0){
userVoted = true;
}
} catch(err){
userVoted = false;
}
res.render('stories/index', {
title: story.headline,
link: story.link,
author: story.author,
description: story.description,
rank: story.upVotes.length,
upVotes: story.upVotes,
comments: story.comments,
id: story._id,
timeAgo: moment(story.timePosted).fromNow(),
image: story.image,
page: 'show',
storyMetaDescription: story.metaDescription,
hasUserVoted: userVoted
});
var userVoted = false;
try {
var votedObj = story.upVotes.filter(function(a) {
return a['upVotedByUsername'] === req.user['profile']['username'];
});
if (votedObj.length > 0) {
userVoted = true;
}
} catch(err) {
userVoted = false;
}
res.render('stories/index', {
title: story.headline,
link: story.link,
author: story.author,
description: story.description,
rank: story.upVotes.length,
upVotes: story.upVotes,
comments: story.comments,
id: story._id,
timeAgo: moment(story.timePosted).fromNow(),
image: story.image,
page: 'show',
storyMetaDescription: story.metaDescription,
hasUserVoted: userVoted
});
});
};
exports.getStories = function(req, res) {
MongoClient.connect(secrets.db, function(err, database) {
database.collection('stories').find({
"$text": {
"$search": req.body.data.searchValue
}
}, {
headline: 1,
timePosted: 1,
link: 1,
description: 1,
rank: 1,
upVotes: 1,
author: 1,
comments: 1,
image: 1,
storyLink: 1,
metaDescription: 1,
textScore: {
$meta: "textScore"
}
}, {
sort: {
textScore: {
$meta: "textScore"
}
}
}).toArray(function(err, items) {
if (items !== null && items.length !== 0) {
return res.json(items);
}
return res.status(404);
});
exports.getStories = function(req, res, next) {
MongoClient.connect(secrets.db, function(err, database) {
if (err) {
return next(err);
}
database.collection('stories').find({
'$text': {
'$search': req.body.data.searchValue
}
}, {
headline: 1,
timePosted: 1,
link: 1,
description: 1,
rank: 1,
upVotes: 1,
author: 1,
comments: 1,
image: 1,
storyLink: 1,
metaDescription: 1,
textScore: {
$meta: 'textScore'
}
}, {
sort: {
textScore: {
$meta: 'textScore'
}
}
}).toArray(function(err, items) {
if (err) {
return next(err);
}
if (items !== null && items.length !== 0) {
return res.json(items);
}
return res.sendStatus(404);
});
});
};
exports.upvote = function(req, res, next) {
var data = req.body.data;
Story.find({'_id': data.id}, function(err, story) {
if (err) {
res.status(500);
return next(err);
}
story = story.pop();
story.rank++;
story.upVotes.push(
{
upVotedBy: data.upVoter._id,
upVotedByUsername: data.upVoter.profile.username
}
);
story.markModified('rank');
story.save();
return res.send(story);
var data = req.body.data;
Story.find({'_id': data.id}, function(err, story) {
if (err) {
return next(err);
}
story = story.pop();
story.rank++;
story.upVotes.push(
{
upVotedBy: data.upVoter._id,
upVotedByUsername: data.upVoter.profile.username
}
);
story.markModified('rank');
story.save();
User.find({'_id': story.author.userId}, function(err, user) {
'use strict';
if (err) {
return next(err);
}
user = user.pop();
user.progressTimestamps.push(Date.now());
user.save();
});
return res.send(story);
});
};
exports.comments = function(req, res, next) {
var data = req.params.id;
Comment.find({'_id': data}, function(err, comment) {
if (err) {
res.status(500);
return next(err);
}
comment = comment.pop();
return res.send(comment);
});
var data = req.params.id;
Comment.find({'_id': data}, function(err, comment) {
if (err) {
return next(err);
}
comment = comment.pop();
return res.send(comment);
});
};
exports.newStory = function(req, res) {
if (!req.user) {
return res.status(500);
exports.newStory = function(req, res, next) {
if (!req.user) {
return next(new Error('Must be logged in'));
}
var url = req.body.data.url;
var cleanURL = sanitizeHtml(url, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"');
if (cleanURL !== url) {
req.flash('errors', {
msg: "The URL you submitted doesn't appear valid"
});
return res.json({
alreadyPosted: true,
storyURL: '/stories/submit'
});
}
if (url.search(/^https?:\/\//g) === -1) {
url = 'http://' + url;
}
Story.find({'link': url}, function(err, story) {
if (err) {
return next(err);
}
var url = req.body.data.url;
var cleanURL = sanitizeHtml(url, {
if (story.length) {
req.flash('errors', {
msg: "Someone's already posted that link. Here's the discussion."
});
return res.json({
alreadyPosted: true,
storyURL: '/stories/' + story.pop().storyLink
});
}
resources.getURLTitle(url, processResponse);
});
function processResponse(err, story) {
if (err) {
res.json({
alreadyPosted: false,
storyURL: url,
storyTitle: '',
storyImage: '',
storyMetaDescription: ''
});
} else {
res.json({
alreadyPosted: false,
storyURL: url,
storyTitle: story.title,
storyImage: story.image,
storyMetaDescription: story.description
});
}
}
};
exports.storySubmission = function(req, res, next) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return next(new Error('Not authorized'));
}
var storyLink = data.headline
.replace(/\'/g, '')
.replace(/\"/g, '')
.replace(/,/g, '')
.replace(/[^a-z0-9]/gi, ' ')
.replace(/\s+/g, ' ')
.toLowerCase();
var link = data.link;
if (link.search(/^https?:\/\//g) === -1) {
link = 'http://' + link;
}
var story = new Story({
headline: sanitizeHtml(data.headline, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"'),
timePosted: Date.now(),
link: link,
description: sanitizeHtml(data.description, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"'),
rank: 1,
upVotes: data.upVotes,
author: data.author,
comments: [],
image: data.image,
storyLink: storyLink,
metaDescription: data.storyMetaDescription
});
req.user.progressTimestamps.push(Date.now());
req.user.save();
story.save(function(err) {
if (err) {
return next(err);
}
res.send(JSON.stringify({
storyLink: story.storyLink.replace(/\s/g, '-').toLowerCase()
}));
});
};
exports.commentSubmit = function(req, res, next) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return next(new Error('Not authorized'));
}
var sanitizedBody = sanitizeHtml(data.body,
{
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"');
if (cleanURL !== url) {
req.flash('errors', {
msg: "The URL you submitted doesn't appear valid"
});
return res.json({
alreadyPosted: true,
storyURL: '/stories/submit'
});
}
if (url.search(/^https?:\/\//g) === -1) {
url = 'http://' + url;
}
Story.find({'link': url}, function(err, story) {
if (err) {
return res.status(500);
}
if (story.length) {
req.flash('errors', {
msg: "Someone's already posted that link. Here's the discussion."
});
return res.json({
alreadyPosted: true,
storyURL: '/stories/' + story.pop().storyLink
});
}
resources.getURLTitle(url, processResponse);
if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
function processResponse(err, story) {
if (err) {
res.json({
alreadyPosted: false,
storyURL: url,
storyTitle: '',
storyImage: '',
storyMetaDescription: ''
});
} else {
res.json({
alreadyPosted: false,
storyURL: url,
storyTitle: story.title,
storyImage: story.image,
storyMetaDescription: story.description
});
}
}
return res.send(true);
}
var comment = new Comment({
associatedPost: data.associatedPost,
body: sanitizedBody,
rank: 0,
upvotes: 0,
author: data.author,
comments: [],
topLevel: true,
commentOn: Date.now()
});
commentSave(comment, Story, res, next);
};
exports.storySubmission = function(req, res) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return res.status(500);
}
var storyLink = data.headline
.replace(/\'/g, '')
.replace(/\"/g, '')
.replace(/,/g, '')
.replace(/[^a-z0-9]/gi, ' ')
.replace(/\s+/g, ' ')
.toLowerCase();
var link = data.link;
if (link.search(/^https?:\/\//g) === -1) {
link = 'http://' + link;
}
var story = new Story({
headline: sanitizeHtml(data.headline, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"'),
timePosted: Date.now(),
link: link,
description: sanitizeHtml(data.description, {
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"'),
rank: 1,
upVotes: data.upVotes,
author: data.author,
comments: [],
image: data.image,
storyLink: storyLink,
metaDescription: data.storyMetaDescription
});
exports.commentOnCommentSubmit = function(req, res, next) {
var data = req.body.data;
story.save(function(err) {
if (req.user._id.toString() !== data.author.userId.toString()) {
return next(new Error('Not authorized'));
}
var sanitizedBody = sanitizeHtml(data.body,
{
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"');
if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
return res.send(true);
}
var comment = new Comment({
associatedPost: data.associatedPost,
body: sanitizedBody,
rank: 0,
upvotes: 0,
author: data.author,
comments: [],
topLevel: false,
commentOn: Date.now()
});
commentSave(comment, Comment, res, next);
};
function commentSave(comment, Context, res, next) {
comment.save(function(err, data) {
if (err) {
return next(err);
}
try {
Context.find({'_id': comment.associatedPost}, function (err, associatedStory) {
if (err) {
return res.status(500);
return next(err);
}
res.send(JSON.stringify({
storyLink: story.storyLink.replace(/\s/g, '-').toLowerCase()
}));
});
};
exports.commentSubmit = function(req, res) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return res.status(500);
}
var sanitizedBody = sanitizeHtml(data.body,
{
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"');
if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
return res.send(true);
}
var comment = new Comment({
associatedPost: data.associatedPost,
body: sanitizedBody,
rank: 0,
upvotes: 0,
author: data.author,
comments: [],
topLevel: true,
commentOn: Date.now()
});
commentSave(comment, Story, res);
};
exports.commentOnCommentSubmit = function(req, res) {
var data = req.body.data;
if (req.user._id.toString() !== data.author.userId.toString()) {
return res.status(500);
}
var sanitizedBody = sanitizeHtml(data.body,
{
allowedTags: [],
allowedAttributes: []
}).replace(/&quot;/g, '"');
if (data.body !== sanitizedBody) {
req.flash('errors', {
msg: 'HTML is not allowed'
});
return res.send(true);
}
var comment = new Comment({
associatedPost: data.associatedPost,
body: sanitizedBody,
rank: 0,
upvotes: 0,
author: data.author,
comments: [],
topLevel: false,
commentOn: Date.now()
});
commentSave(comment, Comment, res);
};
function commentSave(comment, Context, res) {
comment.save(function(err, data) {
if (err) {
return res.status(500);
associatedStory = associatedStory.pop();
if (associatedStory) {
associatedStory.comments.push(data._id);
associatedStory.save(function (err) {
if (err) {
return next(err);
}
res.send(true);
});
}
try {
Context.find({'_id': comment.associatedPost}, function (err, associatedStory) {
if (err) {
return res.status(500);
}
associatedStory = associatedStory.pop();
if (associatedStory) {
associatedStory.comments.push(data._id);
associatedStory.save(function (err) {
if (err) {
res.status(500);
}
res.send(true);
});
}
});
} catch (e) {
// delete comment
return res.status(500);
}
});
});
} catch (e) {
// delete comment
return next(err);
}
});
}

View File

@ -6,9 +6,9 @@ var _ = require('lodash'),
User = require('../models/User'),
secrets = require('../config/secrets'),
moment = require('moment'),
Challenge = require('./../models/Challenge'),
debug = require('debug')('freecc:cntr:challenges'),
resources = require('./resources');
resources = require('./resources'),
R = require('ramda');
@ -18,7 +18,9 @@ var _ = require('lodash'),
*/
exports.getSignin = function(req, res) {
if (req.user) return res.redirect('/');
if (req.user) {
return res.redirect('/');
}
res.render('account/signin', {
title: 'Free Code Camp Login'
});
@ -41,13 +43,17 @@ exports.postSignin = function(req, res, next) {
}
passport.authenticate('local', function(err, user, info) {
if (err) return next(err);
if (err) {
return next(err);
}
if (!user) {
req.flash('errors', { msg: info.message });
return res.redirect('/signin');
}
req.logIn(user, function(err) {
if (err) return next(err);
if (err) {
return next(err);
}
req.flash('success', { msg: 'Success! You are logged in.' });
res.redirect(req.session.returnTo || '/');
});
@ -70,7 +76,9 @@ exports.signout = function(req, res) {
*/
exports.getEmailSignin = function(req, res) {
if (req.user) return res.redirect('/');
if (req.user) {
return res.redirect('/');
}
res.render('account/email-signin', {
title: 'Sign in to your Free Code Camp Account'
});
@ -82,7 +90,9 @@ exports.getEmailSignin = function(req, res) {
*/
exports.getEmailSignup = function(req, res) {
if (req.user) return res.redirect('/');
if (req.user) {
return res.redirect('/');
}
res.render('account/email-signup', {
title: 'Create Your Free Code Camp Account'
});
@ -124,7 +134,7 @@ exports.postEmailSignup = function(req, res, next) {
var user = new User({
email: req.body.email.trim(),
password: req.body.password,
profile : {
profile: {
username: req.body.username.trim(),
picture: 'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png'
}
@ -174,7 +184,7 @@ exports.postEmailSignup = function(req, res, next) {
'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",
'And if you have a moment, check out our blog: blog.freecodecamp.com.\n',
'Good luck with the challenges!\n\n',
'- the Volunteer Camp Counselor Team'
].join('')
@ -190,10 +200,45 @@ exports.postEmailSignup = function(req, res, next) {
* For Calendar display
*/
exports.getStreak = function(req, res) {
var completedStreak = req.user.challengesHash;
exports.getStreak = function(req, res, next) {
}
req.user.progressTimestamps = req.user.progressTimestamps.sort(function(a, b) {
return a - b;
});
var timeObject = Object.create(null);
R.forEach(function(time) {
timeObject[moment(time).format('YYYY-MM-DD')] = time;
}, req.user.progressTimestamps);
var tmpLongest = 1;
var timeKeys = R.keys(timeObject);
for (var i = 1; i <= timeKeys.length; i++) {
if (moment(timeKeys[i - 1]).add(1, 'd').toString()
=== moment(timeKeys[i]).toString()) {
tmpLongest++;
if (tmpLongest > req.user.currentStreak) {
req.user.currentStreak = tmpLongest;
}
if ( req.user.currentStreak > req.user.longestStreak) {
req.user.longestStreak = req.user.currentStreak;
}
}
}
req.user.save(function(err) {
if (err) {
return next(err);
}
});
s
var payload = {
longest: req.user.longestStreak,
timeObject: timeObject
};
return res.send(payload);
};
/**
* GET /account
@ -272,7 +317,7 @@ exports.returnUser = function(req, res, next) {
var data = {};
var progressTimestamps = user.progressTimestamps;
for (var i = 0; i < progressTimestamps.length; i++) {
data[progressTimestamps[i].toString()] = 1;
data[(progressTimestamps[i] / 1000).toString()] = 1;
}
res.render('account/show', {

View File

@ -1,7 +1,9 @@
var bcrypt = require('bcrypt-nodejs');
var crypto = require('crypto');
var mongoose = require('mongoose');
require('mongoose-long')(mongoose);
var Long = mongoose.Types.Long;
var userSchema = new mongoose.Schema({
email: {
type: String,
@ -21,7 +23,7 @@ var userSchema = new mongoose.Schema({
type: Number,
default: 0
},
progressTimestamps: { type: Array, default: [Date] },
progressTimestamps: [],
challengesCompleted: { type: Array, default: [] },
pointsNeedMigration: { type: Boolean, default: true },
challengesHash: {
@ -332,9 +334,30 @@ var userSchema = new mongoose.Schema({
resetPasswordToken: String,
resetPasswordExpires: Date,
uncompletedBonfires: Array,
completedBonfires: Array,
completedBonfires: [
{
_id: String,
completedWith: String,
completedDate: Long,
solution: String
}
],
uncompletedCoursewares: Array,
completedCoursewares: Array
completedCoursewares: [
{
completedDate: Long,
_id: String,
name: String
}
],
currentStreak: {
type: Number,
default: 0
},
longestStreak: {
type: Number,
default: 0
}
});
/**

View File

@ -47,11 +47,9 @@
"method-override": "^2.3.0",
"moment": "^2.8.4",
"mongodb": "^1.4.33",
"mongoose": "^3.8.19",
"mongoose-text-search": "0.0.2",
"mongoose": "^4.0.0",
"morgan": "^1.5.0",
"newrelic": "^1.13.3",
"node": "0.0.0",
"nodemailer": "^1.3.0",
"passport": "^0.2.1",
"passport-facebook": "^1.0.3",
@ -76,7 +74,6 @@
"chai": "^1.10.0",
"gulp": "^3.8.8",
"gulp-inject": "^1.0.2",
"gulp-minify-css": "^0.5.1",
"gulp-nodemon": "^1.0.4",
"mocha": "^2.0.1",
"multiline": "^1.0.1",