Merge branch 'staging' of github.com:FreeCodeCamp/freecodecamp into staging
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
public/js/lib/codemirror/* linguist-vendored
|
||||||
|
*.jsx linguist-language=JavaScript
|
@ -1,3 +1,4 @@
|
|||||||
|
import assign from 'object.assign';
|
||||||
import debugFactory from 'debug';
|
import debugFactory from 'debug';
|
||||||
|
|
||||||
const debug = debugFactory('freecc:models:userIdent');
|
const debug = debugFactory('freecc:models:userIdent');
|
||||||
@ -10,6 +11,39 @@ function getFirstImageFromProfile(profile) {
|
|||||||
null;
|
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) {
|
export default function(UserIdent) {
|
||||||
UserIdent.observe('before save', function(ctx, next) {
|
UserIdent.observe('before save', function(ctx, next) {
|
||||||
var userIdent = ctx.currentInstance || ctx.instance;
|
var userIdent = ctx.currentInstance || ctx.instance;
|
||||||
@ -25,7 +59,8 @@ export default function(UserIdent) {
|
|||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
const picture = getFirstImageFromProfile(userIdent.profile);
|
const { profile } = userIdent;
|
||||||
|
const picture = getFirstImageFromProfile(profile);
|
||||||
|
|
||||||
debug('picture', picture, user.picture);
|
debug('picture', picture, user.picture);
|
||||||
// check if picture was found
|
// check if picture was found
|
||||||
@ -41,19 +76,10 @@ export default function(UserIdent) {
|
|||||||
userChanged = true;
|
userChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if user signed in with github
|
// if user signed in with github refresh their info
|
||||||
// and user is not github cool
|
if (userIdent.provider === 'github-login') {
|
||||||
// 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())
|
|
||||||
) {
|
|
||||||
debug("user isn't github cool or username from github is different");
|
debug("user isn't github cool or username from github is different");
|
||||||
user.isGithubCool = true;
|
setProfileFromGithub(user, profile, profile._json);
|
||||||
user.username = userIdent.profile.username.toLowerCase();
|
|
||||||
userChanged = true;
|
userChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,12 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"unique": true
|
"index": {
|
||||||
|
"mongodb": {
|
||||||
|
"unique": true,
|
||||||
|
"background": true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@ -6,7 +6,12 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"unique": true
|
"index": {
|
||||||
|
"mongodb": {
|
||||||
|
"unique": true,
|
||||||
|
"background": true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"whatDoesNonprofitDo": {
|
"whatDoesNonprofitDo": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@ -6,28 +6,29 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"unique": true
|
"index": {
|
||||||
|
"mongodb": {
|
||||||
|
"unique": true,
|
||||||
|
"background": true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"headline": {
|
"headline": {
|
||||||
"type": "string",
|
"type": "string"
|
||||||
"unique": false
|
|
||||||
},
|
},
|
||||||
"timePosted": {
|
"timePosted": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"default": 0
|
"default": 0
|
||||||
},
|
},
|
||||||
"link": {
|
"link": {
|
||||||
"type": "string",
|
"type": "string"
|
||||||
"unique": false
|
|
||||||
},
|
},
|
||||||
"metaDescription": {
|
"metaDescription": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": ""
|
||||||
"unique": false
|
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"type": "string",
|
"type": "string"
|
||||||
"unique": false
|
|
||||||
},
|
},
|
||||||
"originalStoryAuthorEmail": {
|
"originalStoryAuthorEmail": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Observable } from 'rx';
|
import { Observable } from 'rx';
|
||||||
|
import uuid from 'node-uuid';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import debugFactory from 'debug';
|
import debugFactory from 'debug';
|
||||||
|
|
||||||
@ -44,6 +45,16 @@ module.exports = function(User) {
|
|||||||
// username should be unique
|
// username should be unique
|
||||||
User.validatesUniquenessOf('username');
|
User.validatesUniquenessOf('username');
|
||||||
|
|
||||||
|
User.observe('before save', function({ instance: user }, next) {
|
||||||
|
if (user) {
|
||||||
|
user.username = user.username.trim().toLowerCase();
|
||||||
|
user.email = typeof user.email === 'string' ?
|
||||||
|
user.email.trim().toLowerCase() :
|
||||||
|
user.email;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
debug('setting up user hooks');
|
debug('setting up user hooks');
|
||||||
User.afterRemote('confirm', function(ctx) {
|
User.afterRemote('confirm', function(ctx) {
|
||||||
ctx.req.flash('success', {
|
ctx.req.flash('success', {
|
||||||
@ -54,6 +65,11 @@ module.exports = function(User) {
|
|||||||
ctx.res.redirect('/email-signin');
|
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) {
|
User.afterRemote('login', function(ctx, user, next) {
|
||||||
var res = ctx.res;
|
var res = ctx.res;
|
||||||
var req = ctx.req;
|
var req = ctx.req;
|
||||||
|
@ -10,11 +10,10 @@
|
|||||||
"index": {
|
"index": {
|
||||||
"mongodb": {
|
"mongodb": {
|
||||||
"unique": true,
|
"unique": true,
|
||||||
|
"background": true,
|
||||||
"sparse": true
|
"sparse": true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"lowercase": true,
|
|
||||||
"trim": true
|
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -27,11 +26,31 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
|
"githubId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"githubURL": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"githubEmail": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"joinedGithubOn": {
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"isMigrationGrandfathered": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"username": {
|
"username": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"lowercase": true,
|
"require": true,
|
||||||
"trim": true,
|
"index": {
|
||||||
"require": true
|
"mongodb": {
|
||||||
|
"unique": true,
|
||||||
|
"background": true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"bio": {
|
"bio": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -423,7 +423,7 @@
|
|||||||
"description": [
|
"description": [
|
||||||
"",
|
"",
|
||||||
"In JavaScript we can store lists or collections of data in what are called arrays",
|
"In JavaScript we can store lists or collections of data in what are called arrays",
|
||||||
"Arrays are distinguished by the <code> [ </code> and <code> ] </code> around the data. Each piece of data is separated be a <code> , </code>",
|
"Arrays are distinguished by the <code> [ </code> and <code> ] </code> around the data. Each piece of data is separated by a <code> , </code>",
|
||||||
"Now let's create a new array called <code> myArray </code> with a <code> string </code> and a <code> number </code> with a <code> , </code> separating each one",
|
"Now let's create a new array called <code> myArray </code> with a <code> string </code> and a <code> number </code> with a <code> , </code> separating each one",
|
||||||
"Refer to the example if you get stuck",
|
"Refer to the example if you get stuck",
|
||||||
""
|
""
|
||||||
@ -503,7 +503,7 @@
|
|||||||
"difficulty":"9.98171",
|
"difficulty":"9.98171",
|
||||||
"description":[
|
"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:",
|
"Example:",
|
||||||
"<code>",
|
"<code>",
|
||||||
"var ourArray = [1,2,3];",
|
"var ourArray = [1,2,3];",
|
||||||
@ -536,10 +536,10 @@
|
|||||||
"difficulty": "9.9818",
|
"difficulty": "9.9818",
|
||||||
"description": [
|
"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 <code> .pop() </code>",
|
"One common way in which we can manipulate the data in an array is through <code> .pop() </code>",
|
||||||
"<code> .pop() </code> 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.",
|
"<code> .pop() </code> 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 and array",
|
"Any type of variable can be \"popped\" from an array",
|
||||||
"Let's try <code> .pop() </code> now"
|
"Let's try <code> .pop() </code> now"
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
@ -639,13 +639,13 @@
|
|||||||
"description":[
|
"description":[
|
||||||
"",
|
"",
|
||||||
"In JavaScript we can divide up our code into separate and reusable parts called functions",
|
"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",
|
||||||
"<code>",
|
"<code>",
|
||||||
"function functionName (a, b){",
|
"function functionName (a, b){",
|
||||||
" return(a + b);",
|
" return(a + b);",
|
||||||
"}",
|
"}",
|
||||||
"</code>",
|
"</code>",
|
||||||
"our function can be called like this",
|
"Our function can be called like this",
|
||||||
"<code>functionName();</code>",
|
"<code>functionName();</code>",
|
||||||
"Let's try creating and calling a function now called <code>myFunction</code>"
|
"Let's try creating and calling a function now called <code>myFunction</code>"
|
||||||
],
|
],
|
||||||
@ -677,7 +677,7 @@
|
|||||||
"description":[
|
"description":[
|
||||||
"",
|
"",
|
||||||
"A very important data type in javascript is the <code> Object </code>",
|
"A very important data type in javascript is the <code> Object </code>",
|
||||||
"<code> Objects </code> a similar to <code> arrays </code> except that instead of using indexes to access and modify their data, Objects have what are called <code> properties </code>",
|
"<code> Objects </code> are similar to <code> arrays </code> except that instead of using indexes to access and modify their data, Objects have what are called <code> properties </code>",
|
||||||
"Here's a sample Object",
|
"Here's a sample Object",
|
||||||
"<code>",
|
"<code>",
|
||||||
"var cat = {",
|
"var cat = {",
|
||||||
@ -688,7 +688,7 @@
|
|||||||
"};",
|
"};",
|
||||||
"</code>",
|
"</code>",
|
||||||
"Objects are useful for storing data in a structured way or in a way that represents a real world object like a cat.",
|
"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":[
|
"tests":[
|
||||||
|
@ -1883,7 +1883,7 @@
|
|||||||
"Above your right-well, inside its \"col-xs-6\" <code>div</code> element, add a <code>h4</code> element with the text \"#right-well\"."
|
"Above your right-well, inside its \"col-xs-6\" <code>div</code> element, add a <code>h4</code> element with the text \"#right-well\"."
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert($('.col-xs-12').children('h4') && $('.col-xs-12').children('h4').length > 1, 'Add an <code>h4</code> element to each of your <code><div class=\\'col-xs-6\\'></code> elements.');",
|
"assert($('.col-xs-6').children('h4') && $('.col-xs-6').children('h4').length > 1, 'Add an <code>h4</code> element to each of your <code><div class=\\'col-xs-6\\'></code> elements.');",
|
||||||
"assert(new RegExp('#left-well','gi').test($('h4').text()), 'One <code>h4</code> element should have the text \"#left-well\".');",
|
"assert(new RegExp('#left-well','gi').test($('h4').text()), 'One <code>h4</code> element should have the text \"#left-well\".');",
|
||||||
"assert(new RegExp('#right-well','gi').test($('h4').text()), 'One <code>h4</code> element should have the text \"#right-well\".');"
|
"assert(new RegExp('#right-well','gi').test($('h4').text()), 'One <code>h4</code> element should have the text \"#right-well\".');"
|
||||||
],
|
],
|
||||||
@ -2036,9 +2036,9 @@
|
|||||||
"Add a comment at the top of your HTML that says <code>You shouldn't need to modify code below this line</code>."
|
"Add a comment at the top of your HTML that says <code>You shouldn't need to modify code below this line</code>."
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert(editor.match(/<!--/g).length > 1, 'Start a comment with <code><!--</code>.')",
|
"assert(editor.match(/<!--/g) && editor.match(/<!--/g).length > 0, 'Start a comment with <code><!--</code>.')",
|
||||||
"assert(editor.match(/this line/g).length > 1, 'Your comment should have the text <code>You shouldn't need to modify code below this line</code>')",
|
"assert(editor.match(/this line/g) && editor.match(/this line/g).length > 0, \"Your comment should have the text <code>You shouldn't need to modify code below this line</code>\")",
|
||||||
"assert(editor.match(/-->/g).length > 1, 'Be sure to close your comment with <code>--></code>.')"
|
"assert(editor.match(/-->/g) && editor.match(/-->/g).length > 0, 'Be sure to close your comment with <code>--></code>.')"
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"<div class='container-fluid'>",
|
"<div class='container-fluid'>",
|
||||||
|
@ -103,7 +103,7 @@
|
|||||||
"difficulty": "2.02",
|
"difficulty": "2.02",
|
||||||
"description": [
|
"description": [
|
||||||
"Convert the given number into a roman numeral.",
|
"Convert the given number into a roman numeral.",
|
||||||
"All <a href=\"http://www.mathsisfun.com/roman-numerals.html\">roman numerals</a> answers should be provided in upper-case.",
|
"All <a href=\"http://www.mathsisfun.com/roman-numerals.html\" target="_blank">roman numerals</a> answers should be provided in upper-case.",
|
||||||
"Remember to use <a href='//github.com/FreeCodeCamp/freecodecamp/wiki/How-to-get-help-when-you-get-stuck' target='_blank'>RSAP</a> if you get stuck. Try to pair program. Write your own code."
|
"Remember to use <a href='//github.com/FreeCodeCamp/freecodecamp/wiki/How-to-get-help-when-you-get-stuck' target='_blank'>RSAP</a> if you get stuck. Try to pair program. Write your own code."
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
@ -356,7 +356,7 @@
|
|||||||
" return arr1;",
|
" return arr1;",
|
||||||
"}",
|
"}",
|
||||||
"",
|
"",
|
||||||
"unite([1, 2, 3], [5, 2, 1, 4], [2, 1]);"
|
"unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);"
|
||||||
],
|
],
|
||||||
"tests": [
|
"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');",
|
"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');",
|
||||||
@ -675,7 +675,8 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
"assert.deepEqual(steamroller([[['a']], [['b']]]), ['a', 'b'], 'should flatten nested arrays');",
|
"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, [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": [
|
"MDNlinks": [
|
||||||
"Array.isArray()"
|
"Array.isArray()"
|
||||||
|
@ -257,9 +257,9 @@
|
|||||||
"dashedName": "waypoint-target-the-same-element-with-multiple-jQuery-Selectors",
|
"dashedName": "waypoint-target-the-same-element-with-multiple-jQuery-Selectors",
|
||||||
"difficulty": 3.06,
|
"difficulty": 3.06,
|
||||||
"description": [
|
"description": [
|
||||||
"Now you know three ways of targeting elements: by type (<code>$('button')</code>), by class (<code>($('.btn')</code>), and by id (<code>($'#target1')</code>).",
|
"Now you know three ways of targeting elements: by type <code>$('button')</code>, by class <code>$('.btn')</code>), and by id <code>$('#target1')</code>).",
|
||||||
"Use each of these jQuery selectors to target your <code>button</code> element with the class \"btn\" and the id \"target1\".",
|
"Use each of these jQuery selectors to target your <code>button</code> element with the class \"btn\" and the id \"target1\".",
|
||||||
"Use the <code>addClass()</code> jQuery function to give the element one new class for each selector: \"animated\", \"shake\", and \"button-primary\"."
|
"Use the <code>addClass()</code> jQuery function to give the element one new class for each selector: \"animated\", \"shake\", and \"btn-primary\"."
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert(editor.match(/\\$\\(\\'button\\'\\)/g), 'Use the <code>$\\(\\'button\\'\\)</code> selector.')",
|
"assert(editor.match(/\\$\\(\\'button\\'\\)/g), 'Use the <code>$\\(\\'button\\'\\)</code> selector.')",
|
||||||
|
@ -92,6 +92,8 @@ var users = dbObservable
|
|||||||
user.username = 'fcc' + uuid.v4().slice(0, 8);
|
user.username = 'fcc' + uuid.v4().slice(0, 8);
|
||||||
if (user.github) {
|
if (user.github) {
|
||||||
user.isGithubCool = true;
|
user.isGithubCool = true;
|
||||||
|
} else {
|
||||||
|
user.isMigrationGrandfathered = true;
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
})
|
})
|
||||||
|
@ -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'),
|
var R = require('ramda'),
|
||||||
Rx = require('rx'),
|
Rx = require('rx'),
|
||||||
assign = require('object.assign'),
|
assign = require('object.assign'),
|
||||||
|
@ -122,13 +122,28 @@ module.exports = function(app) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function returnUser(req, res, next) {
|
function returnUser(req, res, next) {
|
||||||
|
const username = req.params.username.toLowerCase();
|
||||||
|
const { path } = req;
|
||||||
User.findOne(
|
User.findOne(
|
||||||
{ where: { 'username': req.params.username.toLowerCase() } },
|
{ where: { username } },
|
||||||
function(err, user) {
|
function(err, user) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
if (user) {
|
if (!user) {
|
||||||
|
req.flash('errors', {
|
||||||
|
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 =
|
||||||
user.progressTimestamps.sort(function(a, b) {
|
user.progressTimestamps.sort(function(a, b) {
|
||||||
return a - b;
|
return a - b;
|
||||||
@ -194,17 +209,19 @@ module.exports = function(app) {
|
|||||||
progressTimestamps.forEach(function(timeStamp) {
|
progressTimestamps.forEach(function(timeStamp) {
|
||||||
data[(timeStamp / 1000)] = 1;
|
data[(timeStamp / 1000)] = 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
user.currentStreak = user.currentStreak || 1;
|
|
||||||
user.longestStreak = user.longestStreak || 1;
|
|
||||||
var challenges = user.completedChallenges.filter(function( obj ) {
|
var challenges = user.completedChallenges.filter(function( obj ) {
|
||||||
return obj.challengeType === 3 || obj.challengeType === 4;
|
return obj.challengeType === 3 || obj.challengeType === 4;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
user.currentStreak = user.currentStreak || 1;
|
||||||
|
user.longestStreak = user.longestStreak || 1;
|
||||||
|
|
||||||
res.render('account/show', {
|
res.render('account/show', {
|
||||||
title: 'Camper ' + user.username + '\'s portfolio',
|
title: 'Camper ' + user.username + '\'s portfolio',
|
||||||
username: user.username,
|
username: user.username,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
|
isMigrationGrandfathered: user.isMigrationGrandfathered,
|
||||||
|
isGithubCool: user.isGithubCool,
|
||||||
location: user.location,
|
location: user.location,
|
||||||
githubProfile: user.githubProfile,
|
githubProfile: user.githubProfile,
|
||||||
linkedinProfile: user.linkedinProfile,
|
linkedinProfile: user.linkedinProfile,
|
||||||
@ -214,21 +231,14 @@ module.exports = function(app) {
|
|||||||
bio: user.bio,
|
bio: user.bio,
|
||||||
picture: user.picture,
|
picture: user.picture,
|
||||||
progressTimestamps: user.progressTimestamps,
|
progressTimestamps: user.progressTimestamps,
|
||||||
challenges: challenges,
|
|
||||||
calender: data,
|
calender: data,
|
||||||
|
challenges: challenges,
|
||||||
moment: moment,
|
moment: moment,
|
||||||
longestStreak: user.longestStreak +
|
longestStreak: user.longestStreak +
|
||||||
(user.longestStreak === 1 ? ' day' : ' days'),
|
(user.longestStreak === 1 ? ' day' : ' days'),
|
||||||
currentStreak: user.currentStreak +
|
currentStreak: user.currentStreak +
|
||||||
(user.currentStreak === 1 ? ' day' : ' days')
|
(user.currentStreak === 1 ? ' day' : ' days')
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
req.flash('errors', {
|
|
||||||
msg: "404: We couldn't find a page with that url. " +
|
|
||||||
'Please double check the link.'
|
|
||||||
});
|
|
||||||
return res.redirect('/');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ require('dotenv').load();
|
|||||||
var pmx = require('pmx');
|
var pmx = require('pmx');
|
||||||
pmx.init();
|
pmx.init();
|
||||||
|
|
||||||
var assign = require('lodash').assign,
|
var uuid = require('node-uuid'),
|
||||||
|
assign = require('lodash').assign,
|
||||||
loopback = require('loopback'),
|
loopback = require('loopback'),
|
||||||
boot = require('loopback-boot'),
|
boot = require('loopback-boot'),
|
||||||
expressState = require('express-state'),
|
expressState = require('express-state'),
|
||||||
@ -43,6 +44,39 @@ passportConfigurator.setupModels({
|
|||||||
userCredentialModel: app.models.userCredential
|
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 = {
|
var passportOptions = {
|
||||||
emailOptional: true,
|
emailOptional: true,
|
||||||
profileToUser: function(provider, profile) {
|
profileToUser: function(provider, profile) {
|
||||||
@ -53,9 +87,9 @@ var passportOptions = {
|
|||||||
emails[0].value :
|
emails[0].value :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
var username = (profile.username || profile.id);
|
// create random username
|
||||||
username = typeof username === 'string' ? username.toLowerCase() :
|
// username will be assigned when camper signups for Github
|
||||||
username;
|
var username = 'fcc' + uuid.v4().slice(0, 8);
|
||||||
var password = generateKey('password');
|
var password = generateKey('password');
|
||||||
var userObj = {
|
var userObj = {
|
||||||
username: username,
|
username: username,
|
||||||
@ -65,8 +99,9 @@ var passportOptions = {
|
|||||||
if (email) {
|
if (email) {
|
||||||
userObj.email = email;
|
userObj.email = email;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provider === 'github-login') {
|
if (provider === 'github-login') {
|
||||||
userObj.isGithubCool = true;
|
setProfileFromGithub(userObj, profile, profile._json);
|
||||||
}
|
}
|
||||||
return userObj;
|
return userObj;
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,18 @@ block content
|
|||||||
.panel.panel-info(ng-controller="profileValidationController")
|
.panel.panel-info(ng-controller="profileValidationController")
|
||||||
.panel-heading.text-center Update your portfolio here:
|
.panel-heading.text-center Update your portfolio here:
|
||||||
.panel-body
|
.panel-body
|
||||||
if (!user.github)
|
if (!user.isGithubCool)
|
||||||
|
.row
|
||||||
|
.col-xs-12
|
||||||
|
.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
|
.col-xs-12
|
||||||
a.btn.btn-lg.btn-block.btn-github.btn-link-social(href='/auth/github')
|
a.btn.btn-lg.btn-block.btn-github.btn-link-social(href='/auth/github')
|
||||||
i.fa.fa-github
|
i.fa.fa-github
|
||||||
| Link GitHub with my account
|
| Link GitHub with my account
|
||||||
|
|
||||||
|
.row
|
||||||
.col-xs-12
|
.col-xs-12
|
||||||
form.form-horizontal(action='/account/profile', method='POST', novalidate='novalidate', name='profileForm' ng-show="asyncComplete")
|
form.form-horizontal(action='/account/profile', method='POST', novalidate='novalidate', name='profileForm' ng-show="asyncComplete")
|
||||||
input(type='hidden', name='_csrf', value=_csrf)
|
input(type='hidden', name='_csrf', value=_csrf)
|
||||||
@ -63,7 +70,7 @@ block content
|
|||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
| Please enter a valid URL format (http://www.example.com).
|
| 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')
|
button.btn.btn-lg.btn-block.btn-primary.btn-link-social(type='submit', ng-disabled='!user.isGithubCool || profileForm.$invalid')
|
||||||
span.ion-edit
|
span.ion-edit
|
||||||
| Update my info
|
| Update my info
|
||||||
|
|
||||||
|
@ -19,25 +19,6 @@ block content
|
|||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
| That email address is already in use.
|
| 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
|
.form-group
|
||||||
.col-sm-6.col-sm-offset-3
|
.col-sm-6.col-sm-offset-3
|
||||||
input.form-control(type='password', ng-model='password', name='password', id='password', placeholder='password', required, ng-minlength=8)
|
input.form-control(type='password', ng-model='password', name='password', id='password', placeholder='password', required, ng-minlength=8)
|
||||||
|
@ -21,15 +21,7 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height
|
|||||||
a.btn.signup-btn.signup-btn-nav(href='/login') Sign in
|
a.btn.signup-btn.signup-btn-nav(href='/login') Sign in
|
||||||
else
|
else
|
||||||
li
|
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
|
.hidden-xs.hidden-sm
|
||||||
if (user.username)
|
|
||||||
a(href='/' + user.username)
|
|
||||||
img.profile-picture.float-right(src='#{user.picture}')
|
|
||||||
else
|
|
||||||
a(href='/account')
|
a(href='/account')
|
||||||
img.profile-picture.float-right(src='#{user.picture}')
|
img.profile-picture.float-right(src='#{user.picture}')
|
||||||
|
Reference in New Issue
Block a user