From 5ff371fc6f9bcf7be2a05c33d7b98d4e7e7fb2cc Mon Sep 17 00:00:00 2001 From: "Cristian V. Nica" Date: Wed, 5 Aug 2015 21:55:25 +0300 Subject: [PATCH 01/25] Trying to fix: #1571 I have found a small typo on challenge: An Array Of new Information --- seed/challenges/basic-javascript.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index 0325880343..146693deef 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -423,7 +423,7 @@ "description": [ "", "In JavaScript we can store lists or collections of data in what are called arrays", - "Arrays are distinguished by the [ and ] around the data. Each piece of data is separated be a , ", + "Arrays are distinguished by the [ and ] around the data. Each piece of data is separated by a , ", "Now let's create a new array called myArray with a string and a number with a , separating each one", "Refer to the example if you get stuck", "" From 1ca6bd9c609f46f048ba6384f27f0873ffc28269 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 12:25:52 -0700 Subject: [PATCH 02/25] Create .gitattributes This should fix out Github language statistics --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..2586cf2089 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +public/js/lib/codemirror linguist-vendored +*.jsx linguist-language=JavaScript From b7e39cd9502b95709880ff4d1da1b8a30f8f4af1 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 12:27:07 -0700 Subject: [PATCH 03/25] Update .gitattributes --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 2586cf2089..38f518f571 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ -public/js/lib/codemirror linguist-vendored +public/js/lib/codemirror/* linguist-vendored *.jsx linguist-language=JavaScript From 27cff0becfdec5e0af7fa3655076f77d114d40f5 Mon Sep 17 00:00:00 2001 From: Samuel Plumppu Date: Wed, 5 Aug 2015 21:37:04 +0200 Subject: [PATCH 04/25] added test for object for steamroller bonfire --- seed/challenges/intermediate-bonfires.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seed/challenges/intermediate-bonfires.json b/seed/challenges/intermediate-bonfires.json index 5ae11fc7a4..c3f86b44c6 100644 --- a/seed/challenges/intermediate-bonfires.json +++ b/seed/challenges/intermediate-bonfires.json @@ -675,7 +675,8 @@ "tests": [ "assert.deepEqual(steamroller([[['a']], [['b']]]), ['a', 'b'], 'should flatten nested arrays');", "assert.deepEqual(steamroller([1, [2], [3, [[4]]]]), [1, 2, 3, 4], 'should flatten nested arrays');", - "assert.deepEqual(steamroller([1, [], [3, [[4]]]]), [1, 3, 4], 'should work with empty arrays');" + "assert.deepEqual(steamroller([1, [], [3, [[4]]]]), [1, 3, 4], 'should work with empty arrays');", + "assert.deepEqual(steamroller([1, {}, [3, [[4]]]]), [1, {}, 3, 4], 'should work with actual objects');" ], "MDNlinks": [ "Array.isArray()" From 3cfd69c70446ba23b0133af572236377f981bb63 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 11:29:19 -0700 Subject: [PATCH 05/25] add loopbackMigrationGrandfathered flag --- common/models/user.json | 4 ++++ seed/loopbackMigration.js | 2 ++ 2 files changed, 6 insertions(+) diff --git a/common/models/user.json b/common/models/user.json index d055e431ff..32ef5e3c19 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -27,6 +27,10 @@ "type": "boolean", "default": false }, + "isMigrationGrandfathered": { + "type": "boolean", + "default": false + }, "username": { "type": "string", "lowercase": true, diff --git a/seed/loopbackMigration.js b/seed/loopbackMigration.js index 333629438a..d167ccec8b 100644 --- a/seed/loopbackMigration.js +++ b/seed/loopbackMigration.js @@ -92,6 +92,8 @@ var users = dbObservable user.username = 'fcc' + uuid.v4().slice(0, 8); if (user.github) { user.isGithubCool = true; + } else { + user.isMigrationGrandfathered = true; } return user; }) From 504239050c74a50afa4710c58aa8cc072cb6327b Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 11:45:58 -0700 Subject: [PATCH 06/25] remove old license information repo is now under copyright to FCC find license in root directory --- server/boot/challenge.js | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 5e0cc37f0d..4228a3ff15 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -1,35 +1,3 @@ -/** - * Created by nathanleniz on 5/15/15. - * Copyright (c) 2015, Free Code Camp - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS - BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - var R = require('ramda'), Rx = require('rx'), assign = require('object.assign'), From f167238c74d772ea33b54940a852a5bdc8d88f46 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 13:01:19 -0700 Subject: [PATCH 07/25] remove public accounts if user is not github cool or grandfathered --- server/boot/user.js | 202 +++++++++++++++++++++++--------------------- 1 file changed, 104 insertions(+), 98 deletions(-) diff --git a/server/boot/user.js b/server/boot/user.js index 7278e0a07d..1adfcfa99a 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -122,113 +122,119 @@ module.exports = function(app) { */ function returnUser(req, res, next) { + const username = req.params.username.toLowerCase(); + const { path } = req; User.findOne( - { where: { 'username': req.params.username.toLowerCase() } }, + { where: { username } }, function(err, user) { if (err) { return next(err); } - if (user) { - user.progressTimestamps = - user.progressTimestamps.sort(function(a, b) { - return a - b; - }); - - var timeObject = Object.create(null); - R.forEach(function(time) { - timeObject[moment(time).format('YYYY-MM-DD')] = time; - }, user.progressTimestamps); - - var tmpLongest = 1; - var timeKeys = R.keys(timeObject); - - user.longestStreak = 0; - for (var i = 1; i <= timeKeys.length; i++) { - if (moment(timeKeys[i - 1]).add(1, 'd').toString() - === moment(timeKeys[i]).toString()) { - tmpLongest++; - if (tmpLongest > user.longestStreak) { - user.longestStreak = tmpLongest; - } - } else { - tmpLongest = 1; - } - } - - timeKeys = timeKeys.reverse(); - tmpLongest = 1; - - user.currentStreak = 1; - var today = moment(Date.now()).format('YYYY-MM-DD'); - - const yesterday = moment(today).subtract(1, 'd').toString(); - const yesteryesterday = moment(today).subtract(2, 'd').toString(); - - if ( - moment(today).toString() === moment(timeKeys[0]).toString() || - yesterday === moment(timeKeys[0]).toString() || - yesteryesterday === moment(timeKeys[0]).toString() - ) { - for (var _i = 1; _i <= timeKeys.length; _i++) { - - if ( - moment(timeKeys[_i - 1]).subtract(1, 'd').toString() === - moment(timeKeys[_i]).toString() - ) { - - tmpLongest++; - - if (tmpLongest > user.currentStreak) { - user.currentStreak = tmpLongest; - } - } else { - break; - } - } - } else { - user.currentStreak = 1; - } - - var data = {}; - var progressTimestamps = user.progressTimestamps; - progressTimestamps.forEach(function(timeStamp) { - data[(timeStamp / 1000)] = 1; - }); - - user.currentStreak = user.currentStreak || 1; - user.longestStreak = user.longestStreak || 1; - var challenges = user.completedChallenges.filter(function( obj ) { - return obj.challengeType === 3 || obj.challengeType === 4; - }); - - res.render('account/show', { - title: 'Camper ' + user.username + '\'s portfolio', - username: user.username, - name: user.name, - location: user.location, - githubProfile: user.githubProfile, - linkedinProfile: user.linkedinProfile, - codepenProfile: user.codepenProfile, - facebookProfile: user.facebookProfile, - twitterHandle: user.twitterHandle, - bio: user.bio, - picture: user.picture, - progressTimestamps: user.progressTimestamps, - challenges: challenges, - calender: data, - moment: moment, - longestStreak: user.longestStreak + - (user.longestStreak === 1 ? ' day' : ' days'), - currentStreak: user.currentStreak + - (user.currentStreak === 1 ? ' day' : ' days') - }); - } else { + if (!user) { 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 path ${ path }` }); return res.redirect('/'); } + if (!user.isGithubCool && !user.isMigrationGrandfathered) { + req.flash('errors', { + msg: ` + user ${ username } has not completed account signup + ` + }); + return res.redirect('/'); + } + user.progressTimestamps = + user.progressTimestamps.sort(function(a, b) { + return a - b; + }); + + var timeObject = Object.create(null); + R.forEach(function(time) { + timeObject[moment(time).format('YYYY-MM-DD')] = time; + }, user.progressTimestamps); + + var tmpLongest = 1; + var timeKeys = R.keys(timeObject); + + user.longestStreak = 0; + for (var i = 1; i <= timeKeys.length; i++) { + if (moment(timeKeys[i - 1]).add(1, 'd').toString() + === moment(timeKeys[i]).toString()) { + tmpLongest++; + if (tmpLongest > user.longestStreak) { + user.longestStreak = tmpLongest; + } + } else { + tmpLongest = 1; + } + } + + timeKeys = timeKeys.reverse(); + tmpLongest = 1; + + user.currentStreak = 1; + var today = moment(Date.now()).format('YYYY-MM-DD'); + + const yesterday = moment(today).subtract(1, 'd').toString(); + const yesteryesterday = moment(today).subtract(2, 'd').toString(); + + if ( + moment(today).toString() === moment(timeKeys[0]).toString() || + yesterday === moment(timeKeys[0]).toString() || + yesteryesterday === moment(timeKeys[0]).toString() + ) { + for (var _i = 1; _i <= timeKeys.length; _i++) { + + if ( + moment(timeKeys[_i - 1]).subtract(1, 'd').toString() === + moment(timeKeys[_i]).toString() + ) { + + tmpLongest++; + + if (tmpLongest > user.currentStreak) { + user.currentStreak = tmpLongest; + } + } else { + break; + } + } + } else { + user.currentStreak = 1; + } + + var data = {}; + var progressTimestamps = user.progressTimestamps; + progressTimestamps.forEach(function(timeStamp) { + data[(timeStamp / 1000)] = 1; + }); + + user.currentStreak = user.currentStreak || 1; + user.longestStreak = user.longestStreak || 1; + + res.render('account/show', { + title: 'Camper ' + user.username + '\'s portfolio', + username: user.username, + name: user.name, + isMigrationGrandfathered: user.isMigrationGrandfathered, + isGithubCool: user.isGithubCool, + location: user.location, + githubProfile: user.githubProfile, + linkedinProfile: user.linkedinProfile, + codepenProfile: user.codepenProfile, + facebookProfile: user.facebookProfile, + twitterHandle: user.twitterHandle, + bio: user.bio, + picture: user.picture, + progressTimestamps: user.progressTimestamps, + calender: data, + moment: moment, + longestStreak: user.longestStreak + + (user.longestStreak === 1 ? ' day' : ' days'), + currentStreak: user.currentStreak + + (user.currentStreak === 1 ? ' day' : ' days') + }); } ); } From 0def1b3ba8e47e8f4c1b75fdafd5a703af7f32b3 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 13:21:53 -0700 Subject: [PATCH 08/25] readd challenges --- server/boot/user.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/boot/user.js b/server/boot/user.js index 1adfcfa99a..b1d3c6d8d3 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -209,6 +209,9 @@ module.exports = function(app) { progressTimestamps.forEach(function(timeStamp) { data[(timeStamp / 1000)] = 1; }); + var challenges = user.completedChallenges.filter(function( obj ) { + return obj.challengeType === 3 || obj.challengeType === 4; + }); user.currentStreak = user.currentStreak || 1; user.longestStreak = user.longestStreak || 1; @@ -229,6 +232,7 @@ module.exports = function(app) { picture: user.picture, progressTimestamps: user.progressTimestamps, calender: data, + challenges: challenges, moment: moment, longestStreak: user.longestStreak + (user.longestStreak === 1 ? ' day' : ' days'), From caef8c9da8c78ee74117658f2e568c5cb0e225cd Mon Sep 17 00:00:00 2001 From: "Cristian V. Nica" Date: Wed, 5 Aug 2015 23:22:37 +0300 Subject: [PATCH 09/25] Fixing #1575 Fixing small typo --- seed/challenges/basic-javascript.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index 0325880343..fd393264d3 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -677,7 +677,7 @@ "description":[ "", "A very important data type in javascript is the Object ", - " Objects a similar to arrays except that instead of using indexes to access and modify their data, Objects have what are called properties ", + " Objects are similar to arrays except that instead of using indexes to access and modify their data, Objects have what are called properties ", "Here's a sample Object", "", "var cat = {", From 07e9bbf00a887029e9678c8b8934041ee1d345f0 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 13:54:21 -0700 Subject: [PATCH 10/25] add link to github waypoint to non github cool campers --- server/views/account/account.jade | 117 ++++++++++++++++-------------- 1 file changed, 62 insertions(+), 55 deletions(-) diff --git a/server/views/account/account.jade b/server/views/account/account.jade index b1204b264e..3e95b67dc6 100644 --- a/server/views/account/account.jade +++ b/server/views/account/account.jade @@ -5,67 +5,74 @@ block content .panel.panel-info(ng-controller="profileValidationController") .panel-heading.text-center Update your portfolio here: .panel-body - if (!user.github) + if (!user.isGithubCool) + .row .col-xs-12 - a.btn.btn-lg.btn-block.btn-github.btn-link-social(href='/auth/github') - i.fa.fa-github - | Link GitHub with my account - .col-xs-12 - form.form-horizontal(action='/account/profile', method='POST', novalidate='novalidate', name='profileForm' ng-show="asyncComplete") - input(type='hidden', name='_csrf', value=_csrf) - .form-group - label.col-sm-3.col-sm-offset-1.control-label(for='bio') Bio (140 characters) - .col-sm-4 - input.form-control(type='text', name='bio', autocomplete="off", ng-model='user.bio', ng-maxlength='140', id='bio') - .col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.bio.$error.maxlength && !profileForm.bio.$pristine') - alert(type='danger') - span.ion-close-circled - | Your bio must be fewer than 140 characters. - .form-group - label.col-sm-3.col-sm-offset-1.control-label(for='email') Twitter - .col-sm-4 - .input-group.twitter-input - span.input-group-addon @ - input.form-control(type='text', name='twitterHandle', autocomplete="off", id='twitterHandle', ng-model='user.twitterHandle', ng-maxlength='15', ng-pattern="/^[A-z0-9_]+$/") - .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.twitterHandle.$error.pattern") - alert(type='danger') + .text-left.btn-info.btn.btn-block.btn-link-social(href='http://www.freecodecamp.com/challenges/waypoint-join-our-chat-room') + span Link your account to GitHub to update your portfolio page. Click here if you don’t have a GitHub account yet. + .row + .col-xs-12 + a.btn.btn-lg.btn-block.btn-github.btn-link-social(href='/auth/github') + i.fa.fa-github + | Link GitHub with my account + + .row + .col-xs-12 + form.form-horizontal(action='/account/profile', method='POST', novalidate='novalidate', name='profileForm' ng-show="asyncComplete") + input(type='hidden', name='_csrf', value=_csrf) + .form-group + label.col-sm-3.col-sm-offset-1.control-label(for='bio') Bio (140 characters) + .col-sm-4 + input.form-control(type='text', name='bio', autocomplete="off", ng-model='user.bio', ng-maxlength='140', id='bio') + .col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.bio.$error.maxlength && !profileForm.bio.$pristine') + alert(type='danger') span.ion-close-circled - | Your Twitter handle should only contain letters, numbers and underscores (az10_). - .col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.twitterHandle.$error.maxlength && !profileForm.twitterHandle.$pristine') - alert(type='danger') + | Your bio must be fewer than 140 characters. + .form-group + label.col-sm-3.col-sm-offset-1.control-label(for='email') Twitter + .col-sm-4 + .input-group.twitter-input + span.input-group-addon @ + input.form-control(type='text', name='twitterHandle', autocomplete="off", id='twitterHandle', ng-model='user.twitterHandle', ng-maxlength='15', ng-pattern="/^[A-z0-9_]+$/") + .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.twitterHandle.$error.pattern") + alert(type='danger') + span.ion-close-circled + | Your Twitter handle should only contain letters, numbers and underscores (az10_). + .col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.twitterHandle.$error.maxlength && !profileForm.twitterHandle.$pristine') + alert(type='danger') + span.ion-close-circled + | Your name must be fewer than 15 characters. + + .form-group + label.col-sm-3.col-sm-offset-1.control-label(for='email') CodePen + .col-sm-4 + input.form-control(type='url', name='codepenProfile', id='codepenProfile', autocomplete="off", ng-model='user.codepenProfile', placeholder='http://') + .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.codepenProfile.$error.url && !profileForm.codepenProfile.$pristine") + alert(type='danger') span.ion-close-circled - | Your name must be fewer than 15 characters. + | Please enter a valid URL format (http://www.example.com). - .form-group - label.col-sm-3.col-sm-offset-1.control-label(for='email') CodePen - .col-sm-4 - input.form-control(type='url', name='codepenProfile', id='codepenProfile', autocomplete="off", ng-model='user.codepenProfile', placeholder='http://') - .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.codepenProfile.$error.url && !profileForm.codepenProfile.$pristine") - alert(type='danger') - span.ion-close-circled - | Please enter a valid URL format (http://www.example.com). + .form-group + label.col-sm-3.col-sm-offset-1.control-label(for='email') LinkedIn + .col-sm-4 + input.form-control(type='url', name='linkedinProfile', id='linkedinProfile', autocomplete="off", ng-model='user.linkedinProfile', placeholder='http://') + .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.linkedinProfile.$error.url && !profileForm.linkedinProfile.$pristine") + alert(type='danger') + span.ion-close-circled + | Please enter a valid URL format (http://www.example.com). - .form-group - label.col-sm-3.col-sm-offset-1.control-label(for='email') LinkedIn - .col-sm-4 - input.form-control(type='url', name='linkedinProfile', id='linkedinProfile', autocomplete="off", ng-model='user.linkedinProfile', placeholder='http://') - .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.linkedinProfile.$error.url && !profileForm.linkedinProfile.$pristine") - alert(type='danger') - span.ion-close-circled - | Please enter a valid URL format (http://www.example.com). + .form-group + label.col-sm-3.col-sm-offset-1.control-label(for='email') Facebook + .col-sm-4 + input.form-control(type='url', name='facebookProfile', id='facebookProfile', autocomplete="off", ng-model='user.facebookProfile', placeholder='http://') + .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.facebookProfile.$error.url && !profileForm.facebookProfile.$pristine") + alert(type='danger') + span.ion-close-circled + | Please enter a valid URL format (http://www.example.com). - .form-group - label.col-sm-3.col-sm-offset-1.control-label(for='email') Facebook - .col-sm-4 - input.form-control(type='url', name='facebookProfile', id='facebookProfile', autocomplete="off", ng-model='user.facebookProfile', placeholder='http://') - .col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.facebookProfile.$error.url && !profileForm.facebookProfile.$pristine") - alert(type='danger') - span.ion-close-circled - | Please enter a valid URL format (http://www.example.com). - - button.btn.btn-lg.btn-block.btn-primary.btn-link-social(type='submit', ng-disabled='profileForm.$invalid') - span.ion-edit - | Update my info + button.btn.btn-lg.btn-block.btn-primary.btn-link-social(type='submit', ng-disabled='!user.isGithubCool || profileForm.$invalid') + span.ion-edit + | Update my info .panel.panel-info .panel-heading.text-center Manage your account here: From 43a1a6253616f82c5fbe7b00d872fe7337b824e9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Rodriguez" Date: Wed, 5 Aug 2015 17:34:06 -0400 Subject: [PATCH 11/25] Fix for Issue #1540 --- seed/challenges/intermediate-bonfires.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/intermediate-bonfires.json b/seed/challenges/intermediate-bonfires.json index c3f86b44c6..4019e54af5 100644 --- a/seed/challenges/intermediate-bonfires.json +++ b/seed/challenges/intermediate-bonfires.json @@ -356,7 +356,7 @@ " return arr1;", "}", "", - "unite([1, 2, 3], [5, 2, 1, 4], [2, 1]);" + "unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);" ], "tests": [ "assert.deepEqual(unite([1, 3, 2], [5, 2, 1, 4], [2, 1]), [1, 3, 2, 5, 4], 'should return the union of the given arrays');", From 36f66c1262100542c44f62f9f040133a9a224cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Floty=C5=84ski?= Date: Wed, 5 Aug 2015 23:53:45 +0200 Subject: [PATCH 12/25] Fixed tests in waypoint changes col-xs-12 to col-xs-6 so the tests works Issue #1578 --- seed/challenges/bootstrap.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json index 5a89962f12..5c0ddb95a8 100644 --- a/seed/challenges/bootstrap.json +++ b/seed/challenges/bootstrap.json @@ -1883,7 +1883,7 @@ "Above your right-well, inside its \"col-xs-6\" div element, add a h4 element with the text \"#right-well\"." ], "tests": [ - "assert($('.col-xs-12').children('h4') && $('.col-xs-12').children('h4').length > 1, 'Add an h4 element to each of your <div class=\\'col-xs-6\\'> elements.');", + "assert($('.col-xs-6').children('h4') && $('.col-xs-6').children('h4').length > 1, 'Add an h4 element to each of your <div class=\\'col-xs-6\\'> elements.');", "assert(new RegExp('#left-well','gi').test($('h4').text()), 'One h4 element should have the text \"#left-well\".');", "assert(new RegExp('#right-well','gi').test($('h4').text()), 'One h4 element should have the text \"#right-well\".');" ], From c6b2140c023516e98ab2af6978cbd5c20d620d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Floty=C5=84ski?= Date: Thu, 6 Aug 2015 00:20:52 +0200 Subject: [PATCH 13/25] first fixes - length larger _or equal_ to 1 I'm not sure if the tests are too vague - they allow to comment out way more than just the text specified - like the whole page.. --- seed/challenges/bootstrap.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json index 5a89962f12..5cfb6f699d 100644 --- a/seed/challenges/bootstrap.json +++ b/seed/challenges/bootstrap.json @@ -2036,9 +2036,9 @@ "Add a comment at the top of your HTML that says You shouldn't need to modify code below this line." ], "tests": [ - "assert(editor.match(//g).length > 1, 'Be sure to close your comment with -->.')" + "assert(editor.match(//g).length >= 1, 'Be sure to close your comment with -->.')" ], "challengeSeed": [ "
", From 073456e0de4f99351ef2fde6b75d623d94b71e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Floty=C5=84ski?= Date: Thu, 6 Aug 2015 00:25:11 +0200 Subject: [PATCH 14/25] changed to agree with the style - greater than 0 instead of greater than or eqal to 1 --- seed/challenges/bootstrap.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json index 5cfb6f699d..ba2869359a 100644 --- a/seed/challenges/bootstrap.json +++ b/seed/challenges/bootstrap.json @@ -2036,9 +2036,9 @@ "Add a comment at the top of your HTML that says You shouldn't need to modify code below this line." ], "tests": [ - "assert(editor.match(//g).length >= 1, 'Be sure to close your comment with -->.')" + "assert(editor.match(//g).length > 0, 'Be sure to close your comment with -->.')" ], "challengeSeed": [ "
", From c577190e675f0ce77d512e59567423d2b4ced701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Floty=C5=84ski?= Date: Thu, 6 Aug 2015 00:57:55 +0200 Subject: [PATCH 15/25] Fixed tests Added check if the object isn't null and escaped quotes because text enclosed apostrophe --- seed/challenges/bootstrap.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json index ba2869359a..03c9ed18bd 100644 --- a/seed/challenges/bootstrap.json +++ b/seed/challenges/bootstrap.json @@ -2036,9 +2036,9 @@ "Add a comment at the top of your HTML that says You shouldn't need to modify code below this line." ], "tests": [ - "assert(editor.match(//g).length > 0, 'Be sure to close your comment with -->.')" + "assert(editor.match(//g) && editor.match(/-->/g).length > 0, 'Be sure to close your comment with -->.')" ], "challengeSeed": [ "
", From e9958281c9d87cc82f9537c8541cd0183259927d Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 17:49:19 -0700 Subject: [PATCH 16/25] pull user bio from github --- common/models/User-Identity.js | 52 +++++++++++++++++++++++++--------- common/models/user.json | 12 ++++++++ 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/common/models/User-Identity.js b/common/models/User-Identity.js index 3df7f01400..a69685cc29 100644 --- a/common/models/User-Identity.js +++ b/common/models/User-Identity.js @@ -1,3 +1,4 @@ +import assign from 'object.assign'; import debugFactory from 'debug'; const debug = debugFactory('freecc:models:userIdent'); @@ -10,6 +11,39 @@ function getFirstImageFromProfile(profile) { null; } +// using es6 argument destructing +function setProfileFromGithub( + user, + { + profileUrl: githubURL, + username + }, + { + location, + email: githubEmail, + id: githubId, + 'created_at': joinedGithubOn, + blog: website, + name + } +) { + return assign( + user, + { isGithubCool: true, isMigrationGrandfathered: false }, + { + name, + username: username.toLowerCase(), + location, + joinedGithubOn, + website, + githubId, + githubURL, + githubEmail, + githubProfile: githubURL + } + ); +} + export default function(UserIdent) { UserIdent.observe('before save', function(ctx, next) { var userIdent = ctx.currentInstance || ctx.instance; @@ -25,7 +59,8 @@ export default function(UserIdent) { return next(); } - const picture = getFirstImageFromProfile(userIdent.profile); + const { profile } = userIdent; + const picture = getFirstImageFromProfile(profile); debug('picture', picture, user.picture); // check if picture was found @@ -41,19 +76,10 @@ export default function(UserIdent) { userChanged = true; } - // if user signed in with github - // and user is not github cool - // or username is different from github username - // then make them github cool - // and set their username from their github profile. - if ( - userIdent.provider === 'github-login' && - (!user.isGithubCool || - user.username !== userIdent.provider.username.toLowerCase()) - ) { + // if user signed in with github refresh their info + if (userIdent.provider === 'github-login') { debug("user isn't github cool or username from github is different"); - user.isGithubCool = true; - user.username = userIdent.profile.username.toLowerCase(); + setProfileFromGithub(user, profile, profile._json); userChanged = true; } diff --git a/common/models/user.json b/common/models/user.json index 32ef5e3c19..b961807093 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -27,6 +27,18 @@ "type": "boolean", "default": false }, + "githubId": { + "type": "string" + }, + "githubURL": { + "type": "string" + }, + "githubEmail": { + "type": "string" + }, + "joinedGithubOn": { + "type": "date" + }, "isMigrationGrandfathered": { "type": "boolean", "default": false From 8c7a9dd818d4059d8d1acfb1ff293f88e8f4c44f Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 18:51:15 -0700 Subject: [PATCH 17/25] fix trim and lowercase user username and email --- common/models/user.js | 8 ++++++++ common/models/user.json | 13 +++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/common/models/user.js b/common/models/user.js index 3c7e54cbb3..9b7de00def 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -44,6 +44,14 @@ module.exports = function(User) { // username should be unique User.validatesUniquenessOf('username'); + User.observe('before save', function({ instance: user }, next) { + if (user) { + user.username = user.username.trim().toLowerCase(); + user.email = user.email.trim().toLowerCase(); + } + next(); + }); + debug('setting up user hooks'); User.afterRemote('confirm', function(ctx) { ctx.req.flash('success', { diff --git a/common/models/user.json b/common/models/user.json index b961807093..97027fd50e 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -12,9 +12,7 @@ "unique": true, "sparse": true } - }, - "lowercase": true, - "trim": true + } }, "password": { "type": "string" @@ -45,9 +43,12 @@ }, "username": { "type": "string", - "lowercase": true, - "trim": true, - "require": true + "require": true, + "index": { + "mongodb": { + "unique": true + } + } }, "bio": { "type": "string", From e6d05e34217f4b360b5a1fb29189284bb686d794 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 19:08:25 -0700 Subject: [PATCH 18/25] fix specify index using mongodb property --- common/models/challenge.json | 7 ++++++- common/models/nonprofit.json | 7 ++++++- common/models/story.json | 19 ++++++++++--------- common/models/user.json | 4 +++- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/common/models/challenge.json b/common/models/challenge.json index 3258d641ee..baa78a4e4c 100644 --- a/common/models/challenge.json +++ b/common/models/challenge.json @@ -6,7 +6,12 @@ "properties": { "name": { "type": "string", - "unique": true + "index": { + "mongodb": { + "unique": true, + "background": true + } + } }, "title": { "type": "string" diff --git a/common/models/nonprofit.json b/common/models/nonprofit.json index 738c43e9ad..5d8dddbc3d 100644 --- a/common/models/nonprofit.json +++ b/common/models/nonprofit.json @@ -6,7 +6,12 @@ "properties": { "name": { "type": "string", - "unique": true + "index": { + "mongodb": { + "unique": true, + "background": true + } + } }, "whatDoesNonprofitDo": { "type": "string" diff --git a/common/models/story.json b/common/models/story.json index 8aab09964b..6f252cefb3 100644 --- a/common/models/story.json +++ b/common/models/story.json @@ -6,28 +6,29 @@ "properties": { "name": { "type": "string", - "unique": true + "index": { + "mongodb": { + "unique": true, + "background": true + } + } }, "headline": { - "type": "string", - "unique": false + "type": "string" }, "timePosted": { "type": "number", "default": 0 }, "link": { - "type": "string", - "unique": false + "type": "string" }, "metaDescription": { "type": "string", - "default": "", - "unique": false + "default": "" }, "description": { - "type": "string", - "unique": false + "type": "string" }, "originalStoryAuthorEmail": { "type": "string", diff --git a/common/models/user.json b/common/models/user.json index 97027fd50e..871cd2db46 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -10,6 +10,7 @@ "index": { "mongodb": { "unique": true, + "background": true, "sparse": true } } @@ -46,7 +47,8 @@ "require": true, "index": { "mongodb": { - "unique": true + "unique": true, + "background": true } } }, From a838a4097d6788373802dbb4e83efb20561bf5e2 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 19:12:54 -0700 Subject: [PATCH 19/25] fix clicking on image or points always takes you to /account --- server/views/partials/navbar.jade | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/server/views/partials/navbar.jade b/server/views/partials/navbar.jade index 8d310d8846..a306151639 100644 --- a/server/views/partials/navbar.jade +++ b/server/views/partials/navbar.jade @@ -21,15 +21,7 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height a.btn.signup-btn.signup-btn-nav(href='/login') Sign in else li - if (user.username) - a(href='/' + user.username) [ #{user.progressTimestamps.length} ] - - else - a(href='/account') [ #{user.progressTimestamps.length} ] + a(href='/account') [ #{user.progressTimestamps.length} ] .hidden-xs.hidden-sm - if (user.username) - a(href='/' + user.username) - img.profile-picture.float-right(src='#{user.picture}') - else - a(href='/account') - img.profile-picture.float-right(src='#{user.picture}') + a(href='/account') + img.profile-picture.float-right(src='#{user.picture}') From 0ce81bbabbd78aecc21c68ba8c0d47b09727cd41 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 19:29:17 -0700 Subject: [PATCH 20/25] fix email sign should not require username --- common/models/user.js | 6 ++++++ server/views/account/email-signup.jade | 19 ------------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/common/models/user.js b/common/models/user.js index 9b7de00def..212d972cc5 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -1,4 +1,5 @@ import { Observable } from 'rx'; +import uuid from 'node-uuid'; import moment from 'moment'; import debugFactory from 'debug'; @@ -62,6 +63,11 @@ module.exports = function(User) { ctx.res.redirect('/email-signin'); }); + User.beforeRemote('create', function({ req }, notUsed, next) { + req.body.username = 'fcc' + uuid.v4().slice(0, 8); + next(); + }); + User.afterRemote('login', function(ctx, user, next) { var res = ctx.res; var req = ctx.req; diff --git a/server/views/account/email-signup.jade b/server/views/account/email-signup.jade index c270b2470c..965eca605e 100644 --- a/server/views/account/email-signup.jade +++ b/server/views/account/email-signup.jade @@ -19,25 +19,6 @@ block content alert(type='danger') span.ion-close-circled | That email address is already in use. - .form-group - .col-sm-6.col-sm-offset-3 - input.form-control(type='text', name='username', ng-keypress='', autocomplete="off", id='username', placeholder='username', ng-model='username', unique-username='', required, ng-minlength=5, ng-maxlength=20, ng-pattern="/^[A-z0-9_]+$/") - .col-sm-6.col-sm-offset-3(ng-cloak, ng-show="profileForm.username.$error.pattern && !signupForm.username.$pristine") - alert(type='danger') - span.ion-close-circled - | Your username should only contain letters, numbers and underscores (az10_). - .col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.username.$error.unique && !signupForm.username.$pristine") - alert(type='danger') - span.ion-close-circled - | This username is taken. - .col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.username.$error.minlength && !signupForm.username.$pristine") - alert(type='danger') - span.ion-close-circled - | Your username must be at least 5 characters long. - .col-sm-6.col-sm-offset-3(ng-cloak, ng-show="signupForm.username.$error.maxlength && !signupForm.username.$pristine") - alert(type='danger') - span.ion-close-circled - | 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=8) From ce9e2ebcae1e640e3d5a82def8427de6f2c1b3f8 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 5 Aug 2015 22:56:23 -0700 Subject: [PATCH 21/25] fix test email for string --- common/models/user.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/models/user.js b/common/models/user.js index 212d972cc5..81078d6df0 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -48,7 +48,9 @@ module.exports = function(User) { User.observe('before save', function({ instance: user }, next) { if (user) { user.username = user.username.trim().toLowerCase(); - user.email = user.email.trim().toLowerCase(); + user.email = typeof user.email === 'string' ? + user.email.trim().toLowerCase() : + user.email; } next(); }); From f33ac579841707cff2ad7ddb5ec280edfa417d01 Mon Sep 17 00:00:00 2001 From: "Cristian V. Nica" Date: Thu, 6 Aug 2015 09:30:13 +0300 Subject: [PATCH 22/25] Fixing issue #1488 Correcting small mistakes in the waypoint instruction. --- seed/challenges/jquery-ajax-and-json.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seed/challenges/jquery-ajax-and-json.json b/seed/challenges/jquery-ajax-and-json.json index ed9c2c64c9..dad707f590 100644 --- a/seed/challenges/jquery-ajax-and-json.json +++ b/seed/challenges/jquery-ajax-and-json.json @@ -257,9 +257,9 @@ "dashedName": "waypoint-target-the-same-element-with-multiple-jQuery-Selectors", "difficulty": 3.06, "description": [ - "Now you know three ways of targeting elements: by type ($('button')), by class (($('.btn')), and by id (($'#target1')).", + "Now you know three ways of targeting elements: by type $('button'), by class $('.btn')), and by id $('#target1')).", "Use each of these jQuery selectors to target your button element with the class \"btn\" and the id \"target1\".", - "Use the addClass() jQuery function to give the element one new class for each selector: \"animated\", \"shake\", and \"button-primary\"." + "Use the addClass() jQuery function to give the element one new class for each selector: \"animated\", \"shake\", and \"btn-primary\"." ], "tests": [ "assert(editor.match(/\\$\\(\\'button\\'\\)/g), 'Use the $\\(\\'button\\'\\) selector.')", From 3b94475f9d854fc43ad91991d37ee6e37941c976 Mon Sep 17 00:00:00 2001 From: Martin Kwan Date: Thu, 6 Aug 2015 00:02:15 -0700 Subject: [PATCH 23/25] Updated Roman Numeral Challenge link Updated link so it would open in a new tab instead of the current tab. --- seed/challenges/intermediate-bonfires.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/intermediate-bonfires.json b/seed/challenges/intermediate-bonfires.json index 4019e54af5..f9c9be22d8 100644 --- a/seed/challenges/intermediate-bonfires.json +++ b/seed/challenges/intermediate-bonfires.json @@ -103,7 +103,7 @@ "difficulty": "2.02", "description": [ "Convert the given number into a roman numeral.", - "All roman numerals answers should be provided in upper-case.", + "All roman numerals answers should be provided in upper-case.", "Remember to use RSAP if you get stuck. Try to pair program. Write your own code." ], "challengeSeed": [ From 5882b9b84f903bdc9f0d2687ff3508b0dceffd34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Floty=C5=84ski?= Date: Thu, 6 Aug 2015 09:03:03 +0200 Subject: [PATCH 24/25] Fixed a few typos in javascript-basic --- seed/challenges/basic-javascript.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index 15afb5ab25..52aab20a55 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -503,7 +503,7 @@ "difficulty":"9.98171", "description":[ "", - "We are able to modify the data store in an array be using indexes", + "We are able to modify the data stored in an array by using indexes", "Example:", "", "var ourArray = [1,2,3];", @@ -536,10 +536,10 @@ "difficulty": "9.9818", "description": [ "", - "When and array has been defined we still have the ability to make changes to it afterwards", + "When an array has been defined we still have the ability to make changes to it afterwards", "One common way in which we can manipulate the data in an array is through .pop() ", - " .pop() is used to \"pop\" a value from the end of an array. We can retrieve this value by preforming the pop in a variable declaration.", - "any type of variable can be \"popped\" from and array", + " .pop() is used to \"pop\" a value from the end of an array. We can retrieve this value by performing the pop in a variable declaration.", + "Any type of variable can be \"popped\" from an array", "Let's try .pop() now" ], "tests": [ @@ -639,13 +639,13 @@ "description":[ "", "In JavaScript we can divide up our code into separate and reusable parts called functions", - "here's and example of a function", + "Here's an example of a function", "", "function functionName (a, b){", " return(a + b);", "}", "", - "our function can be called like this", + "Our function can be called like this", "functionName();", "Let's try creating and calling a function now called myFunction" ], @@ -688,7 +688,7 @@ "};", "", "Objects are useful for storing data in a structured way or in a way that represents a real world object like a cat.", - "Let's try to make a Object that represents a dog called myDog!" + "Let's try to make an Object that represents a dog called myDog!" ], "tests":[ From 222d841e4ff756cbd23574ace0f22140a093703c Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 6 Aug 2015 00:19:03 -0700 Subject: [PATCH 25/25] on oauth signin only set profile/username if using github --- server/server.js | 55 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/server/server.js b/server/server.js index 93b0a1a322..8a75517964 100755 --- a/server/server.js +++ b/server/server.js @@ -2,12 +2,13 @@ require('dotenv').load(); var pmx = require('pmx'); pmx.init(); -var assign = require('lodash').assign, - loopback = require('loopback'), - boot = require('loopback-boot'), - expressState = require('express-state'), - path = require('path'), - passportProviders = require('./passport-providers'); +var uuid = require('node-uuid'), + assign = require('lodash').assign, + loopback = require('loopback'), + boot = require('loopback-boot'), + expressState = require('express-state'), + path = require('path'), + passportProviders = require('./passport-providers'); var generateKey = require('loopback-component-passport/lib/models/utils').generateKey; @@ -43,6 +44,39 @@ passportConfigurator.setupModels({ userCredentialModel: app.models.userCredential }); +// using es6 argument destructing +function setProfileFromGithub( + user, + { + profileUrl: githubURL, + username + }, + { + location, + email: githubEmail, + id: githubId, + 'created_at': joinedGithubOn, + blog: website, + name + } +) { + return assign( + user, + { isGithubCool: true, isMigrationGrandfathered: false }, + { + name, + username: username.toLowerCase(), + location, + joinedGithubOn, + website, + githubId, + githubURL, + githubEmail, + githubProfile: githubURL + } + ); +} + var passportOptions = { emailOptional: true, profileToUser: function(provider, profile) { @@ -53,9 +87,9 @@ var passportOptions = { emails[0].value : null; - var username = (profile.username || profile.id); - username = typeof username === 'string' ? username.toLowerCase() : - username; + // create random username + // username will be assigned when camper signups for Github + var username = 'fcc' + uuid.v4().slice(0, 8); var password = generateKey('password'); var userObj = { username: username, @@ -65,8 +99,9 @@ var passportOptions = { if (email) { userObj.email = email; } + if (provider === 'github-login') { - userObj.isGithubCool = true; + setProfileFromGithub(userObj, profile, profile._json); } return userObj; }