2
app.js
2
app.js
@ -324,7 +324,7 @@ app.get(
|
||||
);
|
||||
|
||||
app.get(
|
||||
'/stories/submit/:newStory',
|
||||
'/stories/submit/new-story',
|
||||
storyController.preSubmit
|
||||
);
|
||||
|
||||
|
@ -4,7 +4,6 @@ var User = require('../models/User'),
|
||||
Story = require('./../models/Story'),
|
||||
Comment = require('./../models/Comment'),
|
||||
resources = require('./resources.json'),
|
||||
questions = resources.questions,
|
||||
steps = resources.steps,
|
||||
secrets = require('./../config/secrets'),
|
||||
bonfires = require('../seed_data/bonfires.json'),
|
||||
@ -154,7 +153,7 @@ module.exports = {
|
||||
},
|
||||
bloggerCalls: function(req, res) {
|
||||
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({
|
||||
blog1Title: blog ? blog["items"][0]["title"] : "Can't connect to Blogger",
|
||||
blog1Link: blog ? blog["items"][0]["url"] : "http://blog.freecodecamp.com",
|
||||
@ -225,10 +224,6 @@ module.exports = {
|
||||
return compliments[Math.floor(Math.random() * compliments.length)];
|
||||
},
|
||||
|
||||
numberOfBonfires: function() {
|
||||
return bonfires.length - 1;
|
||||
},
|
||||
|
||||
allBonfireIds: function() {
|
||||
return bonfires.map(function(elem) {
|
||||
return {
|
||||
@ -290,14 +285,19 @@ module.exports = {
|
||||
return process.env.NODE_ENV;
|
||||
},
|
||||
getURLTitle: function(url, callback) {
|
||||
|
||||
debug('got url in meta scraping function', url);
|
||||
(function () {
|
||||
var result = {title: ''};
|
||||
var result = {title: '', image: '', url: '', description: ''};
|
||||
request(url, function (error, response, body) {
|
||||
if (!error && response.statusCode === 200) {
|
||||
var $ = cheerio.load(body);
|
||||
var title = $('title').text();
|
||||
result.title = title;
|
||||
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();
|
||||
result.image = urlImage;
|
||||
result.description = description;
|
||||
callback(null, result);
|
||||
} else {
|
||||
callback('failed');
|
||||
|
@ -8,9 +8,9 @@ var R = require('ramda'),
|
||||
mongodb = require('mongodb'),
|
||||
MongoClient = mongodb.MongoClient,
|
||||
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
|
||||
* 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);
|
||||
story.exec(function(err, stories) {
|
||||
if (err) {
|
||||
throw err;
|
||||
res.send(500);
|
||||
return next(err);
|
||||
}
|
||||
|
||||
var foundationDate = 1413298800000;
|
||||
|
||||
var sliceVal = stories.length >= 100 ? 100 : stories.length;
|
||||
var rankedStories = stories;
|
||||
return res.json(rankedStories.map(function(elem) {
|
||||
return res.json(stories.map(function(elem) {
|
||||
return elem;
|
||||
}).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);
|
||||
}).slice(0, sliceVal));
|
||||
|
||||
@ -49,48 +48,61 @@ exports.recentJSON = function(req, res, next) {
|
||||
var story = Story.find({}).sort({'timePosted': -1}).limit(100);
|
||||
story.exec(function(err, stories) {
|
||||
if (err) {
|
||||
throw err;
|
||||
res.status(500);
|
||||
return next(err);
|
||||
}
|
||||
res.json(stories);
|
||||
});
|
||||
};
|
||||
|
||||
exports.hot = function(req, res, next) {
|
||||
exports.hot = function(req, res) {
|
||||
res.render('stories/index', {
|
||||
page: 'hot'
|
||||
});
|
||||
};
|
||||
|
||||
exports.submitNew = function(req,res, next) {
|
||||
exports.submitNew = function(req, res) {
|
||||
res.render('stories/index', {
|
||||
page: 'submit'
|
||||
});
|
||||
};
|
||||
|
||||
exports.search = function(req, res, next) {
|
||||
exports.search = function(req, res) {
|
||||
res.render('stories/index', {
|
||||
page: 'search'
|
||||
});
|
||||
};
|
||||
|
||||
exports.recent = function(req, res, next) {
|
||||
exports.recent = function(req, res) {
|
||||
res.render('stories/index', {
|
||||
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 url = data[0];
|
||||
var title = data[1];
|
||||
res.render('stories/index', {
|
||||
var title = data.title || '';
|
||||
var image = data.image || '';
|
||||
var description = data.description || '';
|
||||
return res.render('stories/index', {
|
||||
page: 'storySubmission',
|
||||
storyURL: url,
|
||||
storyTitle: title
|
||||
storyURL: data.url,
|
||||
storyTitle: title,
|
||||
storyImage: image,
|
||||
storyMetaDescription: description
|
||||
});
|
||||
};
|
||||
|
||||
@ -132,15 +144,15 @@ exports.returnIndividualStory = function(req, res, next) {
|
||||
user: req.user,
|
||||
timeAgo: moment(story.timePosted).fromNow(),
|
||||
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) {
|
||||
var db = database;
|
||||
db.collection('stories').find({
|
||||
database.collection('stories').find({
|
||||
"$text": {
|
||||
"$search": req.body.data.searchValue
|
||||
}
|
||||
@ -155,6 +167,7 @@ exports.getStories = function(req, res, next) {
|
||||
comments: 1,
|
||||
image: 1,
|
||||
storyLink: 1,
|
||||
metaDescription: 1,
|
||||
textScore: {
|
||||
$meta: "textScore"
|
||||
}
|
||||
@ -177,7 +190,8 @@ exports.upvote = function(req, res, next) {
|
||||
var data = req.body.data;
|
||||
Story.find({'_id': data.id}, function(err, story) {
|
||||
if (err) {
|
||||
throw err;
|
||||
res.status(500);
|
||||
return next(err);
|
||||
}
|
||||
story = story.pop();
|
||||
story.rank++;
|
||||
@ -197,59 +211,68 @@ exports.comments = function(req, res, next) {
|
||||
var data = req.params.id;
|
||||
Comment.find({'_id': data}, function(err, comment) {
|
||||
if (err) {
|
||||
throw err;
|
||||
res.status(500);
|
||||
return next(err);
|
||||
}
|
||||
comment = comment.pop();
|
||||
return res.send(comment);
|
||||
});
|
||||
};
|
||||
|
||||
exports.newStory = function(req, res, next) {
|
||||
exports.newStory = function(req, res) {
|
||||
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) {
|
||||
url = 'http://' + url;
|
||||
}
|
||||
debug('In pre submit with a url', url);
|
||||
|
||||
Story.find({'link': url}, function(err, story) {
|
||||
debug('Attempting to find a story');
|
||||
if (err) {
|
||||
debug('oops');
|
||||
return res.status(500);
|
||||
}
|
||||
if (story.length) {
|
||||
debug('Found a story already, here\'s the return from find', story);
|
||||
req.flash('errors', {
|
||||
msg: "Someone's already posted that link. Here's the discussion."
|
||||
});
|
||||
debug('Redirecting the user with', story[0].storyLink);
|
||||
return res.json({
|
||||
alreadyPosted: true,
|
||||
storyURL: story.pop().storyLink
|
||||
storyURL: '/stories/' + story.pop().storyLink
|
||||
});
|
||||
}
|
||||
resources.getURLTitle(url, processResponse);
|
||||
});
|
||||
|
||||
function processResponse(err, storyTitle) {
|
||||
function processResponse(err, story) {
|
||||
if (err) {
|
||||
res.json({
|
||||
alreadyPosted: false,
|
||||
storyURL: url,
|
||||
storyTitle: ''
|
||||
storyTitle: '',
|
||||
storyImage: '',
|
||||
storyMetaDescription: ''
|
||||
});
|
||||
} else {
|
||||
storyTitle = storyTitle ? storyTitle : '';
|
||||
res.json({
|
||||
alreadyPosted: false,
|
||||
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 storyLink = data.headline
|
||||
.replace(/\'/g, '')
|
||||
@ -263,19 +286,20 @@ exports.storySubmission = function(req, res, next) {
|
||||
link = 'http://' + link;
|
||||
}
|
||||
var story = new Story({
|
||||
headline: data.headline,
|
||||
headline: sanitizeHtml(data.headline),
|
||||
timePosted: Date.now(),
|
||||
link: link,
|
||||
description: data.description,
|
||||
description: sanitizeHtml(data.description),
|
||||
rank: 1,
|
||||
upVotes: data.upVotes,
|
||||
author: data.author,
|
||||
comments: [],
|
||||
image: data.image,
|
||||
storyLink: storyLink
|
||||
storyLink: storyLink,
|
||||
metaDescription: data.storyMetaDescription
|
||||
});
|
||||
|
||||
story.save(function(err, data) {
|
||||
story.save(function(err) {
|
||||
if (err) {
|
||||
return res.status(500);
|
||||
}
|
||||
@ -285,12 +309,22 @@ exports.storySubmission = function(req, res, next) {
|
||||
});
|
||||
};
|
||||
|
||||
exports.commentSubmit = function(req, res, next) {
|
||||
debug('comment submit fired');
|
||||
exports.commentSubmit = function(req, res) {
|
||||
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({
|
||||
associatedPost: data.associatedPost,
|
||||
body: data.body,
|
||||
body: sanitizedBody,
|
||||
rank: 0,
|
||||
upvotes: 0,
|
||||
author: data.author,
|
||||
@ -301,13 +335,22 @@ exports.commentSubmit = function(req, res, next) {
|
||||
commentSave(comment, Story, res);
|
||||
};
|
||||
|
||||
exports.commentOnCommentSubmit = function(req, res, next) {
|
||||
debug('comment on comment submit');
|
||||
var idToFind = req.params.id;
|
||||
exports.commentOnCommentSubmit = function(req, res) {
|
||||
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({
|
||||
associatedPost: data.associatedPost,
|
||||
body: data.body,
|
||||
body: sanitizedBody,
|
||||
rank: 0,
|
||||
upvotes: 0,
|
||||
author: data.author,
|
||||
@ -331,7 +374,7 @@ function commentSave(comment, Context, res) {
|
||||
associatedStory = associatedStory.pop();
|
||||
if (associatedStory) {
|
||||
associatedStory.comments.push(data._id);
|
||||
associatedStory.save(function (err, data) {
|
||||
associatedStory.save(function (err) {
|
||||
if (err) {
|
||||
res.status(500);
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ var _ = require('lodash'),
|
||||
secrets = require('../config/secrets'),
|
||||
moment = require('moment'),
|
||||
Challenge = require('./../models/Challenge'),
|
||||
debug = require('debug')('freecc:cntr:challenges')
|
||||
debug = require('debug')('freecc:cntr:challenges'),
|
||||
resources = require('./resources');
|
||||
|
||||
//TODO(Berks): Refactor to use module.exports = {} pattern.
|
||||
|
||||
|
||||
/**
|
||||
* GET /signin
|
||||
@ -18,10 +18,10 @@ var _ = require('lodash'),
|
||||
*/
|
||||
|
||||
exports.getSignin = function(req, res) {
|
||||
if (req.user) return res.redirect('/');
|
||||
res.render('account/signin', {
|
||||
title: 'Free Code Camp Login'
|
||||
});
|
||||
if (req.user) return res.redirect('/');
|
||||
res.render('account/signin', {
|
||||
title: 'Free Code Camp Login'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -30,28 +30,28 @@ exports.getSignin = function(req, res) {
|
||||
*/
|
||||
|
||||
exports.postSignin = function(req, res, next) {
|
||||
req.assert('email', 'Email is not valid').isEmail();
|
||||
req.assert('password', 'Password cannot be blank').notEmpty();
|
||||
req.assert('email', 'Email is not valid').isEmail();
|
||||
req.assert('password', 'Password cannot be blank').notEmpty();
|
||||
|
||||
var errors = req.validationErrors();
|
||||
var errors = req.validationErrors();
|
||||
|
||||
if (errors) {
|
||||
req.flash('errors', errors);
|
||||
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');
|
||||
if (errors) {
|
||||
req.flash('errors', errors);
|
||||
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);
|
||||
|
||||
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);
|
||||
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) {
|
||||
req.logout();
|
||||
res.redirect('/');
|
||||
req.logout();
|
||||
res.redirect('/');
|
||||
};
|
||||
|
||||
/**
|
||||
@ -70,10 +70,10 @@ exports.signout = function(req, res) {
|
||||
*/
|
||||
|
||||
exports.getEmailSignin = function(req, res) {
|
||||
if (req.user) return res.redirect('/');
|
||||
res.render('account/email-signin', {
|
||||
title: 'Sign in to your Free Code Camp Account'
|
||||
});
|
||||
if (req.user) return res.redirect('/');
|
||||
res.render('account/email-signin', {
|
||||
title: 'Sign in to your Free Code Camp Account'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -82,10 +82,10 @@ exports.getEmailSignin = function(req, res) {
|
||||
*/
|
||||
|
||||
exports.getEmailSignup = function(req, res) {
|
||||
if (req.user) return res.redirect('/');
|
||||
res.render('account/email-signup', {
|
||||
title: 'Create Your Free Code Camp Account'
|
||||
});
|
||||
if (req.user) return res.redirect('/');
|
||||
res.render('account/email-signup', {
|
||||
title: 'Create Your Free Code Camp Account'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -94,64 +94,93 @@ exports.getEmailSignup = function(req, res) {
|
||||
*/
|
||||
|
||||
exports.postEmailSignup = function(req, res, next) {
|
||||
var errors = req.validationErrors();
|
||||
var errors = req.validationErrors();
|
||||
|
||||
if (errors) {
|
||||
req.flash('errors', errors);
|
||||
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'
|
||||
if (errors) {
|
||||
req.flash('errors', errors);
|
||||
return res.redirect('/email-signup');
|
||||
}
|
||||
});
|
||||
|
||||
User.findOne({ email: req.body.email }, function(err, existingUser) {
|
||||
if (err) { return next(err); }
|
||||
var possibleUserData = req.body;
|
||||
|
||||
if (existingUser) {
|
||||
req.flash('errors', {
|
||||
msg: 'Account with that email address already exists.'
|
||||
});
|
||||
return res.redirect('/email-signup');
|
||||
if (possibleUserData.password.length < 8) {
|
||||
req.flash('errors', {
|
||||
msg: 'Your password is too short'
|
||||
});
|
||||
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');
|
||||
});
|
||||
if (possibleUserData.username.length < 5 || possibleUserData.length > 20) {
|
||||
req.flash('errors', {
|
||||
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',
|
||||
auth: {
|
||||
user: secrets.mandrill.user,
|
||||
pass: secrets.mandrill.password
|
||||
}
|
||||
|
||||
User.findOne({ email: req.body.email }, function(err, existingEmail) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
*/
|
||||
|
||||
exports.getAccountAngular = function(req, res) {
|
||||
exports.getAccountAngular = function(req, res) {
|
||||
res.json({
|
||||
user: req.user
|
||||
user: req.user
|
||||
});
|
||||
};
|
||||
|
||||
@ -180,13 +209,13 @@ exports.getAccount = function(req, res) {
|
||||
*/
|
||||
|
||||
exports.checkUniqueUsername = function(req, res) {
|
||||
User.count({'profile.username': req.params.username.toLowerCase()}, function (err, data) {
|
||||
if (data == 1) {
|
||||
return res.send(true);
|
||||
} else {
|
||||
return res.send(false);
|
||||
}
|
||||
});
|
||||
User.count({'profile.username': req.params.username.toLowerCase()}, function (err, data) {
|
||||
if (data == 1) {
|
||||
return res.send(true);
|
||||
} else {
|
||||
return res.send(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -207,13 +236,13 @@ exports.checkExistingUsername = function(req, res) {
|
||||
*/
|
||||
|
||||
exports.checkUniqueEmail = function(req, res) {
|
||||
User.count({'email': decodeURIComponent(req.params.email).toLowerCase()}, function (err, data) {
|
||||
if (data == 1) {
|
||||
return res.send(true);
|
||||
} else {
|
||||
return res.send(false);
|
||||
}
|
||||
});
|
||||
User.count({'email': decodeURIComponent(req.params.email).toLowerCase()}, function (err, data) {
|
||||
if (data == 1) {
|
||||
return res.send(true);
|
||||
} else {
|
||||
return res.send(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -223,44 +252,44 @@ exports.checkUniqueEmail = function(req, res) {
|
||||
*/
|
||||
|
||||
exports.returnUser = function(req, res, next) {
|
||||
User.find({'profile.username': req.params.username.toLowerCase()}, function(err, user) {
|
||||
if (err) { debug('Username err: ', err); next(err); }
|
||||
if (user[0]) {
|
||||
var user = user[0];
|
||||
Challenge.find({}, null, {sort: {challengeNumber: 1}}, function (err, c) {
|
||||
res.render('account/show', {
|
||||
title: 'Camper: ',
|
||||
username: user.profile.username,
|
||||
name: user.profile.name,
|
||||
location: user.profile.location,
|
||||
githubProfile: user.profile.githubProfile,
|
||||
linkedinProfile: user.profile.linkedinProfile,
|
||||
codepenProfile: user.profile.codepenProfile,
|
||||
twitterHandle: user.profile.twitterHandle,
|
||||
bio: user.profile.bio,
|
||||
picture: user.profile.picture,
|
||||
points: user.points,
|
||||
website1Link: user.portfolio.website1Link,
|
||||
website1Title: user.portfolio.website1Title,
|
||||
website1Image: user.portfolio.website1Image,
|
||||
website2Link: user.portfolio.website2Link,
|
||||
website2Title: user.portfolio.website2Title,
|
||||
website2Image: user.portfolio.website2Image,
|
||||
website3Link: user.portfolio.website3Link,
|
||||
website3Title: user.portfolio.website3Title,
|
||||
website3Image: user.portfolio.website3Image,
|
||||
challenges: c,
|
||||
ch: user.challengesHash,
|
||||
moment: moment
|
||||
});
|
||||
});
|
||||
} else {
|
||||
req.flash('errors', {
|
||||
msg: "404: We couldn't find a page with that url. Please double check the link."
|
||||
});
|
||||
return res.redirect('/');
|
||||
}
|
||||
});
|
||||
User.find({'profile.username': req.params.username.toLowerCase()}, function(err, user) {
|
||||
if (err) { debug('Username err: ', err); next(err); }
|
||||
if (user[0]) {
|
||||
var user = user[0];
|
||||
Challenge.find({}, null, {sort: {challengeNumber: 1}}, function (err, c) {
|
||||
res.render('account/show', {
|
||||
title: 'Camper: ',
|
||||
username: user.profile.username,
|
||||
name: user.profile.name,
|
||||
location: user.profile.location,
|
||||
githubProfile: user.profile.githubProfile,
|
||||
linkedinProfile: user.profile.linkedinProfile,
|
||||
codepenProfile: user.profile.codepenProfile,
|
||||
twitterHandle: user.profile.twitterHandle,
|
||||
bio: user.profile.bio,
|
||||
picture: user.profile.picture,
|
||||
points: user.points,
|
||||
website1Link: user.portfolio.website1Link,
|
||||
website1Title: user.portfolio.website1Title,
|
||||
website1Image: user.portfolio.website1Image,
|
||||
website2Link: user.portfolio.website2Link,
|
||||
website2Title: user.portfolio.website2Title,
|
||||
website2Image: user.portfolio.website2Image,
|
||||
website3Link: user.portfolio.website3Link,
|
||||
website3Title: user.portfolio.website3Title,
|
||||
website3Image: user.portfolio.website3Image,
|
||||
challenges: c,
|
||||
ch: user.challengesHash,
|
||||
moment: moment
|
||||
});
|
||||
});
|
||||
} else {
|
||||
req.flash('errors', {
|
||||
msg: "404: We couldn't find a page with that url. Please double check the link."
|
||||
});
|
||||
return res.redirect('/');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -292,69 +321,70 @@ exports.updateProgress = function(req, res) {
|
||||
*/
|
||||
|
||||
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) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
var user = req.user;
|
||||
if (existingEmail && existingEmail.email != user.email) {
|
||||
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);
|
||||
// What does this do?
|
||||
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');
|
||||
}
|
||||
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.save(function (err) {
|
||||
User.findOne({ email: req.body.email }, function(err, existingEmail) {
|
||||
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);
|
||||
var user = req.user;
|
||||
if (existingEmail && existingEmail.email != user.email) {
|
||||
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) {
|
||||
req.assert('password', 'Password must be at least 4 characters long').len(4);
|
||||
req.assert('confirmPassword', 'Passwords do not match')
|
||||
.equals(req.body.password);
|
||||
req.assert('password', 'Password must be at least 4 characters long').len(4);
|
||||
req.assert('confirmPassword', 'Passwords do not match')
|
||||
.equals(req.body.password);
|
||||
|
||||
var errors = req.validationErrors();
|
||||
var errors = req.validationErrors();
|
||||
|
||||
if (errors) {
|
||||
req.flash('errors', errors);
|
||||
return res.redirect('/account');
|
||||
}
|
||||
if (errors) {
|
||||
req.flash('errors', errors);
|
||||
return res.redirect('/account');
|
||||
}
|
||||
|
||||
User.findById(req.user.id, function(err, user) {
|
||||
if (err) { return next(err); }
|
||||
User.findById(req.user.id, function(err, user) {
|
||||
if (err) { return next(err); }
|
||||
|
||||
user.password = req.body.password;
|
||||
user.password = req.body.password;
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
user.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
|
||||
req.flash('success', { msg: 'Password has been changed.' });
|
||||
res.redirect('/account');
|
||||
req.flash('success', { msg: 'Password has been changed.' });
|
||||
res.redirect('/account');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -394,12 +424,12 @@ exports.postUpdatePassword = function(req, res, next) {
|
||||
*/
|
||||
|
||||
exports.postDeleteAccount = function(req, res, next) {
|
||||
User.remove({ _id: req.user.id }, function(err) {
|
||||
if (err) { return next(err); }
|
||||
req.logout();
|
||||
req.flash('info', { msg: 'Your account has been deleted.' });
|
||||
res.redirect('/');
|
||||
});
|
||||
User.remove({ _id: req.user.id }, function(err) {
|
||||
if (err) { return next(err); }
|
||||
req.logout();
|
||||
req.flash('info', { msg: 'Your account has been deleted.' });
|
||||
res.redirect('/');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -408,22 +438,22 @@ exports.postDeleteAccount = function(req, res, next) {
|
||||
*/
|
||||
|
||||
exports.getOauthUnlink = function(req, res, next) {
|
||||
var provider = req.params.provider;
|
||||
User.findById(req.user.id, function(err, user) {
|
||||
if (err) { return next(err); }
|
||||
var provider = req.params.provider;
|
||||
User.findById(req.user.id, function(err, user) {
|
||||
if (err) { return next(err); }
|
||||
|
||||
user[provider] = undefined;
|
||||
user.tokens =
|
||||
_.reject(user.tokens, function(token) {
|
||||
return token.kind === provider;
|
||||
});
|
||||
user[provider] = undefined;
|
||||
user.tokens =
|
||||
_.reject(user.tokens, function(token) {
|
||||
return token.kind === provider;
|
||||
});
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
req.flash('info', { msg: provider + ' account has been unlinked.' });
|
||||
res.redirect('/account');
|
||||
user.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
req.flash('info', { msg: provider + ' account has been unlinked.' });
|
||||
res.redirect('/account');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -432,25 +462,25 @@ exports.getOauthUnlink = function(req, res, next) {
|
||||
*/
|
||||
|
||||
exports.getReset = function(req, res) {
|
||||
if (req.isAuthenticated()) {
|
||||
return res.redirect('/');
|
||||
}
|
||||
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.'
|
||||
if (req.isAuthenticated()) {
|
||||
return res.redirect('/');
|
||||
}
|
||||
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('/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) {
|
||||
var errors = req.validationErrors();
|
||||
var errors = req.validationErrors();
|
||||
|
||||
if (errors) {
|
||||
req.flash('errors', errors);
|
||||
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();
|
||||
});
|
||||
if (errors) {
|
||||
req.flash('errors', errors);
|
||||
return res.redirect('back');
|
||||
}
|
||||
], function(err) {
|
||||
if (err) { return next(err); }
|
||||
res.redirect('/');
|
||||
});
|
||||
|
||||
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); }
|
||||
res.redirect('/');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -533,12 +563,12 @@ exports.postReset = function(req, res, next) {
|
||||
*/
|
||||
|
||||
exports.getForgot = function(req, res) {
|
||||
if (req.isAuthenticated()) {
|
||||
return res.redirect('/');
|
||||
}
|
||||
res.render('account/forgot', {
|
||||
title: 'Forgot Password'
|
||||
});
|
||||
if (req.isAuthenticated()) {
|
||||
return res.redirect('/');
|
||||
}
|
||||
res.render('account/forgot', {
|
||||
title: 'Forgot Password'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -547,80 +577,80 @@ exports.getForgot = function(req, res) {
|
||||
*/
|
||||
|
||||
exports.postForgot = function(req, res, next) {
|
||||
var errors = req.validationErrors();
|
||||
var errors = req.validationErrors();
|
||||
|
||||
if (errors) {
|
||||
req.flash('errors', errors);
|
||||
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');
|
||||
});
|
||||
if (errors) {
|
||||
req.flash('errors', errors);
|
||||
return res.redirect('/forgot');
|
||||
}
|
||||
], function(err) {
|
||||
if (err) { return next(err); }
|
||||
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); }
|
||||
res.redirect('/forgot');
|
||||
});
|
||||
};
|
||||
|
@ -14,6 +14,11 @@ var storySchema = new mongoose.Schema({
|
||||
type: String,
|
||||
unique: false
|
||||
},
|
||||
metaDescription: {
|
||||
type: String,
|
||||
default: '',
|
||||
unique: false
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
unique: false
|
||||
|
@ -61,6 +61,7 @@
|
||||
"passport-twitter": "^1.0.2",
|
||||
"ramda": "^0.10.0",
|
||||
"request": "^2.53.0",
|
||||
"sanitize-html": "^1.6.1",
|
||||
"sitemap": "^0.7.4",
|
||||
"uglify-js": "^2.4.15",
|
||||
"validator": "^3.22.1",
|
||||
|
@ -821,6 +821,11 @@ iframe.iphone {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.url-preview {
|
||||
max-width: 250px;
|
||||
max-height: 250px;
|
||||
}
|
||||
|
||||
//.media ~ .media .media-body-wrapper:nth-child(odd) {
|
||||
// background-color: #e5e5e5;
|
||||
//}
|
||||
|
@ -164,7 +164,7 @@ $(document).ready(function() {
|
||||
headline: headline,
|
||||
timePosted: Date.now(),
|
||||
description: description,
|
||||
|
||||
storyMetaDescription: storyMetaDescription,
|
||||
rank: 1,
|
||||
upVotes: [userDataForUpvote],
|
||||
author: {
|
||||
@ -173,7 +173,7 @@ $(document).ready(function() {
|
||||
username: user.profile.username
|
||||
},
|
||||
comments: [],
|
||||
image: ''
|
||||
image: storyImage
|
||||
}
|
||||
})
|
||||
.fail(function (xhr, textStatus, errorThrown) {
|
||||
|
@ -40,7 +40,7 @@ block content
|
||||
| Your usernames must be 20 characters or fewer.
|
||||
.form-group
|
||||
.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")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
|
@ -34,11 +34,13 @@
|
||||
})
|
||||
.done(function (data, textStatus, xhr) {
|
||||
if (data.alreadyPosted) {
|
||||
window.location = '/stories/' + data.storyURL;
|
||||
window.location = data.storyURL;
|
||||
} else {
|
||||
window.location = '/stories/submit/url=' +
|
||||
window.location = '/stories/submit/new-story?url=' +
|
||||
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 upVotes = !{JSON.stringify(upVotes)};
|
||||
var user = !{JSON.stringify(user)};
|
||||
var image = !{JSON.stringify(image)};
|
||||
.spacer
|
||||
|
||||
h3.row.col-xs-12
|
||||
@ -25,7 +26,14 @@
|
||||
a(href="#{link}")
|
||||
h3= title
|
||||
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
|
||||
.negative-5
|
||||
span Posted #{timeAgo}
|
||||
@ -44,6 +52,9 @@
|
||||
span.spacer.pull-left#textarea_feedback
|
||||
|
||||
script.
|
||||
if (image) {
|
||||
$('#image-display').removeClass('hidden-element')
|
||||
}
|
||||
$('#reply-to-main-post').on('click', function() {
|
||||
$('#initial-comment-submit').removeClass('hidden-element');
|
||||
$(this).unbind('click');
|
||||
|
@ -3,6 +3,8 @@
|
||||
script.
|
||||
var storyURL = !{JSON.stringify(storyURL)};
|
||||
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
|
||||
.col-xs-12
|
||||
.form-group
|
||||
@ -20,13 +22,29 @@
|
||||
label.control-label.control-label-story-submission(for='name') Description
|
||||
.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')
|
||||
span.pull-left#textarea_feedback
|
||||
.spacer
|
||||
.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.
|
||||
$('#story-url').val(storyURL).attr('disabled', 'disabled');
|
||||
$('#story-title').val(storyTitle);
|
||||
if (storyImage) {
|
||||
$('#image-display').removeClass('hidden-element');
|
||||
}
|
||||
var text_max = 140;
|
||||
$('#textarea_feedback').html(text_max + ' characters remaining');
|
||||
$('#description-box').keyup(function () {
|
||||
|
Reference in New Issue
Block a user