Merge remote-tracking branch 'origin/staging' into staging
This commit is contained in:
@ -1,14 +1,16 @@
|
||||
var debug = require('debug')('freecc:models:userIdent');
|
||||
import debugFactory from 'debug';
|
||||
|
||||
var defaultProfileImage =
|
||||
require('../utils/constantStrings.json').defaultProfileImage;
|
||||
const debug = debugFactory('freecc:models:userIdent');
|
||||
|
||||
const { defaultProfileImage } = require('../utils/constantStrings.json');
|
||||
|
||||
function getFirstImageFromProfile(profile) {
|
||||
return profile && profile.photos && profile.photos[0] ?
|
||||
profile.photos[0].value :
|
||||
null;
|
||||
}
|
||||
module.exports = function(UserIdent) {
|
||||
|
||||
export default function(UserIdent) {
|
||||
UserIdent.observe('before save', function(ctx, next) {
|
||||
var userIdent = ctx.currentInstance || ctx.instance;
|
||||
if (!userIdent) {
|
||||
@ -16,13 +18,14 @@ module.exports = function(UserIdent) {
|
||||
return next();
|
||||
}
|
||||
userIdent.user(function(err, user) {
|
||||
let userChanged = false;
|
||||
if (err) { return next(err); }
|
||||
if (!user) {
|
||||
debug('no user attached to identity!');
|
||||
return next();
|
||||
}
|
||||
|
||||
var picture = getFirstImageFromProfile(userIdent.profile);
|
||||
const picture = getFirstImageFromProfile(userIdent.profile);
|
||||
|
||||
debug('picture', picture, user.picture);
|
||||
// check if picture was found
|
||||
@ -34,15 +37,35 @@ module.exports = function(UserIdent) {
|
||||
(!user.picture || user.picture === defaultProfileImage)
|
||||
) {
|
||||
debug('setting user picture');
|
||||
user.picture = userIdent.profile.photos[0].value;
|
||||
user.picture = picture;
|
||||
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())
|
||||
) {
|
||||
debug("user isn't github cool or username from github is different");
|
||||
user.isGithubCool = true;
|
||||
user.username = userIdent.profile.username.toLowerCase();
|
||||
userChanged = true;
|
||||
}
|
||||
|
||||
|
||||
if (userChanged) {
|
||||
return user.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
debug('exiting after user ident');
|
||||
debug('exiting after user identity before save');
|
||||
next();
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -161,9 +161,9 @@ module.exports = function(User) {
|
||||
if (!username) {
|
||||
// Zalgo!!
|
||||
return nextTick(() => {
|
||||
cb(
|
||||
new TypeError('FCC: username should be a string but got %s', username)
|
||||
);
|
||||
cb(new TypeError(
|
||||
`username should be a string but got ${ username }`
|
||||
));
|
||||
});
|
||||
}
|
||||
User.findOne({ where: { username } }, (err, user) => {
|
||||
@ -171,7 +171,7 @@ module.exports = function(User) {
|
||||
return cb(err);
|
||||
}
|
||||
if (!user || user.username !== username) {
|
||||
return cb(new Error('FCC: no user found for %s', username));
|
||||
return cb(new Error(`no user found for ${ username }`));
|
||||
}
|
||||
const aboutUser = getAboutProfile(user);
|
||||
return cb(null, aboutUser);
|
||||
|
@ -19,28 +19,14 @@
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"facebook": {
|
||||
"type": "string"
|
||||
},
|
||||
"twitter": {
|
||||
"type": "string"
|
||||
},
|
||||
"google": {
|
||||
"type": "string"
|
||||
},
|
||||
"github": {
|
||||
"type": "string"
|
||||
},
|
||||
"linkedin": {
|
||||
"type": "string"
|
||||
},
|
||||
"tokens": {
|
||||
"type": "array"
|
||||
},
|
||||
"progressTimestamps": {
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"isGithubCool": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"lowercase": true,
|
||||
@ -87,48 +73,6 @@
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"website1Link": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"website1Title": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"website1Image": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"website2Link": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"website2Title": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"website2Image": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"website3Link": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"website3Title": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"website3Image": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"resetPasswordToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"resetPasswordExpires": {
|
||||
"type": "string"
|
||||
},
|
||||
"completedBonfires": {
|
||||
"type": [
|
||||
{
|
||||
@ -170,21 +114,10 @@
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"needsSomeDataModeled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"needsMigration": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"sendMonthlyEmail": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"challengesHash": {
|
||||
"type": {}
|
||||
},
|
||||
"currentChallenge": {
|
||||
"type": {}
|
||||
},
|
||||
@ -205,10 +138,6 @@
|
||||
}
|
||||
],
|
||||
"default": []
|
||||
},
|
||||
"uncompletedChallenges": {
|
||||
"type": "array",
|
||||
"default": []
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
|
@ -577,6 +577,7 @@ thead {
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
@ -612,6 +613,11 @@ thead {
|
||||
color: #009900
|
||||
}
|
||||
|
||||
.default-border-radius {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
||||
.testimonial-copy {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
|
@ -12,7 +12,7 @@
|
||||
"Bootstrap will figure out how wide your screen is and respond by resizing your HTML elements - hence the name <code>Responsive Design</code>.",
|
||||
"With responsive design, there is no need to design a mobile version of your website. It will look good on devices with screens of any width.",
|
||||
"You can add Bootstrap to any app just by including it with <code><link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css'/></code> at the top of your HTML. But we've gone ahead and automatically added it to your Cat Photo App for you.",
|
||||
"To get started, we should nest all of our HTML in a <code>div</code> element with the class \"fluid-container\"."
|
||||
"To get started, we should nest all of our HTML in a <code>div</code> element with the class \"container-fluid\"."
|
||||
],
|
||||
"tests": [
|
||||
"assert($('div').hasClass('container-fluid'), 'Your <code>div</code> element should have the class \"row\"')",
|
||||
@ -1566,7 +1566,7 @@
|
||||
"difficulty": 2.18,
|
||||
"description": [
|
||||
"Now let's make sure all the content on your page is mobile-responsive.",
|
||||
"Let's nest your <code>h3</code> element within a <code>div<code> element with the class \"containter-fluid\"."
|
||||
"Let's nest your <code>h3</code> element within a <code>div<code> element with the class \"container-fluid\"."
|
||||
],
|
||||
"tests": [
|
||||
"assert($('div').hasClass('container-fluid'), 'Your <code>div</code> element should have the class \"container-fluid\"')",
|
||||
@ -1743,7 +1743,7 @@
|
||||
],
|
||||
"tests": [
|
||||
"assert($('.btn').length > 5, 'Apply the \"btn\" class to each of your <code>button</code> elements.')",
|
||||
"assert($('.btn').length > 5, 'Apply the \"btn-default\" class to each of your <code>button</code> elements.')"
|
||||
"assert($('.btn-default').length > 5, 'Apply the \"btn-default\" class to each of your <code>button</code> elements.')"
|
||||
],
|
||||
"challengeSeed": [
|
||||
"<div class='container-fluid'>",
|
||||
|
@ -4,7 +4,7 @@ module.exports = function(app) {
|
||||
router.get('/nonprofit-project-instructions', function(req, res) {
|
||||
res.redirect(
|
||||
301,
|
||||
"https://github.com/FreeCodeCamp/freecodecamp/wiki/How-Free-Code-Camp's-Nonprofit-Projects-work"
|
||||
"//github.com/FreeCodeCamp/freecodecamp/wiki/How-Free-Code-Camp's-Nonprofit-Projects-work"
|
||||
);
|
||||
});
|
||||
|
||||
@ -14,7 +14,7 @@ module.exports = function(app) {
|
||||
|
||||
router.get('/privacy', function(req, res) {
|
||||
res.redirect(
|
||||
301, "https://github.com/FreeCodeCamp/freecodecamp/wiki/Free-Code-Camp's-Privacy-Policy"
|
||||
301, "//github.com/FreeCodeCamp/freecodecamp/wiki/Free-Code-Camp's-Privacy-Policy"
|
||||
);
|
||||
});
|
||||
|
||||
@ -22,6 +22,10 @@ module.exports = function(app) {
|
||||
res.redirect(301, '/map');
|
||||
});
|
||||
|
||||
router.get('/field-guide/*', function(req, res) {
|
||||
res.redirect(302, '//github.com/freecodecamp/freecodecamp/wiki')
|
||||
});
|
||||
|
||||
router.get('/about', function(req, res) {
|
||||
res.redirect(301, '/map');
|
||||
});
|
||||
|
@ -214,19 +214,7 @@ module.exports = function(app) {
|
||||
bio: user.bio,
|
||||
picture: user.picture,
|
||||
progressTimestamps: user.progressTimestamps,
|
||||
website1Link: user.website1Link,
|
||||
website1Title: user.website1Title,
|
||||
website1Image: user.website1Image,
|
||||
website2Link: user.website2Link,
|
||||
website2Title: user.website2Title,
|
||||
website2Image: user.website2Image,
|
||||
website3Link: user.website3Link,
|
||||
website3Title: user.website3Title,
|
||||
website3Image: user.website3Image,
|
||||
challenges: challenges,
|
||||
bonfires: user.completedChallenges.filter(function(challenge) {
|
||||
return challenge.challengeType === 5;
|
||||
}),
|
||||
calender: data,
|
||||
moment: moment,
|
||||
longestStreak: user.longestStreak +
|
||||
@ -290,35 +278,12 @@ module.exports = function(app) {
|
||||
return res.redirect('/account');
|
||||
}
|
||||
var body = req.body || {};
|
||||
user.email = body.email.trim() || '';
|
||||
user.name = body.name.trim() || '';
|
||||
user.username = body.username.trim() || '';
|
||||
user.location = body.location.trim() || '';
|
||||
|
||||
user.githubProfile = body.githubProfile.trim() || '';
|
||||
user.facebookProfile = body.facebookProfile.trim() || '';
|
||||
user.linkedinProfile = body.linkedinProfile.trim() || '';
|
||||
|
||||
user.codepenProfile = body.codepenProfile.trim() || '';
|
||||
user.twitterHandle = body.twitterHandle.trim() || '';
|
||||
user.bio = body.bio.trim() || '';
|
||||
|
||||
user.picture = body.picture.trim() ||
|
||||
'https://s3.amazonaws.com/freecodecamp/' +
|
||||
'camper-image-placeholder.png';
|
||||
user.website1Title = body.website1Title.trim() || '';
|
||||
user.website1Link = body.website1Link.trim() || '';
|
||||
user.website1Image = body.website1Image.trim() || '';
|
||||
|
||||
user.website2Title = body.website2Title.trim() || '';
|
||||
user.website2Link = body.website2Link.trim() || '';
|
||||
user.website2Image = body.website2Image.trim() || '';
|
||||
|
||||
user.website3Title = body.website3Title.trim() || '';
|
||||
user.website3Link = body.website3Link.trim() || '';
|
||||
user.website3Image = body.website3Image.trim() || '';
|
||||
|
||||
|
||||
user.save(function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
|
172
server/server.js
172
server/server.js
@ -3,30 +3,11 @@ var pmx = require('pmx');
|
||||
pmx.init();
|
||||
|
||||
var assign = require('lodash').assign,
|
||||
loopback = require('loopback'),
|
||||
boot = require('loopback-boot'),
|
||||
accepts = require('accepts'),
|
||||
cookieParser = require('cookie-parser'),
|
||||
compress = require('compression'),
|
||||
session = require('express-session'),
|
||||
expressState = require('express-state'),
|
||||
logger = require('morgan'),
|
||||
errorHandler = require('errorhandler'),
|
||||
methodOverride = require('method-override'),
|
||||
bodyParser = require('body-parser'),
|
||||
helmet = require('helmet'),
|
||||
MongoStore = require('connect-mongo')(session),
|
||||
flash = require('express-flash'),
|
||||
path = require('path'),
|
||||
expressValidator = require('express-validator'),
|
||||
lessMiddleware = require('less-middleware'),
|
||||
|
||||
passportProviders = require('./passport-providers'),
|
||||
rxMiddleware = require('./utils/rx').rxMiddleware,
|
||||
/**
|
||||
* API keys and Passport configuration.
|
||||
*/
|
||||
secrets = require('./../config/secrets');
|
||||
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;
|
||||
@ -45,147 +26,9 @@ var passportConfigurator = new PassportConfigurator(app);
|
||||
app.set('port', process.env.PORT || 3000);
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
app.use(compress());
|
||||
app.use(lessMiddleware(path.join(__dirname, '/public')));
|
||||
app.use(logger('dev'));
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({
|
||||
extended: true
|
||||
}));
|
||||
app.use(expressValidator({
|
||||
customValidators: {
|
||||
matchRegex: function(param, regex) {
|
||||
return regex.test(param);
|
||||
}
|
||||
}
|
||||
}));
|
||||
app.use(methodOverride());
|
||||
app.use(cookieParser(secrets.cookieSecret));
|
||||
app.use(session({
|
||||
resave: true,
|
||||
saveUninitialized: true,
|
||||
secret: secrets.sessionSecret,
|
||||
store: new MongoStore({
|
||||
url: secrets.db,
|
||||
'autoReconnect': true
|
||||
})
|
||||
}));
|
||||
|
||||
app.use(flash());
|
||||
app.disable('x-powered-by');
|
||||
|
||||
// adds passport initialization after session middleware phase is complete
|
||||
|
||||
app.use(helmet.xssFilter());
|
||||
app.use(helmet.noSniff());
|
||||
app.use(helmet.frameguard());
|
||||
app.use(function(req, res, next) {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Headers',
|
||||
'Origin, X-Requested-With, Content-Type, Accept'
|
||||
);
|
||||
next();
|
||||
});
|
||||
|
||||
var trusted = [
|
||||
"'self'",
|
||||
'blob:',
|
||||
'104.236.218.15',
|
||||
'*.freecodecamp.com',
|
||||
'http://www.freecodecamp.com',
|
||||
'https://www.freecodecamp.com',
|
||||
'https://freecodecamp.com',
|
||||
'https://freecodecamp.org',
|
||||
'*.freecodecamp.org',
|
||||
// NOTE(berks): add the following as the blob above was not covering www
|
||||
'http://www.freecodecamp.org',
|
||||
'ws://freecodecamp.com/',
|
||||
'ws://www.freecodecamp.com/',
|
||||
'*.gstatic.com',
|
||||
'*.google-analytics.com',
|
||||
'*.googleapis.com',
|
||||
'*.google.com',
|
||||
'*.gstatic.com',
|
||||
'*.doubleclick.net',
|
||||
'*.twitter.com',
|
||||
'*.twitch.tv',
|
||||
'*.twimg.com',
|
||||
"'unsafe-eval'",
|
||||
"'unsafe-inline'",
|
||||
'*.bootstrapcdn.com',
|
||||
'*.cloudflare.com',
|
||||
'https://*.cloudflare.com',
|
||||
'localhost:3001',
|
||||
'ws://localhost:3001/',
|
||||
'http://localhost:3001',
|
||||
'localhost:3000',
|
||||
'ws://localhost:3000/',
|
||||
'http://localhost:3000',
|
||||
'127.0.0.1',
|
||||
'127.0.0.1:3000',
|
||||
'ws://127.0.0.1:3000/',
|
||||
'http://127.0.0.1:3000',
|
||||
'*.ionicframework.com',
|
||||
'https://syndication.twitter.com',
|
||||
'*.youtube.com',
|
||||
'*.jsdelivr.net',
|
||||
'https://*.jsdelivr.net',
|
||||
'*.ytimg.com',
|
||||
'*.bitly.com',
|
||||
'http://cdn.inspectlet.com/',
|
||||
'https://cdn.inspeclet.com/',
|
||||
'wss://inspectletws.herokuapp.com/',
|
||||
'http://hn.inspectlet.com/',
|
||||
'*.googleapis.com',
|
||||
'*.gstatic.com',
|
||||
'https://hn.inspectlet.com/',
|
||||
'http://*.github.com'
|
||||
];
|
||||
|
||||
app.use(helmet.csp({
|
||||
defaultSrc: trusted,
|
||||
scriptSrc: [
|
||||
'*.optimizely.com',
|
||||
'*.aspnetcdn.com',
|
||||
'*.d3js.org',
|
||||
'https://cdn.inspectlet.com/inspectlet.js',
|
||||
'http://cdn.inspectlet.com/inspectlet.js',
|
||||
'http://beta.freecodecamp.com'
|
||||
].concat(trusted),
|
||||
'connect-src': [
|
||||
'vimeo.com'
|
||||
].concat(trusted),
|
||||
styleSrc: [
|
||||
'*.googleapis.com',
|
||||
'*.gstatic.com'
|
||||
].concat(trusted),
|
||||
imgSrc: [
|
||||
/* allow all input since we have user submitted images for public profile*/
|
||||
'*'
|
||||
].concat(trusted),
|
||||
fontSrc: [
|
||||
'*.googleapis.com',
|
||||
'*.gstatic.com'
|
||||
].concat(trusted),
|
||||
mediaSrc: [
|
||||
'*.amazonaws.com',
|
||||
'*.twitter.com'
|
||||
].concat(trusted),
|
||||
frameSrc: [
|
||||
'*.gitter.im',
|
||||
'*.gitter.im https:',
|
||||
'*.vimeo.com',
|
||||
'*.twitter.com',
|
||||
'*.ghbtns.com'
|
||||
].concat(trusted),
|
||||
// set to true if you only want to report errors
|
||||
reportOnly: false,
|
||||
// set to true if you want to set all headers
|
||||
setAllHeaders: false,
|
||||
// set to true if you want to force buggy CSP in Safari 5
|
||||
safari5: false
|
||||
}));
|
||||
|
||||
passportConfigurator.init();
|
||||
|
||||
boot(app, {
|
||||
@ -222,6 +65,9 @@ var passportOptions = {
|
||||
if (email) {
|
||||
userObj.email = email;
|
||||
}
|
||||
if (provider === 'github-login') {
|
||||
userObj.isGithubCool = true;
|
||||
}
|
||||
return userObj;
|
||||
}
|
||||
};
|
||||
|
@ -2,108 +2,27 @@ extends ../layout
|
||||
block content
|
||||
script.
|
||||
var challengeName = 'Account View'
|
||||
.panel.panel-info.min-height-1000(ng-controller="profileValidationController")
|
||||
.panel.panel-info(ng-controller="profileValidationController")
|
||||
.panel-heading.text-center Update your portfolio here:
|
||||
.panel-body
|
||||
.container.text-center
|
||||
if (!user.github)
|
||||
.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)
|
||||
|
||||
.col-sm-4.col-sm-offset-5
|
||||
h2 Bio
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='name') Name *
|
||||
.col-sm-4
|
||||
input.form-control(type='text', placeholder='Name', name='name', autocomplete="off", ng-model='user.name', ng-minlength='3', ng-maxlength='50', required='required', id='name')
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.name.$invalid && profileForm.name.$error.required")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled(id='#name-error')
|
||||
| Your name is required.
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.name.$error.minlength && !profileForm.name.$pristine')
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Your name must be at least 3 characters.
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show='profileForm.name.$error.maxlength && !profileForm.name.$pristine')
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Your name must be fewer than 50 characters.
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='username') Username (path to public profile) *
|
||||
.col-sm-4
|
||||
input.form-control(type='text', placeholder='username' name='username', autocomplete="off", id='username', ng-model='user.username', required='required', ng-minlength='5', ng-maxlength='20', ng-keypress='', unique-username='', ng-pattern="/^[A-z0-9_]+$/")
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.username.$error.pattern")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Your username should only contain letters, numbers and underscores (az10_).
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.username.$error.required")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Your username is required.
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.username.$error.minlength && !profileForm.username.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Your username must be at least 5 characters.
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.username.$error.maxlength && !profileForm.username.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Your username must be fewer than 15 characters.
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.username.$error.unique && !profileForm.username.$pristine && $scope.storedUsername !== user.username")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| That username is already in use.
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') Email *
|
||||
.col-sm-4
|
||||
input.form-control(type='email', name='email', id='email', autocomplete="off", ng-model='user.email', required='required', ng-keypress='', unique-email='')
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.email.$error.required")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Your email address is required.
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.$error.email && !profileForm.email.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Please enter a valid email format.
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.email.$error.unique && !profileForm.email.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| That email is already in use.
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='location') Location
|
||||
.col-sm-4
|
||||
input.form-control(type='text', name='location', autocomplete="off", id='location', ng-model='user.location')
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') Link to Profile Photo (1:1 ratio)
|
||||
.col-sm-4
|
||||
input.form-control(type='url', name='picture', id='picture', ng-model='user.picture', placeholder='http://www.example.com/image.jpg')
|
||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.picture.$error.url && !profileForm.picture.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Please enter a valid URL format (http://www.example.com/image.jpg).
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='bio') Bio (140 characters)
|
||||
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
|
||||
.col-sm-offset-5.col-sm-4
|
||||
button.btn.btn-primary.btn-block(type='submit', ng-disabled='profileForm.$invalid')
|
||||
span.ion-edit
|
||||
| Update my Bio
|
||||
|
||||
.col-sm-4.col-sm-offset-5
|
||||
h2 Social Profiles
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') Twitter
|
||||
label.col-sm-3.col-sm-offset-1.control-label(for='email') Twitter
|
||||
.col-sm-4
|
||||
.input-group.twitter-input
|
||||
span.input-group-addon @
|
||||
@ -116,17 +35,9 @@ block content
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Your name must be fewer than 15 characters.
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') GitHub
|
||||
.col-sm-4
|
||||
input.form-control(type='url', name='githubProfile', id='githubProfile', autocomplete="off", ng-model='user.githubProfile', placeholder='http://')
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.githubProfile.$error.url && !profileForm.githubProfile.$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-2.control-label(for='email') CodePen
|
||||
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")
|
||||
@ -135,7 +46,7 @@ block content
|
||||
| Please enter a valid URL format (http://www.example.com).
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') LinkedIn
|
||||
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")
|
||||
@ -144,7 +55,7 @@ block content
|
||||
| Please enter a valid URL format (http://www.example.com).
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') Facebook
|
||||
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")
|
||||
@ -152,120 +63,14 @@ block content
|
||||
span.ion-close-circled
|
||||
| Please enter a valid URL format (http://www.example.com).
|
||||
|
||||
.form-group
|
||||
.col-sm-offset-5.col-sm-4
|
||||
button.btn.btn-primary.btn-block(type='submit', ng-disabled='profileForm.$invalid')
|
||||
span.ion-edit
|
||||
| Update my Social Links
|
||||
|
||||
.col-sm-4.col-sm-offset-5.negative-bottom
|
||||
h2 Portfolio
|
||||
|
||||
.col-sm-4.col-sm-offset-5.flat-top
|
||||
h3 First Portfolio Project
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='website1Title') Title
|
||||
.col-sm-4
|
||||
input.form-control(type='text', name='website1Title', id='website1Title', autocomplete="off", ng-model='user.website1Title', ng-maxlength='140')
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website1Title.$error.maxlength && !profileForm.website1Title.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Portfolio project title must be fewer than 140 characters.
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='website1Link') Link
|
||||
.col-sm-4
|
||||
input.form-control(type='url', name='website1Link', id='website1Link', autocomplete="off", ng-model='user.website1Link', placeholder='http://')
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website1Link.$error.url && !profileForm.website1Link.$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-2.control-label(for='website1Image') Image Link (4:3 ratio)
|
||||
.col-sm-4
|
||||
input.form-control(type='url', name='website1Image', id='website1Image', autocomplete="off", ng-model='user.website1Image', placeholder='http://www.example.com/image.jpg')
|
||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website1Image.$error.url && !profileForm.website1Image.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Please enter a valid URL format (http://www.example.com/image.jpg).
|
||||
|
||||
.col-sm-4.col-sm-offset-5.flat-top
|
||||
h3 Second Portfolio Project
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='website2Title') Title
|
||||
.col-sm-4
|
||||
input.form-control(type='text', name='website2Title', id='website2Title', autocomplete="off", ng-model='user.website2Title', ng-maxlength='140')
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website2Title.$error.maxlength && !profileForm.website2Title.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Portfolio project title must be fewer than 140 characters.
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='website2Link') Link
|
||||
.col-sm-4
|
||||
input.form-control(type='url', name='website2Link', id='website2Link', autocomplete="off", ng-model='user.website2Link', placeholder='http://')
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website2Link.$error.url && !profileForm.website2Link.$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-2.control-label(for='website2Image') Image Link (4:3 ratio)
|
||||
.col-sm-4
|
||||
input.form-control(type='url', name='website2Image', id='website2Image', autocomplete="off", ng-model='user.website2Image', placeholder='http://www.example.com/image.jpg')
|
||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website2Image.$error.url && !profileForm.website2Image.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Please enter a valid URL format (http://www.example.com/image.jpg).
|
||||
|
||||
.col-sm-4.col-sm-offset-5.flat-top
|
||||
h3 Third Portfolio Project
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='website3Title') Title
|
||||
.col-sm-4
|
||||
input.form-control(type='text', name='website3Title', id='website3Title', autocomplete="off", ng-model='user.website3Title', ng-maxlength='140')
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website3Title.$error.maxlength && !profileForm.website3Title.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Portfolio project title must be fewer than 140 characters.
|
||||
|
||||
.form-group
|
||||
label.col-sm-3.col-sm-offset-2.control-label(for='website3Link') Link
|
||||
.col-sm-4
|
||||
input.form-control(type='url', name='website3Link', id='website3Link', autocomplete="off", ng-model='user.website3Link', placeholder='http://')
|
||||
.col-sm-4.col-sm-offset-5(ng-cloak, ng-show="profileForm.website3Link.$error.url && !profileForm.website3Link.$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-2.control-label(for='website3Image') Image Link (4:3 ratio)
|
||||
.col-sm-4
|
||||
input.form-control(type='url', name='website3Image', id='website3Image', autocomplete="off", ng-model='user.website3Image', placeholder='http://www.example.com/image.jpg', ng-pattern='/[\.](jpg|png|jpeg|gif)(\s+)?$/')
|
||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website3Image.$error.url && !profileForm.website3Image.$pristine")
|
||||
alert(type='danger')
|
||||
span.ion-close-circled
|
||||
| Please enter a valid URL format (http://www.example.com/image.jpg).
|
||||
|
||||
.form-group
|
||||
.col-sm-offset-5.col-sm-4
|
||||
button.btn.btn-primary.btn-block(type='submit', ng-disabled='profileForm.$invalid')
|
||||
span.ion-edit
|
||||
| Update my Portfolio
|
||||
br
|
||||
button.btn.btn-lg.btn-block.btn-primary.btn-link-social(type='submit', ng-disabled='profileForm.$invalid')
|
||||
span.ion-edit
|
||||
| Update my info
|
||||
|
||||
.panel.panel-info
|
||||
.panel-heading.text-center Manage your account here:
|
||||
.panel-body
|
||||
if (!user.google || !user.facebook || !user.linkedin || !user.twitter)
|
||||
if (!user.github)
|
||||
.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
|
||||
if (!user.twitter)
|
||||
.col-xs-12
|
||||
a.btn.btn-lg.btn-block.btn-twitter.btn-link-social.disabled(href='#')
|
||||
|
@ -96,21 +96,3 @@ block content
|
||||
td.col-xs-2= moment(challenge.completedDate, 'x').format("MMM DD, YYYY")
|
||||
td.col-xs-6
|
||||
a(href=challenge.solution, target='_blank') View my solution
|
||||
|
||||
br
|
||||
if (bonfires.length > 0)
|
||||
.col-sm-12
|
||||
table.table.table-striped
|
||||
thead
|
||||
tr
|
||||
th.col-xs-4 Bonfire
|
||||
th.col-xs-2 Completed
|
||||
th.col-xs-6 Solution
|
||||
for bonfire in bonfires
|
||||
tr
|
||||
td.col-xs-4
|
||||
a(href='/challenges/' + bonfire.name, target='_blank')= bonfire.name
|
||||
td.col-xs-2= moment(bonfire.completedDate, 'x').format("MMM DD, YYYY")
|
||||
td.col-xs-6
|
||||
pre.wrappable= bonfire.solution
|
||||
br
|
||||
|
@ -3,6 +3,9 @@ block content
|
||||
script.
|
||||
var completedChallenges = !{JSON.stringify(completedChallengeList)};
|
||||
var challengeList = !{JSON.stringify(challengeList)};
|
||||
.bg-danger.default-border-radius
|
||||
p
|
||||
a(href='https://github.com/FreeCodeCamp/freecodecamp/wiki/beta' target='_blank') You're using our experimental beta site. None of your progress here will be saved. Please click this to learn more.
|
||||
.panel.panel-info
|
||||
.panel-heading.text-center
|
||||
h1 Challenge Map
|
||||
|
@ -1,8 +1,8 @@
|
||||
extends layout
|
||||
block content
|
||||
.bg-danger
|
||||
a(href='https://github.com/FreeCodeCamp/freecodecamp/wiki/beta before continuing')
|
||||
h3 Warning! You are on beta! Please read this link before continuing! https://github.com/freecodecamp/freecodecamp/wiki/beta
|
||||
.bg-danger.default-border-radius
|
||||
p
|
||||
a(href='https://github.com/FreeCodeCamp/freecodecamp/wiki/beta' target='_blank') You're using our experimental beta site. None of your progress here will be saved. Please click this to learn more.
|
||||
.jumbotron
|
||||
.text-center
|
||||
h1.hug-top Code with Us
|
||||
|
@ -7,10 +7,6 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height
|
||||
img.img-responsive.nav-logo(src='https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg.gz', alt='learn to code javascript at Free Code Camp logo')
|
||||
.collapse.navbar-collapse
|
||||
ul.nav.navbar-nav.navbar-right.hamburger-dropdown
|
||||
|
||||
if user
|
||||
li
|
||||
a(href='/challenges') Learn
|
||||
li
|
||||
a(href='/map') Map
|
||||
li
|
||||
@ -18,7 +14,7 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height
|
||||
li
|
||||
a(href='/news') News
|
||||
li
|
||||
a(href='//github.com/FreeCodeCamp/freecodecamp/wiki/Home') Wiki
|
||||
a(href='//github.com/FreeCodeCamp/freecodecamp/wiki/Home', target='_blank') Wiki
|
||||
if !user
|
||||
li      
|
||||
li
|
||||
|
Reference in New Issue
Block a user