diff --git a/controllers/bonfire.js b/controllers/bonfire.js index 88678099e1..68f22d9532 100644 --- a/controllers/bonfire.js +++ b/controllers/bonfire.js @@ -326,7 +326,6 @@ exports.completedBonfire = function (req, res, next) { return next(err); } if (user) { - debug('Saving user'); res.send(true); } }); diff --git a/controllers/fieldGuide.js b/controllers/fieldGuide.js index d4bf9a216d..51f01e8529 100644 --- a/controllers/fieldGuide.js +++ b/controllers/fieldGuide.js @@ -22,7 +22,7 @@ exports.returnIndividualFieldGuide = function(req, res, next) { return res.redirect('/field-guide'); } - var fieldGuide = fieldGuideFromMongo.pop(); + var fieldGuide = R.head(fieldGuideFromMongo); var dashedNameFull = fieldGuide.name.toLowerCase().replace(/\s/g, '-').replace(/\?/g, ''); if (dashedNameFull !== dashedName) { return res.redirect('../field-guide/' + dashedNameFull); @@ -54,15 +54,14 @@ exports.returnNextFieldGuide = function(req, res, next) { var completed = req.user.completedFieldGuides; - req.user.uncompletedFieldGuides = resources.allFieldGuideIds().filter(function (elem) { + var uncompletedFieldGuides = resources.allFieldGuideIds().filter(function (elem) { if (completed.indexOf(elem) === -1) { return elem; } }); + req.user.uncompletedFieldGuides = uncompletedFieldGuides; req.user.save(); - var uncompletedFieldGuides = req.user.uncompletedFieldGuides; - var displayedFieldGuides = FieldGuide.find({'_id': uncompletedFieldGuides[0]}); displayedFieldGuides.exec(function(err, fieldGuide) { if (err) { @@ -81,14 +80,13 @@ exports.returnNextFieldGuide = function(req, res, next) { }; exports.completedFieldGuide = function (req, res, next) { - debug('params in completedFieldGuide', req.params); var fieldGuideId = req.body.fieldGuideInfo.fieldGuideId; req.user.completedFieldGuides.push(fieldGuideId); var index = req.user.uncompletedFieldGuides.indexOf(fieldGuideId); if (index > -1) { - req.user.progressTimestamps.push(Date.now() || 0); + req.user.progressTimestamps.push(Date.now()); req.user.uncompletedFieldGuides.splice(index, 1); } diff --git a/controllers/nonprofits.js b/controllers/nonprofits.js index a7871327a3..d3b824cf3e 100644 --- a/controllers/nonprofits.js +++ b/controllers/nonprofits.js @@ -132,7 +132,6 @@ exports.returnIndividualNonprofit = function(req, res, next) { var hasShownInterest = nonprofit.interestedCampers.filter(function ( obj ) { return obj.username === req.user.profile.username; }); - console.log(hasShownInterest); if (hasShownInterest.length === 0) { buttonActive = true; } diff --git a/controllers/resources.js b/controllers/resources.js index d9e9450c6f..29e38c67c8 100644 --- a/controllers/resources.js +++ b/controllers/resources.js @@ -261,9 +261,7 @@ module.exports = { } else { allFieldGuideIds = fieldGuides. map(function (elem) { - return { - _id: elem._id - }; + return elem._id; }); return allFieldGuideIds; } diff --git a/controllers/story.js b/controllers/story.js index 9047279e3f..1f1bf90247 100755 --- a/controllers/story.js +++ b/controllers/story.js @@ -118,7 +118,7 @@ exports.preSubmit = function(req, res) { exports.returnIndividualStory = function(req, res, next) { var dashedName = req.params.storyName; - var storyName = dashedName.replace(/\-/g, ' '); + var storyName = dashedName.replace(/\-/g, ' ').trim(); Story.find({'storyLink': storyName}, function(err, story) { if (err) { @@ -155,7 +155,7 @@ exports.returnIndividualStory = function(req, res, next) { title: story.headline, link: story.link, originalStoryLink: dashedName, - originalStoryAuthorEmail: story.author.email || "", + originalStoryAuthorEmail: story.author.email || '', author: story.author, description: story.description, rank: story.upVotes.length, @@ -321,9 +321,10 @@ exports.storySubmission = function(req, res, next) { .replace(/\'/g, '') .replace(/\"/g, '') .replace(/,/g, '') - .replace(/[^a-z0-9]/gi, ' ') .replace(/\s+/g, ' ') - .toLowerCase(); + .replace(/[^a-z0-9\s]/gi, '') + .toLowerCase() + .trim(); var link = data.link; if (link.search(/^https?:\/\//g) === -1) { link = 'http://' + link; @@ -334,7 +335,7 @@ exports.storySubmission = function(req, res, next) { } // if duplicate storyLink add unique number - storyLink = (storyCount == 0) ? storyLink : storyLink + ' ' + storyCount; + storyLink = (storyCount === 0) ? storyLink : storyLink + ' ' + storyCount; var link = data.link; if (link.search(/^https?:\/\//g) === -1) { @@ -398,7 +399,7 @@ exports.storySubmission = function(req, res, next) { var comment = new Comment({ associatedPost: data.associatedPost, originalStoryLink: data.originalStoryLink, - originalStoryAuthorEmail: req.user.email, + originalStoryAuthorEmail: data.originalStoryAuthorEmail, body: sanitizedBody, rank: 0, upvotes: 0, @@ -496,53 +497,54 @@ exports.storySubmission = function(req, res, next) { return next(err); } try { - Context.find({'_id': comment.associatedPost}, function (err, associatedStory) { + // Based on the context retrieve the parent object of the comment (Story/Comment) + Context.find({'_id': data.associatedPost}, function (err, associatedContext) { if (err) { return next(err); } - associatedStory = associatedStory.pop(); - if (associatedStory) { - associatedStory.comments.push(data._id); - associatedStory.save(function (err) { + associatedContext = associatedContext.pop(); + if (associatedContext) { + associatedContext.comments.push(data._id); + associatedContext.save(function (err) { if (err) { return next(err); } res.send(true); }); } - User.findOne({'profile.username': associatedStory.author.username}, function(err, recipient) { + // Find the author of the parent object + User.findOne({'profile.username': associatedContext.author.username}, function(err, recipient) { if (err) { return next(err); } - var recipients = ''; - if (data.originalStoryAuthorEmail && (data.originalStoryAuthorEmail !== recipient.email)) { - recipients = data.originalStoryAuthorEmail + ',' + recipient.email; - } else { - recipients = recipient.email; + // If the emails of both authors differ, only then proceed with email notification + if (data.author.email && (data.author.email !== recipient.email)) { + var transporter = nodemailer.createTransport({ + service: 'Mandrill', + auth: { + user: secrets.mandrill.user, + pass: secrets.mandrill.password + } + }); + + var mailOptions = { + to: recipient.email, + from: 'Team@freecodecamp.com', + subject: data.author.username + ' replied to your post on Camper News', + text: [ + 'Just a quick heads-up: ' + data.author.username + ' replied to you on Camper News.', + 'You can keep this conversation going.', + 'Just head back to the discussion here: http://freecodecamp.com/stories/' + data.originalStoryLink, + '- the Free Code Camp Volunteer Team' + ].join('\n') + }; + + transporter.sendMail(mailOptions, function (err) { + if (err) { + return err; + } + }); } - var transporter = nodemailer.createTransport({ - service: 'Mandrill', - auth: { - user: secrets.mandrill.user, - pass: secrets.mandrill.password - } - }); - var mailOptions = { - to: recipients, - from: 'Team@freecodecamp.com', - subject: associatedStory.author.username + " replied to your post on Camper News", - text: [ - "Just a quick heads-up: " + associatedStory.author.username + " replied to you on Camper News.", - "You can keep this conversation going.", - "Just head back to the discussion here: http://freecodecamp.com/stories/" + comment.originalStoryLink, - '- the Free Code Camp Volunteer Team' - ].join('\n') - }; - transporter.sendMail(mailOptions, function (err) { - if (err) { - return err; - } - }); }); }); } catch (e) { diff --git a/public/js/main.js b/public/js/main.js index e01fe3dcfe..5ce2c1d9bb 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -125,7 +125,6 @@ $(document).ready(function() { }); $('#next-courseware-button').on('click', function() { - console.log(passedCoursewareHash); if ($('.signup-btn-nav').length < 1) { switch (challengeType) { case 0: @@ -278,7 +277,7 @@ $(document).ready(function() { $('#story-submit').on('click', storySubmitButtonHandler); var commentSubmitButtonHandler = function commentSubmitButtonHandler() { - $('comment-button').unbind('click'); + $('#comment-button').unbind('click'); var data = $('#comment-box').val(); $('#comment-button').attr('disabled', 'disabled'); @@ -286,11 +285,14 @@ $(document).ready(function() { { data: { associatedPost: storyId, + originalStoryLink: originalStoryLink, + originalStoryAuthorEmail: originalStoryAuthorEmail, body: data } }) .fail(function (xhr, textStatus, errorThrown) { $('#comment-button').attr('disabled', false); + $('#comment-button').bind('click', commentSubmitButtonHandler); }) .done(function (data, textStatus, xhr) { window.location.reload(); diff --git a/seed_data/coursewares.json b/seed_data/coursewares.json index 7c130a5d3c..c95d431077 100644 --- a/seed_data/coursewares.json +++ b/seed_data/coursewares.json @@ -821,9 +821,9 @@ "Rule #3: Reverse engineer the example project's functionality, and also feel free to personalize it.", "Here are the user stories you must enable, and optional bonus user stories:", "User Story: As a user, I can play a game of Tic Tac Toe with the computer.", + "Bonus User Story: As a user, I can never actually win against the computer - at best I can tie.", "Bonus User Story: As a user, my game will reset as soon as it's over so I can play again.", "Bonus User Story: As a user, I can choose whether I want to play as X or O.", - "Hint: Here's an example call to Twitch.tv's JSON API: https://api.twitch.tv/kraken/streams/freecodecamp.", "When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen. If you pair programmed, you should also include the Free Code Camp username of your pair.", "If you'd like immediate feedback on your project, click this button and paste in a link to your CodePen project. Otherwise, we'll review it before you start your nonprofit projects.Click here then add your link to your tweet's text" ], diff --git a/seed_data/field-guides.json b/seed_data/field-guides.json index 5b40a05a0c..e8ee3ba70e 100644 --- a/seed_data/field-guides.json +++ b/seed_data/field-guides.json @@ -258,6 +258,29 @@ "" ] }, + { + "_id": "bd7159d9c442eddfaeb5bdef", + "name": "What does Register mean?", + "description": [ + "
", + "

", + "
", + "
", + "

These global shortcuts work everywhere on a Mac:", + "
    ", + "
  • Control + F = Forward
  • ", + "
  • Control + B = Backward
  • ", + "
  • Control + N = Next Line
  • ", + "
  • Control + P = Previous Line
  • ", + "
  • Control + H = Backspace
  • ", + "
  • Control + D = Delete
  • ", + "
  • Control + A = Beginning of Line
  • ", + "
  • Control + E = End of Line
  • ", + "
  • Control + K = Kill line
  • ", + "
", + "

" + ] + }, { "_id": "bd7158d9c445eddfaeb5bdef", "name": "Gmail Zero Inbox Shortcuts", @@ -370,103 +393,6 @@ "
" ] }, - { - "_id": "bd7158d9c448eddfaeb5bdef", - "name": "Live Stream Pair Programming on Twitch.tv", - "description": [ - "
", - "

Live Pair Programming

", - "

", - "

Watch the live stream below or on our  Twitch.tv channel.

", - "
", - "
", - "
", - " ", - "
", - "
", - "
", - "
", - "
", - " ", - "
", - "
", - "
", - "
", - " ", - "
", - "
", - "
", - "
", - "

Previous Live Pair Programming Sessions

", - "
", - "
", - " ", - "
", - "

link:  http://www.youtube.com/watch/_BErpDdmBOw

", - "
", - " ", - "
", - "

link:  http://www.youtube.com/watch/Fn9HMn79KH0

", - "
", - " ", - "
", - "

link:  http://www.youtube.com/watch/S7iRBZJwOAs

", - "
", - " ", - "
", - "

link:  http://www.youtube.com/watch/BHNRg39ZblE

", - "
", - " ", - "
", - "

link:  http://www.youtube.com/watch/YDfkHlDmehA

", - "
", - " ", - " ", - " ", - "
" - ] - }, - { - "_id": "bd7158d9c449eddfaeb5bdef", - "name": "Nodeschool Challenges", - "description": [ - "

Learn Node.js, NPM, Express.js, and advanced JavaScript like Functional Programming and Promises


", - "
", - " ", - "
", - "
", - "

Here are the NodeSchool courses you should complete:", - "
    ", - "
  1. NodeSchool.io's LearnYouNode course
  2. ", - "
  3. NodeSchool.io's Learn-to-NPM course
  4. ", - "
  5. NodeSchool.io's Express.js course
  6. ", - "
  7. NodeSchool.io's Functional Programming in JavaScript course
  8. ", - "
  9. NodeSchool.io's Promise It Won't Hurt Promises course
  10. ", - "
", - "

", - "

" - ] - }, - { - "_id": "bd7158d9c450eddfaeb5bdef", - "name": "Nonprofit Project Instructions", - "description": [ - "
", - "

It's time to apply what you've learned here at Free Code Camp.

", - "

By the end of this process, you'll have a portfolio of live apps being used by real people.

", - "

Please do the following immediately:

", - "

", - "
    ", - "
  1. Complete this form:  http://goo.gl/forms/f61dLt67t8.
  2. ", - "
  3. Read this document, which will answer many questions you may have about our nonprofit projects:  http://freecodecamp.com/field-guide/guide-to-our-nonprofit-projects.
  4. ", - "
  5. We'll send you an invite to our Nonprofit Projects Trello board. Once we do, go there and add yourself to at least 3 nonprofit projects that interest you.
  6. ", - "
  7. Finish any unfinished Bonfire challenges. These challenges serve as the Free Code Camp \"exit test\". You must complete these before you can start working on nonprofit projects. If you completed CoderByte or CodeWars challenges instead of Bonfire, email us and we'll take a look: team@freecodecamp.com.
  8. ", - "
", - "

Please email us if you have further questions:  team@freecodecamp.com.

", - "", - "
" - ] - }, { "_id": "bd7158d9c451eddfaeb5bded", "name": "Bonfire Style Guide", diff --git a/seed_data/storyCleanup.js b/seed_data/storyCleanup.js new file mode 100644 index 0000000000..645ff84206 --- /dev/null +++ b/seed_data/storyCleanup.js @@ -0,0 +1,50 @@ +/** + * Created by nathanleniz on 4/25/15. + */ +require('dotenv').load(); +var mongodb = require('mongodb'), + Story = require('../models/Story.js'), + secrets = require('../config/secrets'); + mongoose = require('mongoose'); + +mongoose.connect(secrets.db); + +function storyLinkCleanup(cb) { + console.log('headLineCleanup'); + var i = 1; + var stream = Story.find({}).skip(0).limit(0).batchSize(20000).stream(); + + stream.on('data', function (story) { + console.log(i++); + this.pause(); + story.storyLink = story.storyLink. + replace(/\'/g, ''). + replace(/\"/g, ''). + replace(/,/g, ''). + replace(/\s+/g, ' '). + replace(/[^a-z0-9\s]/gi, ''). + toLowerCase(). + trim(); + story.save(function (err) { + if (err) { + console.log('woops'); + } + this.resume(); + }.bind(this)); + }) + .on('error', function (err) { + console.error(err); + }).on('close', function () { + console.log('done with set'); + stream.destroy(); + cb(); + }); +} + +function done() { + console.log('Migration script has completed'); + process.exit(0); +} + + +storyLinkCleanup(done); diff --git a/views/stories/comments.jade b/views/stories/comments.jade index 07412b59be..425d52890d 100755 --- a/views/stories/comments.jade +++ b/views/stories/comments.jade @@ -108,6 +108,7 @@ data: { associatedPost: commentId, originalStoryLink: originalStoryLink, + originalStoryAuthorEmail: originalStoryAuthorEmail, body: $('#comment-to-comment-textinput').val(), } }) diff --git a/views/stories/show.jade b/views/stories/show.jade index edb1e3c6b8..1e04c3a34f 100644 --- a/views/stories/show.jade +++ b/views/stories/show.jade @@ -2,6 +2,7 @@ script. var storyId = !{JSON.stringify(id)}; var originalStoryLink = !{JSON.stringify(originalStoryLink)}; + var originalStoryAuthorEmail = !{JSON.stringify(originalStoryAuthorEmail)}; var comments = !{JSON.stringify(comments)}; var upVotes = !{JSON.stringify(upVotes)}; var image = !{JSON.stringify(image)};