2
app.js
2
app.js
@ -324,7 +324,7 @@ app.get(
|
|||||||
);
|
);
|
||||||
|
|
||||||
app.get(
|
app.get(
|
||||||
'/stories/submit/:newStory',
|
'/stories/submit/new-story',
|
||||||
storyController.preSubmit
|
storyController.preSubmit
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ var User = require('../models/User'),
|
|||||||
Story = require('./../models/Story'),
|
Story = require('./../models/Story'),
|
||||||
Comment = require('./../models/Comment'),
|
Comment = require('./../models/Comment'),
|
||||||
resources = require('./resources.json'),
|
resources = require('./resources.json'),
|
||||||
questions = resources.questions,
|
|
||||||
steps = resources.steps,
|
steps = resources.steps,
|
||||||
secrets = require('./../config/secrets'),
|
secrets = require('./../config/secrets'),
|
||||||
bonfires = require('../seed_data/bonfires.json'),
|
bonfires = require('../seed_data/bonfires.json'),
|
||||||
@ -154,7 +153,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
bloggerCalls: function(req, res) {
|
bloggerCalls: function(req, res) {
|
||||||
request('https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/posts?key=' + secrets.blogger.key, function (err, status, blog) {
|
request('https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/posts?key=' + secrets.blogger.key, function (err, status, blog) {
|
||||||
var blog = blog.length > 100 ? JSON.parse(blog) : "";
|
blog = blog.length > 100 ? JSON.parse(blog) : '';
|
||||||
res.send({
|
res.send({
|
||||||
blog1Title: blog ? blog["items"][0]["title"] : "Can't connect to Blogger",
|
blog1Title: blog ? blog["items"][0]["title"] : "Can't connect to Blogger",
|
||||||
blog1Link: blog ? blog["items"][0]["url"] : "http://blog.freecodecamp.com",
|
blog1Link: blog ? blog["items"][0]["url"] : "http://blog.freecodecamp.com",
|
||||||
@ -225,10 +224,6 @@ module.exports = {
|
|||||||
return compliments[Math.floor(Math.random() * compliments.length)];
|
return compliments[Math.floor(Math.random() * compliments.length)];
|
||||||
},
|
},
|
||||||
|
|
||||||
numberOfBonfires: function() {
|
|
||||||
return bonfires.length - 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
allBonfireIds: function() {
|
allBonfireIds: function() {
|
||||||
return bonfires.map(function(elem) {
|
return bonfires.map(function(elem) {
|
||||||
return {
|
return {
|
||||||
@ -290,14 +285,19 @@ module.exports = {
|
|||||||
return process.env.NODE_ENV;
|
return process.env.NODE_ENV;
|
||||||
},
|
},
|
||||||
getURLTitle: function(url, callback) {
|
getURLTitle: function(url, callback) {
|
||||||
|
debug('got url in meta scraping function', url);
|
||||||
(function () {
|
(function () {
|
||||||
var result = {title: ''};
|
var result = {title: '', image: '', url: '', description: ''};
|
||||||
request(url, function (error, response, body) {
|
request(url, function (error, response, body) {
|
||||||
if (!error && response.statusCode === 200) {
|
if (!error && response.statusCode === 200) {
|
||||||
var $ = cheerio.load(body);
|
var $ = cheerio.load(body);
|
||||||
var title = $('title').text();
|
var metaDescription = $("meta[name='description']");
|
||||||
result.title = title;
|
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();
|
||||||
|
result.image = urlImage;
|
||||||
|
result.description = description;
|
||||||
callback(null, result);
|
callback(null, result);
|
||||||
} else {
|
} else {
|
||||||
callback('failed');
|
callback('failed');
|
||||||
|
@ -8,9 +8,9 @@ var R = require('ramda'),
|
|||||||
mongodb = require('mongodb'),
|
mongodb = require('mongodb'),
|
||||||
MongoClient = mongodb.MongoClient,
|
MongoClient = mongodb.MongoClient,
|
||||||
secrets = require('../config/secrets'),
|
secrets = require('../config/secrets'),
|
||||||
User = require('./../models/User');
|
sanitizeHtml = require('sanitize-html');
|
||||||
|
|
||||||
function hotRank(timeValue, rank, headline) {
|
function hotRank(timeValue, rank) {
|
||||||
/*
|
/*
|
||||||
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
|
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
|
||||||
* tMS = postedOnDate - foundationTime;
|
* tMS = postedOnDate - foundationTime;
|
||||||
@ -24,21 +24,20 @@ function hotRank(timeValue, rank, headline) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.hotJSON = function(req, res, next) {
|
exports.hotJSON = function(req, res) {
|
||||||
var story = Story.find({}).sort({'timePosted': -1}).limit(1000);
|
var story = Story.find({}).sort({'timePosted': -1}).limit(1000);
|
||||||
story.exec(function(err, stories) {
|
story.exec(function(err, stories) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
res.send(500);
|
||||||
|
return next(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var foundationDate = 1413298800000;
|
var foundationDate = 1413298800000;
|
||||||
|
|
||||||
var sliceVal = stories.length >= 100 ? 100 : stories.length;
|
var sliceVal = stories.length >= 100 ? 100 : stories.length;
|
||||||
var rankedStories = stories;
|
return res.json(stories.map(function(elem) {
|
||||||
return res.json(rankedStories.map(function(elem) {
|
|
||||||
return elem;
|
return elem;
|
||||||
}).sort(function(a, b) {
|
}).sort(function(a, b) {
|
||||||
debug('a rank and b rank', hotRank(a.timePosted - foundationDate, a.rank, a.headline), hotRank(b.timePosted - foundationDate, b.rank, b.headline));
|
|
||||||
return hotRank(b.timePosted - foundationDate, b.rank, b.headline) - hotRank(a.timePosted - foundationDate, a.rank, a.headline);
|
return hotRank(b.timePosted - foundationDate, b.rank, b.headline) - hotRank(a.timePosted - foundationDate, a.rank, a.headline);
|
||||||
}).slice(0, sliceVal));
|
}).slice(0, sliceVal));
|
||||||
|
|
||||||
@ -49,48 +48,61 @@ exports.recentJSON = function(req, res, next) {
|
|||||||
var story = Story.find({}).sort({'timePosted': -1}).limit(100);
|
var story = Story.find({}).sort({'timePosted': -1}).limit(100);
|
||||||
story.exec(function(err, stories) {
|
story.exec(function(err, stories) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
res.status(500);
|
||||||
|
return next(err);
|
||||||
}
|
}
|
||||||
res.json(stories);
|
res.json(stories);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.hot = function(req, res, next) {
|
exports.hot = function(req, res) {
|
||||||
res.render('stories/index', {
|
res.render('stories/index', {
|
||||||
page: 'hot'
|
page: 'hot'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.submitNew = function(req,res, next) {
|
exports.submitNew = function(req, res) {
|
||||||
res.render('stories/index', {
|
res.render('stories/index', {
|
||||||
page: 'submit'
|
page: 'submit'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.search = function(req, res, next) {
|
exports.search = function(req, res) {
|
||||||
res.render('stories/index', {
|
res.render('stories/index', {
|
||||||
page: 'search'
|
page: 'search'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.recent = function(req, res, next) {
|
exports.recent = function(req, res) {
|
||||||
res.render('stories/index', {
|
res.render('stories/index', {
|
||||||
page: 'recent'
|
page: 'recent'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.preSubmit = function(req, res, next) {
|
exports.preSubmit = function(req, res) {
|
||||||
|
|
||||||
var data = req.params.newStory;
|
var data = req.query;
|
||||||
|
var cleanData = sanitizeHtml(data.url);
|
||||||
|
if (data.url.replace(/&/g, '&') !== cleanData) {
|
||||||
|
|
||||||
|
debug('data and cleandata', data, cleanData, data.url === cleanData);
|
||||||
|
req.flash('errors', {
|
||||||
|
msg: 'The data for this post is malformed'
|
||||||
|
});
|
||||||
|
return res.render('stories/index', {
|
||||||
|
page: 'stories/submit'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
data = data.replace(/url=/gi, '').replace(/&title=/gi, ',').split(',');
|
var title = data.title || '';
|
||||||
var url = data[0];
|
var image = data.image || '';
|
||||||
var title = data[1];
|
var description = data.description || '';
|
||||||
res.render('stories/index', {
|
return res.render('stories/index', {
|
||||||
page: 'storySubmission',
|
page: 'storySubmission',
|
||||||
storyURL: url,
|
storyURL: data.url,
|
||||||
storyTitle: title
|
storyTitle: title,
|
||||||
|
storyImage: image,
|
||||||
|
storyMetaDescription: description
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -132,15 +144,15 @@ exports.returnIndividualStory = function(req, res, next) {
|
|||||||
user: req.user,
|
user: req.user,
|
||||||
timeAgo: moment(story.timePosted).fromNow(),
|
timeAgo: moment(story.timePosted).fromNow(),
|
||||||
image: story.image,
|
image: story.image,
|
||||||
page: 'show'
|
page: 'show',
|
||||||
|
storyMetaDescription: story.metaDescription
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.getStories = function(req, res, next) {
|
exports.getStories = function(req, res) {
|
||||||
MongoClient.connect(secrets.db, function(err, database) {
|
MongoClient.connect(secrets.db, function(err, database) {
|
||||||
var db = database;
|
database.collection('stories').find({
|
||||||
db.collection('stories').find({
|
|
||||||
"$text": {
|
"$text": {
|
||||||
"$search": req.body.data.searchValue
|
"$search": req.body.data.searchValue
|
||||||
}
|
}
|
||||||
@ -155,6 +167,7 @@ exports.getStories = function(req, res, next) {
|
|||||||
comments: 1,
|
comments: 1,
|
||||||
image: 1,
|
image: 1,
|
||||||
storyLink: 1,
|
storyLink: 1,
|
||||||
|
metaDescription: 1,
|
||||||
textScore: {
|
textScore: {
|
||||||
$meta: "textScore"
|
$meta: "textScore"
|
||||||
}
|
}
|
||||||
@ -177,7 +190,8 @@ exports.upvote = function(req, res, next) {
|
|||||||
var data = req.body.data;
|
var data = req.body.data;
|
||||||
Story.find({'_id': data.id}, function(err, story) {
|
Story.find({'_id': data.id}, function(err, story) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
res.status(500);
|
||||||
|
return next(err);
|
||||||
}
|
}
|
||||||
story = story.pop();
|
story = story.pop();
|
||||||
story.rank++;
|
story.rank++;
|
||||||
@ -197,59 +211,68 @@ exports.comments = function(req, res, next) {
|
|||||||
var data = req.params.id;
|
var data = req.params.id;
|
||||||
Comment.find({'_id': data}, function(err, comment) {
|
Comment.find({'_id': data}, function(err, comment) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
res.status(500);
|
||||||
|
return next(err);
|
||||||
}
|
}
|
||||||
comment = comment.pop();
|
comment = comment.pop();
|
||||||
return res.send(comment);
|
return res.send(comment);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.newStory = function(req, res, next) {
|
exports.newStory = function(req, res) {
|
||||||
var url = req.body.data.url;
|
var url = req.body.data.url;
|
||||||
|
var cleanURL = sanitizeHtml(url);
|
||||||
|
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) {
|
if (url.search(/^https?:\/\//g) === -1) {
|
||||||
url = 'http://' + url;
|
url = 'http://' + url;
|
||||||
}
|
}
|
||||||
debug('In pre submit with a url', url);
|
|
||||||
|
|
||||||
Story.find({'link': url}, function(err, story) {
|
Story.find({'link': url}, function(err, story) {
|
||||||
debug('Attempting to find a story');
|
|
||||||
if (err) {
|
if (err) {
|
||||||
debug('oops');
|
|
||||||
return res.status(500);
|
return res.status(500);
|
||||||
}
|
}
|
||||||
if (story.length) {
|
if (story.length) {
|
||||||
debug('Found a story already, here\'s the return from find', story);
|
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: "Someone's already posted that link. Here's the discussion."
|
msg: "Someone's already posted that link. Here's the discussion."
|
||||||
});
|
});
|
||||||
debug('Redirecting the user with', story[0].storyLink);
|
|
||||||
return res.json({
|
return res.json({
|
||||||
alreadyPosted: true,
|
alreadyPosted: true,
|
||||||
storyURL: story.pop().storyLink
|
storyURL: '/stories/' + story.pop().storyLink
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
resources.getURLTitle(url, processResponse);
|
resources.getURLTitle(url, processResponse);
|
||||||
});
|
});
|
||||||
|
|
||||||
function processResponse(err, storyTitle) {
|
function processResponse(err, story) {
|
||||||
if (err) {
|
if (err) {
|
||||||
res.json({
|
res.json({
|
||||||
alreadyPosted: false,
|
alreadyPosted: false,
|
||||||
storyURL: url,
|
storyURL: url,
|
||||||
storyTitle: ''
|
storyTitle: '',
|
||||||
|
storyImage: '',
|
||||||
|
storyMetaDescription: ''
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
storyTitle = storyTitle ? storyTitle : '';
|
|
||||||
res.json({
|
res.json({
|
||||||
alreadyPosted: false,
|
alreadyPosted: false,
|
||||||
storyURL: url,
|
storyURL: url,
|
||||||
storyTitle: storyTitle.title
|
storyTitle: story.title,
|
||||||
|
storyImage: story.image,
|
||||||
|
storyMetaDescription: story.description
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.storySubmission = function(req, res, next) {
|
exports.storySubmission = function(req, res) {
|
||||||
var data = req.body.data;
|
var data = req.body.data;
|
||||||
var storyLink = data.headline
|
var storyLink = data.headline
|
||||||
.replace(/\'/g, '')
|
.replace(/\'/g, '')
|
||||||
@ -263,19 +286,20 @@ exports.storySubmission = function(req, res, next) {
|
|||||||
link = 'http://' + link;
|
link = 'http://' + link;
|
||||||
}
|
}
|
||||||
var story = new Story({
|
var story = new Story({
|
||||||
headline: data.headline,
|
headline: sanitizeHtml(data.headline),
|
||||||
timePosted: Date.now(),
|
timePosted: Date.now(),
|
||||||
link: link,
|
link: link,
|
||||||
description: data.description,
|
description: sanitizeHtml(data.description),
|
||||||
rank: 1,
|
rank: 1,
|
||||||
upVotes: data.upVotes,
|
upVotes: data.upVotes,
|
||||||
author: data.author,
|
author: data.author,
|
||||||
comments: [],
|
comments: [],
|
||||||
image: data.image,
|
image: data.image,
|
||||||
storyLink: storyLink
|
storyLink: storyLink,
|
||||||
|
metaDescription: data.storyMetaDescription
|
||||||
});
|
});
|
||||||
|
|
||||||
story.save(function(err, data) {
|
story.save(function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(500);
|
return res.status(500);
|
||||||
}
|
}
|
||||||
@ -285,12 +309,22 @@ exports.storySubmission = function(req, res, next) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.commentSubmit = function(req, res, next) {
|
exports.commentSubmit = function(req, res) {
|
||||||
debug('comment submit fired');
|
|
||||||
var data = req.body.data;
|
var data = req.body.data;
|
||||||
|
var sanitizedBody = sanitizeHtml(data.body,
|
||||||
|
{
|
||||||
|
allowedTags: [],
|
||||||
|
allowedAttributes: []
|
||||||
|
});
|
||||||
|
if (data.body !== sanitizedBody) {
|
||||||
|
req.flash('errors', {
|
||||||
|
msg: 'HTML is not allowed'
|
||||||
|
});
|
||||||
|
return res.send(true);
|
||||||
|
}
|
||||||
var comment = new Comment({
|
var comment = new Comment({
|
||||||
associatedPost: data.associatedPost,
|
associatedPost: data.associatedPost,
|
||||||
body: data.body,
|
body: sanitizedBody,
|
||||||
rank: 0,
|
rank: 0,
|
||||||
upvotes: 0,
|
upvotes: 0,
|
||||||
author: data.author,
|
author: data.author,
|
||||||
@ -301,13 +335,22 @@ exports.commentSubmit = function(req, res, next) {
|
|||||||
commentSave(comment, Story, res);
|
commentSave(comment, Story, res);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.commentOnCommentSubmit = function(req, res, next) {
|
exports.commentOnCommentSubmit = function(req, res) {
|
||||||
debug('comment on comment submit');
|
|
||||||
var idToFind = req.params.id;
|
|
||||||
var data = req.body.data;
|
var data = req.body.data;
|
||||||
|
var sanitizedBody = sanitizeHtml(data.body,
|
||||||
|
{
|
||||||
|
allowedTags: [],
|
||||||
|
allowedAttributes: []
|
||||||
|
});
|
||||||
|
if (data.body !== sanitizedBody) {
|
||||||
|
req.flash('errors', {
|
||||||
|
msg: 'HTML is not allowed'
|
||||||
|
});
|
||||||
|
return res.send(true);
|
||||||
|
}
|
||||||
var comment = new Comment({
|
var comment = new Comment({
|
||||||
associatedPost: data.associatedPost,
|
associatedPost: data.associatedPost,
|
||||||
body: data.body,
|
body: sanitizedBody,
|
||||||
rank: 0,
|
rank: 0,
|
||||||
upvotes: 0,
|
upvotes: 0,
|
||||||
author: data.author,
|
author: data.author,
|
||||||
@ -331,7 +374,7 @@ function commentSave(comment, Context, res) {
|
|||||||
associatedStory = associatedStory.pop();
|
associatedStory = associatedStory.pop();
|
||||||
if (associatedStory) {
|
if (associatedStory) {
|
||||||
associatedStory.comments.push(data._id);
|
associatedStory.comments.push(data._id);
|
||||||
associatedStory.save(function (err, data) {
|
associatedStory.save(function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
res.status(500);
|
res.status(500);
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@ var _ = require('lodash'),
|
|||||||
secrets = require('../config/secrets'),
|
secrets = require('../config/secrets'),
|
||||||
moment = require('moment'),
|
moment = require('moment'),
|
||||||
Challenge = require('./../models/Challenge'),
|
Challenge = require('./../models/Challenge'),
|
||||||
debug = require('debug')('freecc:cntr:challenges')
|
debug = require('debug')('freecc:cntr:challenges'),
|
||||||
resources = require('./resources');
|
resources = require('./resources');
|
||||||
|
|
||||||
//TODO(Berks): Refactor to use module.exports = {} pattern.
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /signin
|
* GET /signin
|
||||||
@ -18,10 +18,10 @@ var _ = require('lodash'),
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.getSignin = function(req, res) {
|
exports.getSignin = function(req, res) {
|
||||||
if (req.user) return res.redirect('/');
|
if (req.user) return res.redirect('/');
|
||||||
res.render('account/signin', {
|
res.render('account/signin', {
|
||||||
title: 'Free Code Camp Login'
|
title: 'Free Code Camp Login'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,28 +30,28 @@ exports.getSignin = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.postSignin = function(req, res, next) {
|
exports.postSignin = function(req, res, next) {
|
||||||
req.assert('email', 'Email is not valid').isEmail();
|
req.assert('email', 'Email is not valid').isEmail();
|
||||||
req.assert('password', 'Password cannot be blank').notEmpty();
|
req.assert('password', 'Password cannot be blank').notEmpty();
|
||||||
|
|
||||||
var errors = req.validationErrors();
|
var errors = req.validationErrors();
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
req.flash('errors', errors);
|
req.flash('errors', errors);
|
||||||
return res.redirect('/signin');
|
return res.redirect('/signin');
|
||||||
}
|
|
||||||
|
|
||||||
passport.authenticate('local', function(err, user, info) {
|
|
||||||
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);
|
passport.authenticate('local', function(err, user, info) {
|
||||||
req.flash('success', { msg: 'Success! You are logged in.' });
|
if (err) return next(err);
|
||||||
res.redirect(req.session.returnTo || '/');
|
if (!user) {
|
||||||
});
|
req.flash('errors', { msg: info.message });
|
||||||
})(req, res, next);
|
return res.redirect('/signin');
|
||||||
|
}
|
||||||
|
req.logIn(user, function(err) {
|
||||||
|
if (err) return next(err);
|
||||||
|
req.flash('success', { msg: 'Success! You are logged in.' });
|
||||||
|
res.redirect(req.session.returnTo || '/');
|
||||||
|
});
|
||||||
|
})(req, res, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,8 +60,8 @@ exports.postSignin = function(req, res, next) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.signout = function(req, res) {
|
exports.signout = function(req, res) {
|
||||||
req.logout();
|
req.logout();
|
||||||
res.redirect('/');
|
res.redirect('/');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,10 +70,10 @@ exports.signout = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.getEmailSignin = 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', {
|
res.render('account/email-signin', {
|
||||||
title: 'Sign in to your Free Code Camp Account'
|
title: 'Sign in to your Free Code Camp Account'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,10 +82,10 @@ exports.getEmailSignin = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.getEmailSignup = 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', {
|
res.render('account/email-signup', {
|
||||||
title: 'Create Your Free Code Camp Account'
|
title: 'Create Your Free Code Camp Account'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,64 +94,93 @@ exports.getEmailSignup = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.postEmailSignup = function(req, res, next) {
|
exports.postEmailSignup = function(req, res, next) {
|
||||||
var errors = req.validationErrors();
|
var errors = req.validationErrors();
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
req.flash('errors', errors);
|
req.flash('errors', errors);
|
||||||
return res.redirect('/email-signup');
|
return res.redirect('/email-signup');
|
||||||
debug(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
var user = new User({
|
|
||||||
email: req.body.email.trim(),
|
|
||||||
password: req.body.password,
|
|
||||||
profile : {
|
|
||||||
username: req.body.username.trim(),
|
|
||||||
picture: 'https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png'
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
User.findOne({ email: req.body.email }, function(err, existingUser) {
|
var possibleUserData = req.body;
|
||||||
if (err) { return next(err); }
|
|
||||||
|
|
||||||
if (existingUser) {
|
if (possibleUserData.password.length < 8) {
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: 'Account with that email address already exists.'
|
msg: 'Your password is too short'
|
||||||
});
|
});
|
||||||
return res.redirect('/email-signup');
|
return res.redirect('email-signup');
|
||||||
}
|
}
|
||||||
user.save(function(err) {
|
|
||||||
if (err) { return next(err); }
|
|
||||||
|
|
||||||
req.logIn(user, function(err) {
|
if (possibleUserData.username.length < 5 || possibleUserData.length > 20) {
|
||||||
if (err) { return next(err); }
|
req.flash('errors', {
|
||||||
res.redirect('/email-signup');
|
msg: 'Your username must be between 5 and 20 characters'
|
||||||
});
|
});
|
||||||
|
return res.redirect('email-signup');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var user = new User({
|
||||||
|
email: req.body.email.trim(),
|
||||||
|
password: req.body.password,
|
||||||
|
profile : {
|
||||||
|
username: req.body.username.trim(),
|
||||||
|
picture: 'https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
var transporter = nodemailer.createTransport({
|
|
||||||
service: 'Mandrill',
|
User.findOne({ email: req.body.email }, function(err, existingEmail) {
|
||||||
auth: {
|
if (err) {
|
||||||
user: secrets.mandrill.user,
|
return next(err);
|
||||||
pass: secrets.mandrill.password
|
}
|
||||||
}
|
|
||||||
|
if (existingEmail) {
|
||||||
|
req.flash('errors', {
|
||||||
|
msg: 'Account with that email address already exists.'
|
||||||
|
});
|
||||||
|
return res.redirect('/email-signup');
|
||||||
|
}
|
||||||
|
User.findOne({'profile.username': req.body.username }, function(err, existingUsername) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
if (existingUsername) {
|
||||||
|
req.flash('errors', {
|
||||||
|
msg: 'Account with that username already exists.'
|
||||||
|
});
|
||||||
|
return res.redirect('/email-signup');
|
||||||
|
}
|
||||||
|
|
||||||
|
user.save(function(err) {
|
||||||
|
if (err) { return next(err); }
|
||||||
|
req.logIn(user, function(err) {
|
||||||
|
if (err) { return next(err); }
|
||||||
|
res.redirect('/email-signup');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var transporter = nodemailer.createTransport({
|
||||||
|
service: 'Mandrill',
|
||||||
|
auth: {
|
||||||
|
user: secrets.mandrill.user,
|
||||||
|
pass: secrets.mandrill.password
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var mailOptions = {
|
||||||
|
to: user.email,
|
||||||
|
from: 'Team@freecodecamp.com',
|
||||||
|
subject: 'Welcome to Free Code Camp!',
|
||||||
|
text: [
|
||||||
|
'Greetings from San Francisco!\n\n',
|
||||||
|
'Thank you for joining our community.\n',
|
||||||
|
'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
|
||||||
|
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
|
||||||
|
'Good luck with the challenges!\n\n',
|
||||||
|
'- the Volunteer Camp Counselor Team'
|
||||||
|
].join('')
|
||||||
|
};
|
||||||
|
transporter.sendMail(mailOptions, function(err) {
|
||||||
|
if (err) { return err; }
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
var mailOptions = {
|
|
||||||
to: user.email,
|
|
||||||
from: 'Team@freecodecamp.com',
|
|
||||||
subject: 'Welcome to Free Code Camp!',
|
|
||||||
text: [
|
|
||||||
'Greetings from San Francisco!\n\n',
|
|
||||||
'Thank you for joining our community.\n',
|
|
||||||
'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
|
|
||||||
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
|
|
||||||
'Good luck with the challenges!\n\n',
|
|
||||||
'- the Volunteer Camp Counselor Team'
|
|
||||||
].join('')
|
|
||||||
};
|
|
||||||
transporter.sendMail(mailOptions, function(err) {
|
|
||||||
if (err) { return err; }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -161,7 +190,7 @@ exports.postEmailSignup = function(req, res, next) {
|
|||||||
|
|
||||||
exports.getAccount = function(req, res) {
|
exports.getAccount = function(req, res) {
|
||||||
res.render('account/account', {
|
res.render('account/account', {
|
||||||
title: 'Manage your Free Code Camp Account'
|
title: 'Manage your Free Code Camp Account'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -169,9 +198,9 @@ exports.getAccount = function(req, res) {
|
|||||||
* Angular API Call
|
* Angular API Call
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.getAccountAngular = function(req, res) {
|
exports.getAccountAngular = function(req, res) {
|
||||||
res.json({
|
res.json({
|
||||||
user: req.user
|
user: req.user
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -180,13 +209,13 @@ exports.getAccount = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.checkUniqueUsername = function(req, res) {
|
exports.checkUniqueUsername = function(req, res) {
|
||||||
User.count({'profile.username': req.params.username.toLowerCase()}, function (err, data) {
|
User.count({'profile.username': req.params.username.toLowerCase()}, function (err, data) {
|
||||||
if (data == 1) {
|
if (data == 1) {
|
||||||
return res.send(true);
|
return res.send(true);
|
||||||
} else {
|
} else {
|
||||||
return res.send(false);
|
return res.send(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,13 +236,13 @@ exports.checkExistingUsername = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.checkUniqueEmail = function(req, res) {
|
exports.checkUniqueEmail = function(req, res) {
|
||||||
User.count({'email': decodeURIComponent(req.params.email).toLowerCase()}, function (err, data) {
|
User.count({'email': decodeURIComponent(req.params.email).toLowerCase()}, function (err, data) {
|
||||||
if (data == 1) {
|
if (data == 1) {
|
||||||
return res.send(true);
|
return res.send(true);
|
||||||
} else {
|
} else {
|
||||||
return res.send(false);
|
return res.send(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -223,44 +252,44 @@ exports.checkUniqueEmail = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.returnUser = function(req, res, next) {
|
exports.returnUser = function(req, res, next) {
|
||||||
User.find({'profile.username': req.params.username.toLowerCase()}, function(err, user) {
|
User.find({'profile.username': req.params.username.toLowerCase()}, function(err, user) {
|
||||||
if (err) { debug('Username err: ', err); next(err); }
|
if (err) { debug('Username err: ', err); next(err); }
|
||||||
if (user[0]) {
|
if (user[0]) {
|
||||||
var user = user[0];
|
var user = user[0];
|
||||||
Challenge.find({}, null, {sort: {challengeNumber: 1}}, function (err, c) {
|
Challenge.find({}, null, {sort: {challengeNumber: 1}}, function (err, c) {
|
||||||
res.render('account/show', {
|
res.render('account/show', {
|
||||||
title: 'Camper: ',
|
title: 'Camper: ',
|
||||||
username: user.profile.username,
|
username: user.profile.username,
|
||||||
name: user.profile.name,
|
name: user.profile.name,
|
||||||
location: user.profile.location,
|
location: user.profile.location,
|
||||||
githubProfile: user.profile.githubProfile,
|
githubProfile: user.profile.githubProfile,
|
||||||
linkedinProfile: user.profile.linkedinProfile,
|
linkedinProfile: user.profile.linkedinProfile,
|
||||||
codepenProfile: user.profile.codepenProfile,
|
codepenProfile: user.profile.codepenProfile,
|
||||||
twitterHandle: user.profile.twitterHandle,
|
twitterHandle: user.profile.twitterHandle,
|
||||||
bio: user.profile.bio,
|
bio: user.profile.bio,
|
||||||
picture: user.profile.picture,
|
picture: user.profile.picture,
|
||||||
points: user.points,
|
points: user.points,
|
||||||
website1Link: user.portfolio.website1Link,
|
website1Link: user.portfolio.website1Link,
|
||||||
website1Title: user.portfolio.website1Title,
|
website1Title: user.portfolio.website1Title,
|
||||||
website1Image: user.portfolio.website1Image,
|
website1Image: user.portfolio.website1Image,
|
||||||
website2Link: user.portfolio.website2Link,
|
website2Link: user.portfolio.website2Link,
|
||||||
website2Title: user.portfolio.website2Title,
|
website2Title: user.portfolio.website2Title,
|
||||||
website2Image: user.portfolio.website2Image,
|
website2Image: user.portfolio.website2Image,
|
||||||
website3Link: user.portfolio.website3Link,
|
website3Link: user.portfolio.website3Link,
|
||||||
website3Title: user.portfolio.website3Title,
|
website3Title: user.portfolio.website3Title,
|
||||||
website3Image: user.portfolio.website3Image,
|
website3Image: user.portfolio.website3Image,
|
||||||
challenges: c,
|
challenges: c,
|
||||||
ch: user.challengesHash,
|
ch: user.challengesHash,
|
||||||
moment: moment
|
moment: moment
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: "404: We couldn't find a page with that url. Please double check the link."
|
msg: "404: We couldn't find a page with that url. Please double check the link."
|
||||||
});
|
});
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -292,69 +321,70 @@ exports.updateProgress = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.postUpdateProfile = function(req, res, next) {
|
exports.postUpdateProfile = function(req, res, next) {
|
||||||
User.findById(req.user.id, function(err, user) {
|
|
||||||
if (err) return next(err);
|
|
||||||
var errors = req.validationErrors();
|
|
||||||
if (errors) {
|
|
||||||
req.flash('errors', errors);
|
|
||||||
return res.redirect('/account');
|
|
||||||
}
|
|
||||||
|
|
||||||
User.findOne({ email: req.body.email }, function(err, existingEmail) {
|
// What does this do?
|
||||||
if (err) {
|
User.findById(req.user.id, function(err, user) {
|
||||||
return next(err);
|
if (err) return next(err);
|
||||||
}
|
var errors = req.validationErrors();
|
||||||
var user = req.user;
|
if (errors) {
|
||||||
if (existingEmail && existingEmail.email != user.email) {
|
req.flash('errors', errors);
|
||||||
req.flash('errors', {
|
return res.redirect('/account');
|
||||||
msg: "An account with that email address already exists."
|
|
||||||
});
|
|
||||||
return res.redirect('/account');
|
|
||||||
}
|
|
||||||
User.findOne({ username: req.body.username }, function(err, existingUsername) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
}
|
||||||
var user = req.user;
|
|
||||||
if (existingUsername && existingUsername.profile.username !== user.profile.username) {
|
|
||||||
req.flash('errors', {
|
|
||||||
msg: 'An account with that username already exists.'
|
|
||||||
});
|
|
||||||
return res.redirect('/account');
|
|
||||||
}
|
|
||||||
var user = req.user;
|
|
||||||
user.email = req.body.email.trim() || '';
|
|
||||||
user.profile.name = req.body.name.trim() || '';
|
|
||||||
user.profile.username = req.body.username.trim() || '';
|
|
||||||
user.profile.location = req.body.location.trim() || '';
|
|
||||||
user.profile.githubProfile = req.body.githubProfile.trim() || '';
|
|
||||||
user.profile.linkedinProfile = req.body.linkedinProfile.trim() || '';
|
|
||||||
user.profile.codepenProfile = req.body.codepenProfile.trim() || '';
|
|
||||||
user.profile.twitterHandle = req.body.twitterHandle.trim() || '';
|
|
||||||
user.profile.bio = req.body.bio.trim() || '';
|
|
||||||
user.profile.picture = req.body.picture.trim() || 'https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png';
|
|
||||||
user.portfolio.website1Title = req.body.website1Title.trim() || '';
|
|
||||||
user.portfolio.website1Link = req.body.website1Link.trim() || '';
|
|
||||||
user.portfolio.website1Image = req.body.website1Image.trim() || '';
|
|
||||||
user.portfolio.website2Title = req.body.website2Title.trim() || '';
|
|
||||||
user.portfolio.website2Link = req.body.website2Link.trim() || '';
|
|
||||||
user.portfolio.website2Image = req.body.website2Image.trim() || '';
|
|
||||||
user.portfolio.website3Title = req.body.website3Title.trim() || '';
|
|
||||||
user.portfolio.website3Link = req.body.website3Link.trim() || '';
|
|
||||||
user.portfolio.website3Image = req.body.website3Image.trim() || '';
|
|
||||||
|
|
||||||
|
User.findOne({ email: req.body.email }, function(err, existingEmail) {
|
||||||
user.save(function (err) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
req.flash('success', {msg: 'Profile information updated.'});
|
var user = req.user;
|
||||||
res.redirect('/account');
|
if (existingEmail && existingEmail.email != user.email) {
|
||||||
resources.updateUserStoryPictures(user._id.toString(), user.profile.picture, user.profile.username);
|
req.flash('errors', {
|
||||||
|
msg: "An account with that email address already exists."
|
||||||
|
});
|
||||||
|
return res.redirect('/account');
|
||||||
|
}
|
||||||
|
User.findOne({ username: req.body.username }, function(err, existingUsername) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
var user = req.user;
|
||||||
|
if (existingUsername && existingUsername.profile.username !== user.profile.username) {
|
||||||
|
req.flash('errors', {
|
||||||
|
msg: 'An account with that username already exists.'
|
||||||
|
});
|
||||||
|
return res.redirect('/account');
|
||||||
|
}
|
||||||
|
user.email = req.body.email.trim() || '';
|
||||||
|
user.profile.name = req.body.name.trim() || '';
|
||||||
|
user.profile.username = req.body.username.trim() || '';
|
||||||
|
user.profile.location = req.body.location.trim() || '';
|
||||||
|
user.profile.githubProfile = req.body.githubProfile.trim() || '';
|
||||||
|
user.profile.linkedinProfile = req.body.linkedinProfile.trim() || '';
|
||||||
|
user.profile.codepenProfile = req.body.codepenProfile.trim() || '';
|
||||||
|
user.profile.twitterHandle = req.body.twitterHandle.trim() || '';
|
||||||
|
user.profile.bio = req.body.bio.trim() || '';
|
||||||
|
user.profile.picture = req.body.picture.trim() || 'https://s3.amazonaws.com/freecodecamp/favicons/apple-touch-icon-180x180.png';
|
||||||
|
user.portfolio.website1Title = req.body.website1Title.trim() || '';
|
||||||
|
user.portfolio.website1Link = req.body.website1Link.trim() || '';
|
||||||
|
user.portfolio.website1Image = req.body.website1Image.trim() || '';
|
||||||
|
user.portfolio.website2Title = req.body.website2Title.trim() || '';
|
||||||
|
user.portfolio.website2Link = req.body.website2Link.trim() || '';
|
||||||
|
user.portfolio.website2Image = req.body.website2Image.trim() || '';
|
||||||
|
user.portfolio.website3Title = req.body.website3Title.trim() || '';
|
||||||
|
user.portfolio.website3Link = req.body.website3Link.trim() || '';
|
||||||
|
user.portfolio.website3Image = req.body.website3Image.trim() || '';
|
||||||
|
|
||||||
|
|
||||||
|
user.save(function (err) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
req.flash('success', {msg: 'Profile information updated.'});
|
||||||
|
res.redirect('/account');
|
||||||
|
resources.updateUserStoryPictures(user._id.toString(), user.profile.picture, user.profile.username);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -363,29 +393,29 @@ exports.postUpdateProfile = function(req, res, next) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.postUpdatePassword = function(req, res, next) {
|
exports.postUpdatePassword = function(req, res, next) {
|
||||||
req.assert('password', 'Password must be at least 4 characters long').len(4);
|
req.assert('password', 'Password must be at least 4 characters long').len(4);
|
||||||
req.assert('confirmPassword', 'Passwords do not match')
|
req.assert('confirmPassword', 'Passwords do not match')
|
||||||
.equals(req.body.password);
|
.equals(req.body.password);
|
||||||
|
|
||||||
var errors = req.validationErrors();
|
var errors = req.validationErrors();
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
req.flash('errors', errors);
|
req.flash('errors', errors);
|
||||||
return res.redirect('/account');
|
return res.redirect('/account');
|
||||||
}
|
}
|
||||||
|
|
||||||
User.findById(req.user.id, function(err, user) {
|
User.findById(req.user.id, function(err, user) {
|
||||||
if (err) { return next(err); }
|
if (err) { return next(err); }
|
||||||
|
|
||||||
user.password = req.body.password;
|
user.password = req.body.password;
|
||||||
|
|
||||||
user.save(function(err) {
|
user.save(function(err) {
|
||||||
if (err) { return next(err); }
|
if (err) { return next(err); }
|
||||||
|
|
||||||
req.flash('success', { msg: 'Password has been changed.' });
|
req.flash('success', { msg: 'Password has been changed.' });
|
||||||
res.redirect('/account');
|
res.redirect('/account');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -394,12 +424,12 @@ exports.postUpdatePassword = function(req, res, next) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.postDeleteAccount = function(req, res, next) {
|
exports.postDeleteAccount = function(req, res, next) {
|
||||||
User.remove({ _id: req.user.id }, function(err) {
|
User.remove({ _id: req.user.id }, function(err) {
|
||||||
if (err) { return next(err); }
|
if (err) { return next(err); }
|
||||||
req.logout();
|
req.logout();
|
||||||
req.flash('info', { msg: 'Your account has been deleted.' });
|
req.flash('info', { msg: 'Your account has been deleted.' });
|
||||||
res.redirect('/');
|
res.redirect('/');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -408,22 +438,22 @@ exports.postDeleteAccount = function(req, res, next) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.getOauthUnlink = function(req, res, next) {
|
exports.getOauthUnlink = function(req, res, next) {
|
||||||
var provider = req.params.provider;
|
var provider = req.params.provider;
|
||||||
User.findById(req.user.id, function(err, user) {
|
User.findById(req.user.id, function(err, user) {
|
||||||
if (err) { return next(err); }
|
if (err) { return next(err); }
|
||||||
|
|
||||||
user[provider] = undefined;
|
user[provider] = undefined;
|
||||||
user.tokens =
|
user.tokens =
|
||||||
_.reject(user.tokens, function(token) {
|
_.reject(user.tokens, function(token) {
|
||||||
return token.kind === provider;
|
return token.kind === provider;
|
||||||
});
|
});
|
||||||
|
|
||||||
user.save(function(err) {
|
user.save(function(err) {
|
||||||
if (err) { return next(err); }
|
if (err) { return next(err); }
|
||||||
req.flash('info', { msg: provider + ' account has been unlinked.' });
|
req.flash('info', { msg: provider + ' account has been unlinked.' });
|
||||||
res.redirect('/account');
|
res.redirect('/account');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -432,25 +462,25 @@ exports.getOauthUnlink = function(req, res, next) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.getReset = function(req, res) {
|
exports.getReset = function(req, res) {
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
User
|
User
|
||||||
.findOne({ resetPasswordToken: req.params.token })
|
.findOne({ resetPasswordToken: req.params.token })
|
||||||
.where('resetPasswordExpires').gt(Date.now())
|
.where('resetPasswordExpires').gt(Date.now())
|
||||||
.exec(function(err, user) {
|
.exec(function(err, user) {
|
||||||
if (err) { return next(err); }
|
if (err) { return next(err); }
|
||||||
if (!user) {
|
if (!user) {
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: 'Password reset token is invalid or has expired.'
|
msg: 'Password reset token is invalid or has expired.'
|
||||||
|
});
|
||||||
|
return res.redirect('/forgot');
|
||||||
|
}
|
||||||
|
res.render('account/reset', {
|
||||||
|
title: 'Password Reset',
|
||||||
|
token: req.params.token
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return res.redirect('/forgot');
|
|
||||||
}
|
|
||||||
res.render('account/reset', {
|
|
||||||
title: 'Password Reset',
|
|
||||||
token: req.params.token
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -459,72 +489,72 @@ exports.getReset = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.postReset = function(req, res, next) {
|
exports.postReset = function(req, res, next) {
|
||||||
var errors = req.validationErrors();
|
var errors = req.validationErrors();
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
req.flash('errors', errors);
|
req.flash('errors', errors);
|
||||||
return res.redirect('back');
|
return res.redirect('back');
|
||||||
}
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
function(done) {
|
|
||||||
User
|
|
||||||
.findOne({ resetPasswordToken: req.params.token })
|
|
||||||
.where('resetPasswordExpires').gt(Date.now())
|
|
||||||
.exec(function(err, user) {
|
|
||||||
if (err) { return next(err); }
|
|
||||||
if (!user) {
|
|
||||||
req.flash('errors', {
|
|
||||||
msg: 'Password reset token is invalid or has expired.'
|
|
||||||
});
|
|
||||||
return res.redirect('back');
|
|
||||||
}
|
|
||||||
|
|
||||||
user.password = req.body.password;
|
|
||||||
user.resetPasswordToken = undefined;
|
|
||||||
user.resetPasswordExpires = undefined;
|
|
||||||
|
|
||||||
user.save(function(err) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
req.logIn(user, function(err) {
|
|
||||||
done(err, user);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function(user, done) {
|
|
||||||
var transporter = nodemailer.createTransport({
|
|
||||||
service: 'Mandrill',
|
|
||||||
auth: {
|
|
||||||
user: secrets.mandrill.user,
|
|
||||||
pass: secrets.mandrill.password
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var mailOptions = {
|
|
||||||
to: user.email,
|
|
||||||
from: 'Team@freecodecamp.com',
|
|
||||||
subject: 'Your Free Code Camp password has been changed',
|
|
||||||
text: [
|
|
||||||
'Hello,\n\n',
|
|
||||||
'This email is confirming that you requested to',
|
|
||||||
'reset your password for your Free Code Camp account.',
|
|
||||||
'This is your email:',
|
|
||||||
user.email,
|
|
||||||
'\n'
|
|
||||||
].join(' ')
|
|
||||||
};
|
|
||||||
transporter.sendMail(mailOptions, function(err) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
req.flash('success', {
|
|
||||||
msg: 'Success! Your password has been changed.'
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
], function(err) {
|
|
||||||
if (err) { return next(err); }
|
async.waterfall([
|
||||||
res.redirect('/');
|
function(done) {
|
||||||
});
|
User
|
||||||
|
.findOne({ resetPasswordToken: req.params.token })
|
||||||
|
.where('resetPasswordExpires').gt(Date.now())
|
||||||
|
.exec(function(err, user) {
|
||||||
|
if (err) { return next(err); }
|
||||||
|
if (!user) {
|
||||||
|
req.flash('errors', {
|
||||||
|
msg: 'Password reset token is invalid or has expired.'
|
||||||
|
});
|
||||||
|
return res.redirect('back');
|
||||||
|
}
|
||||||
|
|
||||||
|
user.password = req.body.password;
|
||||||
|
user.resetPasswordToken = undefined;
|
||||||
|
user.resetPasswordExpires = undefined;
|
||||||
|
|
||||||
|
user.save(function(err) {
|
||||||
|
if (err) { return done(err); }
|
||||||
|
req.logIn(user, function(err) {
|
||||||
|
done(err, user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(user, done) {
|
||||||
|
var transporter = nodemailer.createTransport({
|
||||||
|
service: 'Mandrill',
|
||||||
|
auth: {
|
||||||
|
user: secrets.mandrill.user,
|
||||||
|
pass: secrets.mandrill.password
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var mailOptions = {
|
||||||
|
to: user.email,
|
||||||
|
from: 'Team@freecodecamp.com',
|
||||||
|
subject: 'Your Free Code Camp password has been changed',
|
||||||
|
text: [
|
||||||
|
'Hello,\n\n',
|
||||||
|
'This email is confirming that you requested to',
|
||||||
|
'reset your password for your Free Code Camp account.',
|
||||||
|
'This is your email:',
|
||||||
|
user.email,
|
||||||
|
'\n'
|
||||||
|
].join(' ')
|
||||||
|
};
|
||||||
|
transporter.sendMail(mailOptions, function(err) {
|
||||||
|
if (err) { return done(err); }
|
||||||
|
req.flash('success', {
|
||||||
|
msg: 'Success! Your password has been changed.'
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
], function(err) {
|
||||||
|
if (err) { return next(err); }
|
||||||
|
res.redirect('/');
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -533,12 +563,12 @@ exports.postReset = function(req, res, next) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.getForgot = function(req, res) {
|
exports.getForgot = function(req, res) {
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
return res.redirect('/');
|
return res.redirect('/');
|
||||||
}
|
}
|
||||||
res.render('account/forgot', {
|
res.render('account/forgot', {
|
||||||
title: 'Forgot Password'
|
title: 'Forgot Password'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -547,80 +577,80 @@ exports.getForgot = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.postForgot = function(req, res, next) {
|
exports.postForgot = function(req, res, next) {
|
||||||
var errors = req.validationErrors();
|
var errors = req.validationErrors();
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
req.flash('errors', errors);
|
req.flash('errors', errors);
|
||||||
return res.redirect('/forgot');
|
return res.redirect('/forgot');
|
||||||
}
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
function(done) {
|
|
||||||
crypto.randomBytes(16, function(err, buf) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
var token = buf.toString('hex');
|
|
||||||
done(null, token);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function(token, done) {
|
|
||||||
User.findOne({
|
|
||||||
email: req.body.email.toLowerCase()
|
|
||||||
}, function(err, user) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
if (!user) {
|
|
||||||
req.flash('errors', {
|
|
||||||
msg: 'No account with that email address exists.'
|
|
||||||
});
|
|
||||||
return res.redirect('/forgot');
|
|
||||||
}
|
|
||||||
|
|
||||||
user.resetPasswordToken = token;
|
|
||||||
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
|
|
||||||
|
|
||||||
user.save(function(err) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
done(null, token, user);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function(token, user, done) {
|
|
||||||
var transporter = nodemailer.createTransport({
|
|
||||||
service: 'Mandrill',
|
|
||||||
auth: {
|
|
||||||
user: secrets.mandrill.user,
|
|
||||||
pass: secrets.mandrill.password
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var mailOptions = {
|
|
||||||
to: user.email,
|
|
||||||
from: 'Team@freecodecamp.com',
|
|
||||||
subject: 'Reset your Free Code Camp password',
|
|
||||||
text: [
|
|
||||||
'You are receiving this email because you (or someone else)\n',
|
|
||||||
'requested we reset your Free Code Camp account\'s password.\n\n',
|
|
||||||
'Please click on the following link, or paste this into your\n',
|
|
||||||
'browser to complete the process:\n\n',
|
|
||||||
'http://',
|
|
||||||
req.headers.host,
|
|
||||||
'/reset/',
|
|
||||||
token,
|
|
||||||
'\n\n',
|
|
||||||
'If you did not request this, please ignore this email and\n',
|
|
||||||
'your password will remain unchanged.\n'
|
|
||||||
].join('')
|
|
||||||
};
|
|
||||||
transporter.sendMail(mailOptions, function(err) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
req.flash('info', {
|
|
||||||
msg: 'An e-mail has been sent to ' +
|
|
||||||
user.email +
|
|
||||||
' with further instructions.'
|
|
||||||
});
|
|
||||||
done(null, 'done');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
], function(err) {
|
|
||||||
if (err) { return next(err); }
|
async.waterfall([
|
||||||
res.redirect('/forgot');
|
function(done) {
|
||||||
});
|
crypto.randomBytes(16, function(err, buf) {
|
||||||
|
if (err) { return done(err); }
|
||||||
|
var token = buf.toString('hex');
|
||||||
|
done(null, token);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(token, done) {
|
||||||
|
User.findOne({
|
||||||
|
email: req.body.email.toLowerCase()
|
||||||
|
}, function(err, user) {
|
||||||
|
if (err) { return done(err); }
|
||||||
|
if (!user) {
|
||||||
|
req.flash('errors', {
|
||||||
|
msg: 'No account with that email address exists.'
|
||||||
|
});
|
||||||
|
return res.redirect('/forgot');
|
||||||
|
}
|
||||||
|
|
||||||
|
user.resetPasswordToken = token;
|
||||||
|
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
|
||||||
|
|
||||||
|
user.save(function(err) {
|
||||||
|
if (err) { return done(err); }
|
||||||
|
done(null, token, user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(token, user, done) {
|
||||||
|
var transporter = nodemailer.createTransport({
|
||||||
|
service: 'Mandrill',
|
||||||
|
auth: {
|
||||||
|
user: secrets.mandrill.user,
|
||||||
|
pass: secrets.mandrill.password
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var mailOptions = {
|
||||||
|
to: user.email,
|
||||||
|
from: 'Team@freecodecamp.com',
|
||||||
|
subject: 'Reset your Free Code Camp password',
|
||||||
|
text: [
|
||||||
|
'You are receiving this email because you (or someone else)\n',
|
||||||
|
'requested we reset your Free Code Camp account\'s password.\n\n',
|
||||||
|
'Please click on the following link, or paste this into your\n',
|
||||||
|
'browser to complete the process:\n\n',
|
||||||
|
'http://',
|
||||||
|
req.headers.host,
|
||||||
|
'/reset/',
|
||||||
|
token,
|
||||||
|
'\n\n',
|
||||||
|
'If you did not request this, please ignore this email and\n',
|
||||||
|
'your password will remain unchanged.\n'
|
||||||
|
].join('')
|
||||||
|
};
|
||||||
|
transporter.sendMail(mailOptions, function(err) {
|
||||||
|
if (err) { return done(err); }
|
||||||
|
req.flash('info', {
|
||||||
|
msg: 'An e-mail has been sent to ' +
|
||||||
|
user.email +
|
||||||
|
' with further instructions.'
|
||||||
|
});
|
||||||
|
done(null, 'done');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
], function(err) {
|
||||||
|
if (err) { return next(err); }
|
||||||
|
res.redirect('/forgot');
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -14,6 +14,11 @@ var storySchema = new mongoose.Schema({
|
|||||||
type: String,
|
type: String,
|
||||||
unique: false
|
unique: false
|
||||||
},
|
},
|
||||||
|
metaDescription: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
unique: false
|
||||||
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
unique: false
|
unique: false
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
"passport-twitter": "^1.0.2",
|
"passport-twitter": "^1.0.2",
|
||||||
"ramda": "^0.10.0",
|
"ramda": "^0.10.0",
|
||||||
"request": "^2.53.0",
|
"request": "^2.53.0",
|
||||||
|
"sanitize-html": "^1.6.1",
|
||||||
"sitemap": "^0.7.4",
|
"sitemap": "^0.7.4",
|
||||||
"uglify-js": "^2.4.15",
|
"uglify-js": "^2.4.15",
|
||||||
"validator": "^3.22.1",
|
"validator": "^3.22.1",
|
||||||
|
@ -821,6 +821,11 @@ iframe.iphone {
|
|||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.url-preview {
|
||||||
|
max-width: 250px;
|
||||||
|
max-height: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
//.media ~ .media .media-body-wrapper:nth-child(odd) {
|
//.media ~ .media .media-body-wrapper:nth-child(odd) {
|
||||||
// background-color: #e5e5e5;
|
// background-color: #e5e5e5;
|
||||||
//}
|
//}
|
||||||
|
@ -164,7 +164,7 @@ $(document).ready(function() {
|
|||||||
headline: headline,
|
headline: headline,
|
||||||
timePosted: Date.now(),
|
timePosted: Date.now(),
|
||||||
description: description,
|
description: description,
|
||||||
|
storyMetaDescription: storyMetaDescription,
|
||||||
rank: 1,
|
rank: 1,
|
||||||
upVotes: [userDataForUpvote],
|
upVotes: [userDataForUpvote],
|
||||||
author: {
|
author: {
|
||||||
@ -173,7 +173,7 @@ $(document).ready(function() {
|
|||||||
username: user.profile.username
|
username: user.profile.username
|
||||||
},
|
},
|
||||||
comments: [],
|
comments: [],
|
||||||
image: ''
|
image: storyImage
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fail(function (xhr, textStatus, errorThrown) {
|
.fail(function (xhr, textStatus, errorThrown) {
|
||||||
|
@ -40,7 +40,7 @@ block content
|
|||||||
| Your usernames must be 20 characters or fewer.
|
| Your usernames must be 20 characters or fewer.
|
||||||
.form-group
|
.form-group
|
||||||
.col-sm-6.col-sm-offset-3
|
.col-sm-6.col-sm-offset-3
|
||||||
input.form-control(type='password', ng-model='password', name='password', id='password', placeholder='password', required, ng-minlength=5)
|
input.form-control(type='password', ng-model='password', name='password', id='password', placeholder='password', required, ng-minlength=8)
|
||||||
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.password.$error.minlength && !signupForm.password.$pristine")
|
.col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.password.$error.minlength && !signupForm.password.$pristine")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
|
@ -34,11 +34,13 @@
|
|||||||
})
|
})
|
||||||
.done(function (data, textStatus, xhr) {
|
.done(function (data, textStatus, xhr) {
|
||||||
if (data.alreadyPosted) {
|
if (data.alreadyPosted) {
|
||||||
window.location = '/stories/' + data.storyURL;
|
window.location = data.storyURL;
|
||||||
} else {
|
} else {
|
||||||
window.location = '/stories/submit/url=' +
|
window.location = '/stories/submit/new-story?url=' +
|
||||||
encodeURIComponent(data.storyURL) +
|
encodeURIComponent(data.storyURL) +
|
||||||
'&title=' + encodeURIComponent(data.storyTitle);
|
'&title=' + encodeURIComponent(data.storyTitle) +
|
||||||
|
'&image=' + encodeURIComponent(data.storyImage) +
|
||||||
|
'&description=' + encodeURIComponent(data.storyMetaDescription);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
var comments = !{JSON.stringify(comments)};
|
var comments = !{JSON.stringify(comments)};
|
||||||
var upVotes = !{JSON.stringify(upVotes)};
|
var upVotes = !{JSON.stringify(upVotes)};
|
||||||
var user = !{JSON.stringify(user)};
|
var user = !{JSON.stringify(user)};
|
||||||
|
var image = !{JSON.stringify(image)};
|
||||||
.spacer
|
.spacer
|
||||||
|
|
||||||
h3.row.col-xs-12
|
h3.row.col-xs-12
|
||||||
@ -25,7 +26,14 @@
|
|||||||
a(href="#{link}")
|
a(href="#{link}")
|
||||||
h3= title
|
h3= title
|
||||||
h6
|
h6
|
||||||
.col-xs-12.negative-28
|
.col-xs-12.positive-15.hidden-element#image-display
|
||||||
|
.media
|
||||||
|
.media-left
|
||||||
|
img.url-preview.media-object(src="#{image}", alt="#{storyMetaDescription}")
|
||||||
|
.media-body
|
||||||
|
.col-xs-12.col-sm-12.col-md-6
|
||||||
|
h4= storyMetaDescription
|
||||||
|
.col-xs-12
|
||||||
h4= description
|
h4= description
|
||||||
.negative-5
|
.negative-5
|
||||||
span Posted #{timeAgo}
|
span Posted #{timeAgo}
|
||||||
@ -44,6 +52,9 @@
|
|||||||
span.spacer.pull-left#textarea_feedback
|
span.spacer.pull-left#textarea_feedback
|
||||||
|
|
||||||
script.
|
script.
|
||||||
|
if (image) {
|
||||||
|
$('#image-display').removeClass('hidden-element')
|
||||||
|
}
|
||||||
$('#reply-to-main-post').on('click', function() {
|
$('#reply-to-main-post').on('click', function() {
|
||||||
$('#initial-comment-submit').removeClass('hidden-element');
|
$('#initial-comment-submit').removeClass('hidden-element');
|
||||||
$(this).unbind('click');
|
$(this).unbind('click');
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
script.
|
script.
|
||||||
var storyURL = !{JSON.stringify(storyURL)};
|
var storyURL = !{JSON.stringify(storyURL)};
|
||||||
var storyTitle = !{JSON.stringify(storyTitle)};
|
var storyTitle = !{JSON.stringify(storyTitle)};
|
||||||
|
var storyImage = !{JSON.stringify(storyImage)};
|
||||||
|
var storyMetaDescription = !{JSON.stringify(storyMetaDescription)};
|
||||||
form.form-horizontal.control-label-story-submission#story-submission-form
|
form.form-horizontal.control-label-story-submission#story-submission-form
|
||||||
.col-xs-12
|
.col-xs-12
|
||||||
.form-group
|
.form-group
|
||||||
@ -20,13 +22,29 @@
|
|||||||
label.control-label.control-label-story-submission(for='name') Description
|
label.control-label.control-label-story-submission(for='name') Description
|
||||||
.col-xs-12.col-md-11
|
.col-xs-12.col-md-11
|
||||||
input#description-box.form-control(name="comment-box", placeholder="Start off the discussion with a description of your post" maxlength='140')
|
input#description-box.form-control(name="comment-box", placeholder="Start off the discussion with a description of your post" maxlength='140')
|
||||||
span.pull-left#textarea_feedback
|
|
||||||
.spacer
|
|
||||||
.form-group
|
.form-group
|
||||||
button.btn.btn-big.btn-block.btn-primary#story-submit Submit
|
.col-xs-12.col-md-offset-1
|
||||||
|
span.pull-left#textarea_feedback
|
||||||
|
.form-group
|
||||||
|
.col-xs-11.col-md-offset-1
|
||||||
|
.hidden-element#image-display
|
||||||
|
.media
|
||||||
|
.media-left
|
||||||
|
img.url-preview.media-object(src="#{storyImage}", alt="#{storyMetaDescription}")
|
||||||
|
.media-body
|
||||||
|
.col-xs-12
|
||||||
|
p= storyMetaDescription
|
||||||
|
.spacer
|
||||||
|
.row
|
||||||
|
.form-group
|
||||||
|
|
||||||
|
button.btn.btn-big.btn-block.btn-primary#story-submit Submit
|
||||||
script.
|
script.
|
||||||
$('#story-url').val(storyURL).attr('disabled', 'disabled');
|
$('#story-url').val(storyURL).attr('disabled', 'disabled');
|
||||||
$('#story-title').val(storyTitle);
|
$('#story-title').val(storyTitle);
|
||||||
|
if (storyImage) {
|
||||||
|
$('#image-display').removeClass('hidden-element');
|
||||||
|
}
|
||||||
var text_max = 140;
|
var text_max = 140;
|
||||||
$('#textarea_feedback').html(text_max + ' characters remaining');
|
$('#textarea_feedback').html(text_max + ' characters remaining');
|
||||||
$('#description-box').keyup(function () {
|
$('#description-box').keyup(function () {
|
||||||
|
Reference in New Issue
Block a user