Merge pull request #14 from FreeCodeCamp/angularize-login
Angularize login
This commit is contained in:
11
app.js
11
app.js
@ -102,6 +102,7 @@ app.use(flash());
|
|||||||
app.disable('x-powered-by');
|
app.disable('x-powered-by');
|
||||||
|
|
||||||
app.use(helmet.xssFilter());
|
app.use(helmet.xssFilter());
|
||||||
|
app.use(helmet.noSniff());
|
||||||
app.use(helmet.xframe());
|
app.use(helmet.xframe());
|
||||||
|
|
||||||
var trusted = [
|
var trusted = [
|
||||||
@ -126,7 +127,8 @@ var trusted = [
|
|||||||
'localhost:3000',
|
'localhost:3000',
|
||||||
'ws://localhost:3000/',
|
'ws://localhost:3000/',
|
||||||
'http://localhost:3000',
|
'http://localhost:3000',
|
||||||
'*.ionicframework.com'
|
'*.ionicframework.com',
|
||||||
|
'https://syndication.twitter.com'
|
||||||
];
|
];
|
||||||
|
|
||||||
debug(trusted);
|
debug(trusted);
|
||||||
@ -256,6 +258,9 @@ app.get(
|
|||||||
);
|
);
|
||||||
app.all('/account', passportConf.isAuthenticated);
|
app.all('/account', passportConf.isAuthenticated);
|
||||||
app.get('/account/api', userController.getAccountAngular);
|
app.get('/account/api', userController.getAccountAngular);
|
||||||
|
// Unique Check API route
|
||||||
|
app.get('/api/checkUniqueUsername/:username', userController.checkUniqueUsername);
|
||||||
|
app.get('/api/checkUniqueEmail/:email', userController.checkUniqueEmail);
|
||||||
app.get('/account', userController.getAccount);
|
app.get('/account', userController.getAccount);
|
||||||
app.post('/account/profile', userController.postUpdateProfile);
|
app.post('/account/profile', userController.postUpdateProfile);
|
||||||
app.post('/account/password', userController.postUpdatePassword);
|
app.post('/account/password', userController.postUpdatePassword);
|
||||||
@ -293,13 +298,11 @@ app.get('/auth/twitter', passport.authenticate('twitter'));
|
|||||||
app.get(
|
app.get(
|
||||||
'/auth/twitter/callback',
|
'/auth/twitter/callback',
|
||||||
passport.authenticate('twitter', {
|
passport.authenticate('twitter', {
|
||||||
successRedirect: '/auth/twitter/middle',
|
successRedirect: '/',
|
||||||
failureRedirect: '/login'
|
failureRedirect: '/login'
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
app.get('/auth/twitter/middle', passportConf.hasEmail);
|
|
||||||
|
|
||||||
app.get(
|
app.get(
|
||||||
'/auth/linkedin',
|
'/auth/linkedin',
|
||||||
passport.authenticate('linkedin', {
|
passport.authenticate('linkedin', {
|
||||||
|
@ -9,27 +9,35 @@ var _ = require('lodash'),
|
|||||||
OAuthStrategy = require('passport-oauth').OAuthStrategy,
|
OAuthStrategy = require('passport-oauth').OAuthStrategy,
|
||||||
OAuth2Strategy = require('passport-oauth').OAuth2Strategy,
|
OAuth2Strategy = require('passport-oauth').OAuth2Strategy,
|
||||||
User = require('../models/User'),
|
User = require('../models/User'),
|
||||||
|
nodemailer = require('nodemailer'),
|
||||||
secrets = require('./secrets');
|
secrets = require('./secrets');
|
||||||
|
|
||||||
// Login Required middleware.
|
|
||||||
module.exports = {
|
|
||||||
isAuthenticated: isAuthenticated,
|
|
||||||
isAuthorized: isAuthorized,
|
|
||||||
hasEmail: hasEmail
|
|
||||||
};
|
|
||||||
|
|
||||||
passport.serializeUser(function(user, done) {
|
passport.serializeUser(function(user, done) {
|
||||||
done(null, user.id);
|
done(null, user.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
passport.deserializeUser(function(id, done) {
|
passport.deserializeUser(function(id, done) {
|
||||||
User.findOne({
|
User.findById(id, function(err, user) {
|
||||||
_id: id
|
|
||||||
}, '-password', function(err, user) {
|
|
||||||
done(err, user);
|
done(err, user);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Sign in using Email and Password.
|
||||||
|
|
||||||
|
passport.use(new LocalStrategy({ usernameField: 'email' }, function(email, password, done) {
|
||||||
|
User.findOne({ email: email }, function(err, user) {
|
||||||
|
if (!user) return done(null, false, { message: 'Email ' + email + ' not found'});
|
||||||
|
user.comparePassword(password, function(err, isMatch) {
|
||||||
|
if (isMatch) {
|
||||||
|
return done(null, user);
|
||||||
|
} else {
|
||||||
|
return done(null, false, { message: 'Invalid email or password.' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OAuth Strategy Overview
|
* OAuth Strategy Overview
|
||||||
*
|
*
|
||||||
@ -45,137 +53,69 @@ passport.deserializeUser(function(id, done) {
|
|||||||
* - Else create a new account.
|
* - Else create a new account.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Sign in with Twitter.
|
// Sign in with Facebook.
|
||||||
|
|
||||||
passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tokenSecret, profile, done) {
|
passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
User.findOne({ twitter: profile.id }, function(err, existingUser) {
|
User.findOne({ facebook: profile.id }, function(err, existingUser) {
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
req.flash('errors', { msg: 'There is already a Twitter account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
|
req.flash('errors', { msg: 'There is already a Facebook account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
User.findById(req.user.id, function(err, user) {
|
User.findById(req.user.id, function(err, user) {
|
||||||
user.twitter = profile.id;
|
user.facebook = profile.id;
|
||||||
user.tokens.push({ kind: 'twitter', accessToken: accessToken, tokenSecret: tokenSecret });
|
user.tokens.push({ kind: 'facebook', accessToken: accessToken });
|
||||||
user.profile.name = user.profile.name || profile.displayName;
|
|
||||||
user.profile.location = user.profile.location || profile._json.location;
|
|
||||||
user.profile.picture = user.profile.picture || profile._json.profile_image_url_https;
|
|
||||||
user.save(function(err) {
|
|
||||||
req.flash('info', { msg: 'Twitter account has been linked.' });
|
|
||||||
done(err, user);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
User.findOne({ twitter: profile.id }, function(err, existingUser) {
|
|
||||||
if (existingUser) return done(null, existingUser);
|
|
||||||
var user = new User();
|
|
||||||
// Twitter will not provide an email address. Period.
|
|
||||||
// But a person’s twitter username is guaranteed to be unique
|
|
||||||
// so we can "fake" a twitter email address as follows:
|
|
||||||
user.email = profile.username + "@please_add_your_email_here.com";
|
|
||||||
user.profile.username = profile.username;
|
|
||||||
user.twitter = profile.id;
|
|
||||||
user.tokens.push({ kind: 'twitter', accessToken: accessToken, tokenSecret: tokenSecret });
|
|
||||||
user.profile.name = profile.displayName;
|
|
||||||
user.profile.location = profile._json.location;
|
|
||||||
user.profile.picture = profile._json.profile_image_url_https;
|
|
||||||
user.save(function(err) {
|
|
||||||
done(err, user);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Sign in with Google.
|
|
||||||
|
|
||||||
passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refreshToken, profile, done) {
|
|
||||||
if (req.user) {
|
|
||||||
User.findOne({ google: profile.id }, function(err, existingUser) {
|
|
||||||
if (existingUser) {
|
|
||||||
req.flash('errors', { msg: 'There is already a Google account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
|
|
||||||
done(err);
|
|
||||||
} else {
|
|
||||||
User.findById(req.user.id, function(err, user) {
|
|
||||||
user.google = profile.id;
|
|
||||||
user.tokens.push({ kind: 'google', accessToken: accessToken });
|
|
||||||
user.profile.name = user.profile.name || profile.displayName;
|
user.profile.name = user.profile.name || profile.displayName;
|
||||||
user.profile.gender = user.profile.gender || profile._json.gender;
|
user.profile.gender = user.profile.gender || profile._json.gender;
|
||||||
user.profile.picture = user.profile.picture || profile._json.picture;
|
user.profile.picture = user.profile.picture || 'https://graph.facebook.com/' + profile.id + '/picture?type=large';
|
||||||
user.save(function(err) {
|
user.save(function(err) {
|
||||||
req.flash('info', { msg: 'Google account has been linked.' });
|
req.flash('info', { msg: 'Facebook account has been linked.' });
|
||||||
done(err, user);
|
done(err, user);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
User.findOne({ google: profile.id }, function(err, existingUser) {
|
User.findOne({ facebook: profile.id }, function(err, existingUser) {
|
||||||
if (existingUser) return done(null, existingUser);
|
if (existingUser) return done(null, existingUser);
|
||||||
User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
|
User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
|
||||||
if (existingEmailUser) {
|
if (existingEmailUser) {
|
||||||
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Google manually from Account Settings.' });
|
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings.' });
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
var user = new User();
|
var user = new User();
|
||||||
user.email = profile._json.email;
|
user.email = profile._json.email;
|
||||||
user.google = profile.id;
|
user.facebook = profile.id;
|
||||||
user.tokens.push({ kind: 'google', accessToken: accessToken });
|
user.tokens.push({ kind: 'facebook', accessToken: accessToken });
|
||||||
user.profile.name = profile.displayName;
|
user.profile.name = profile.displayName;
|
||||||
user.profile.gender = profile._json.gender;
|
user.profile.gender = profile._json.gender;
|
||||||
user.profile.picture = profile._json.picture;
|
user.profile.picture = 'https://graph.facebook.com/' + profile.id + '/picture?type=large';
|
||||||
|
user.profile.location = (profile._json.location) ? profile._json.location.name : '';
|
||||||
user.save(function(err) {
|
user.save(function(err) {
|
||||||
done(err, user);
|
done(err, user);
|
||||||
});
|
});
|
||||||
}
|
var transporter = nodemailer.createTransport({
|
||||||
});
|
service: 'Mandrill',
|
||||||
});
|
auth: {
|
||||||
}
|
user: secrets.mandrill.user,
|
||||||
}));
|
pass: secrets.mandrill.password
|
||||||
|
}
|
||||||
// Sign in with LinkedIn.
|
|
||||||
|
|
||||||
passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, refreshToken, profile, done) {
|
|
||||||
if (req.user) {
|
|
||||||
User.findOne({ linkedin: profile.id }, function(err, existingUser) {
|
|
||||||
if (existingUser) {
|
|
||||||
req.flash('errors', { msg: 'There is already a LinkedIn account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
|
|
||||||
done(err);
|
|
||||||
} else {
|
|
||||||
User.findById(req.user.id, function(err, user) {
|
|
||||||
user.linkedin = profile.id;
|
|
||||||
user.tokens.push({ kind: 'linkedin', accessToken: accessToken });
|
|
||||||
user.profile.name = user.profile.name || profile.displayName;
|
|
||||||
user.profile.location = user.profile.location || profile._json.location.name;
|
|
||||||
user.profile.picture = user.profile.picture || profile._json.pictureUrl;
|
|
||||||
user.profile.website = user.profile.website || profile._json.publicProfileUrl;
|
|
||||||
user.save(function(err) {
|
|
||||||
req.flash('info', { msg: 'LinkedIn account has been linked.' });
|
|
||||||
done(err, user);
|
|
||||||
});
|
});
|
||||||
});
|
var mailOptions = {
|
||||||
}
|
to: user.email,
|
||||||
});
|
from: 'Team@freecodecamp.com',
|
||||||
} else {
|
subject: 'Welcome to Free Code Camp!',
|
||||||
User.findOne({ linkedin: profile.id }, function(err, existingUser) {
|
text: [
|
||||||
if (existingUser) return done(null, existingUser);
|
'Greetings from San Francisco!\n\n',
|
||||||
User.findOne({ email: profile._json.emailAddress }, function(err, existingEmailUser) {
|
'Thank you for joining our community.\n',
|
||||||
if (existingEmailUser) {
|
'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
|
||||||
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with LinkedIn manually from Account Settings.' });
|
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
|
||||||
done(err);
|
'Good luck with the challenges!\n\n',
|
||||||
} else {
|
'- the Volunteer Camp Counselor Team'
|
||||||
var user = new User();
|
].join('')
|
||||||
user.linkedin = profile.id;
|
};
|
||||||
user.tokens.push({ kind: 'linkedin', accessToken: accessToken });
|
transporter.sendMail(mailOptions, function(err) {
|
||||||
user.email = profile._json.emailAddress;
|
if (err) { return err; }
|
||||||
user.profile.name = profile.displayName;
|
|
||||||
user.profile.location = profile._json.location.name;
|
|
||||||
user.profile.picture = profile._json.pictureUrl;
|
|
||||||
user.profile.website = profile._json.publicProfileUrl;
|
|
||||||
user.save(function(err) {
|
|
||||||
done(err, user);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -225,273 +165,228 @@ passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refre
|
|||||||
user.save(function(err) {
|
user.save(function(err) {
|
||||||
done(err, user);
|
done(err, user);
|
||||||
});
|
});
|
||||||
|
var transporter = nodemailer.createTransport({
|
||||||
|
service: 'Mandrill',
|
||||||
|
auth: {
|
||||||
|
user: secrets.mandrill.user,
|
||||||
|
pass: secrets.mandrill.password
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var mailOptions = {
|
||||||
|
to: user.email,
|
||||||
|
from: 'Team@freecodecamp.com',
|
||||||
|
subject: 'Welcome to Free Code Camp!',
|
||||||
|
text: [
|
||||||
|
'Greetings from San Francisco!\n\n',
|
||||||
|
'Thank you for joining our community.\n',
|
||||||
|
'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
|
||||||
|
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
|
||||||
|
'Good luck with the challenges!\n\n',
|
||||||
|
'- the Volunteer Camp Counselor Team'
|
||||||
|
].join('')
|
||||||
|
};
|
||||||
|
transporter.sendMail(mailOptions, function(err) {
|
||||||
|
if (err) { return err; }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Sign in with Facebook.
|
// Sign in with Twitter.
|
||||||
|
|
||||||
passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) {
|
passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tokenSecret, profile, done) {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
User.findOne({ facebook: profile.id }, function(err, existingUser) {
|
User.findOne({ twitter: profile.id }, function(err, existingUser) {
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
req.flash('errors', { msg: 'There is already a Facebook account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
|
req.flash('errors', { msg: 'There is already a Twitter account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
User.findById(req.user.id, function(err, user) {
|
User.findById(req.user.id, function(err, user) {
|
||||||
user.facebook = profile.id;
|
user.twitter = profile.id;
|
||||||
user.tokens.push({ kind: 'facebook', accessToken: accessToken });
|
user.tokens.push({ kind: 'twitter', accessToken: accessToken, tokenSecret: tokenSecret });
|
||||||
|
user.profile.username = user.profile.username || profile.username.toLowerCase();
|
||||||
|
user.profile.name = user.profile.name || profile.displayName;
|
||||||
|
user.profile.location = user.profile.location || profile._json.location;
|
||||||
|
user.profile.picture = user.profile.picture || profile._json.profile_image_url_https.replace('_normal', '');
|
||||||
|
user.profile.twitterHandle = user.profile.twitterHandle || profile.username.toLowerCase();
|
||||||
|
user.save(function(err) {
|
||||||
|
req.flash('info', { msg: 'Twitter account has been linked.' });
|
||||||
|
done(err, user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
User.findOne({ twitter: profile.id }, function(err, existingUser) {
|
||||||
|
if (existingUser) return done(null, existingUser);
|
||||||
|
var user = new User();
|
||||||
|
user.profile.username = profile.username.toLowerCase();
|
||||||
|
user.twitter = profile.id;
|
||||||
|
user.tokens.push({ kind: 'twitter', accessToken: accessToken, tokenSecret: tokenSecret });
|
||||||
|
user.profile.name = profile.displayName;
|
||||||
|
user.profile.location = profile._json.location;
|
||||||
|
user.profile.picture = profile._json.profile_image_url_https.replace('_normal', '');
|
||||||
|
user.profile.twitterHandle = user.profile.twitterHandle || profile.username.toLowerCase();
|
||||||
|
user.save(function(err) {
|
||||||
|
done(err, user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Sign in with Google.
|
||||||
|
|
||||||
|
passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refreshToken, profile, done) {
|
||||||
|
if (req.user) {
|
||||||
|
User.findOne({ google: profile.id }, function(err, existingUser) {
|
||||||
|
if (existingUser) {
|
||||||
|
req.flash('errors', { msg: 'There is already a Google account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
|
||||||
|
done(err);
|
||||||
|
} else {
|
||||||
|
User.findById(req.user.id, function(err, user) {
|
||||||
|
user.google = profile.id;
|
||||||
|
user.tokens.push({ kind: 'google', accessToken: accessToken });
|
||||||
user.profile.name = user.profile.name || profile.displayName;
|
user.profile.name = user.profile.name || profile.displayName;
|
||||||
user.profile.gender = user.profile.gender || profile._json.gender;
|
user.profile.gender = user.profile.gender || profile._json.gender;
|
||||||
user.profile.picture = user.profile.picture || 'https://graph.facebook.com/' + profile.id + '/picture?type=large';
|
user.profile.picture = user.profile.picture || profile._json.picture;
|
||||||
user.save(function(err) {
|
user.save(function(err) {
|
||||||
req.flash('info', { msg: 'Facebook account has been linked.' });
|
req.flash('info', { msg: 'Google account has been linked.' });
|
||||||
done(err, user);
|
done(err, user);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
User.findOne({ facebook: profile.id }, function(err, existingUser) {
|
User.findOne({ google: profile.id }, function(err, existingUser) {
|
||||||
if (existingUser) return done(null, existingUser);
|
if (existingUser) return done(null, existingUser);
|
||||||
User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
|
User.findOne({ email: profile._json.email }, function(err, existingEmailUser) {
|
||||||
if (existingEmailUser) {
|
if (existingEmailUser) {
|
||||||
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings.' });
|
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Google manually from Account Settings.' });
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
var user = new User();
|
var user = new User();
|
||||||
user.email = profile._json.email;
|
user.email = profile._json.email;
|
||||||
user.facebook = profile.id;
|
user.google = profile.id;
|
||||||
user.tokens.push({ kind: 'facebook', accessToken: accessToken });
|
user.tokens.push({ kind: 'google', accessToken: accessToken });
|
||||||
user.profile.name = profile.displayName;
|
user.profile.name = profile.displayName;
|
||||||
user.profile.gender = profile._json.gender;
|
user.profile.gender = profile._json.gender;
|
||||||
user.profile.picture = 'https://graph.facebook.com/' + profile.id + '/picture?type=large';
|
user.profile.picture = profile._json.picture;
|
||||||
user.profile.location = (profile._json.location) ? profile._json.location.name : '';
|
|
||||||
user.save(function(err) {
|
user.save(function(err) {
|
||||||
done(err, user);
|
done(err, user);
|
||||||
});
|
});
|
||||||
|
var transporter = nodemailer.createTransport({
|
||||||
|
service: 'Mandrill',
|
||||||
|
auth: {
|
||||||
|
user: secrets.mandrill.user,
|
||||||
|
pass: secrets.mandrill.password
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var mailOptions = {
|
||||||
|
to: user.email,
|
||||||
|
from: 'Team@freecodecamp.com',
|
||||||
|
subject: 'Welcome to Free Code Camp!',
|
||||||
|
text: [
|
||||||
|
'Greetings from San Francisco!\n\n',
|
||||||
|
'Thank you for joining our community.\n',
|
||||||
|
'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
|
||||||
|
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
|
||||||
|
'Good luck with the challenges!\n\n',
|
||||||
|
'- the Volunteer Camp Counselor Team'
|
||||||
|
].join('')
|
||||||
|
};
|
||||||
|
transporter.sendMail(mailOptions, function(err) {
|
||||||
|
if (err) { return err; }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Sign in using Email and Password.
|
// Sign in with LinkedIn.
|
||||||
passport.use(
|
|
||||||
new LocalStrategy(
|
|
||||||
{ usernameField: 'email' }, function(email, password, done) {
|
|
||||||
User.findOne({ email: email }, function(err, user) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
|
|
||||||
if (!user) {
|
passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, refreshToken, profile, done) {
|
||||||
return done(null, false, { message: 'Email ' + email + ' not found'});
|
if (req.user) {
|
||||||
}
|
User.findOne({ linkedin: profile.id }, function(err, existingUser) {
|
||||||
user.comparePassword(password, function(err, isMatch) {
|
if (existingUser) {
|
||||||
if (err) { return done(err); }
|
req.flash('errors', { msg: 'There is already a LinkedIn account that belongs to you. Sign in with that account or delete it, then link it with your current account.' });
|
||||||
|
done(err);
|
||||||
if (isMatch) {
|
|
||||||
return done(null, user);
|
|
||||||
} else {
|
|
||||||
return done(null, false, { message: 'Invalid email or password.' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
// Sign in with Facebook.
|
|
||||||
passport.use(
|
|
||||||
new FacebookStrategy(
|
|
||||||
secrets.facebook, function(req, accessToken, refreshToken, profile, done) {
|
|
||||||
if (req.user) {
|
|
||||||
User.findOne({ facebook: profile.id }, function(err, existingUser) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
|
|
||||||
if (existingUser) {
|
|
||||||
req.flash('errors', {
|
|
||||||
msg: [
|
|
||||||
'There is already a Facebook account that belongs to you.',
|
|
||||||
'Sign in with that account or delete it, then link it with',
|
|
||||||
'your current account.'
|
|
||||||
].join(' ')
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
User.findById(req.user.id, function(err, user) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
|
|
||||||
user.facebook = profile.id;
|
|
||||||
user.tokens.push({
|
|
||||||
kind: 'facebook',
|
|
||||||
accessToken: accessToken
|
|
||||||
});
|
|
||||||
|
|
||||||
user.profile.name = user.profile.name || profile.displayName;
|
|
||||||
user.profile.gender = user.profile.gender || profile._json.gender;
|
|
||||||
|
|
||||||
user.profile.picture =
|
|
||||||
user.profile.picture ||
|
|
||||||
'https://graph.facebook.com/' +
|
|
||||||
profile.id +
|
|
||||||
'/picture?type=large';
|
|
||||||
|
|
||||||
user.save(function(err) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
|
|
||||||
req.flash(
|
|
||||||
'info', { msg: 'Facebook account has been linked.' });
|
|
||||||
done(null, user);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
User.findOne({ facebook: profile.id }, function(err, existingUser) {
|
User.findById(req.user.id, function(err, user) {
|
||||||
if (err) { return done(err); }
|
user.linkedin = profile.id;
|
||||||
|
user.tokens.push({ kind: 'linkedin', accessToken: accessToken });
|
||||||
if (existingUser) { return done(null, existingUser); }
|
user.profile.name = user.profile.name || profile.displayName;
|
||||||
|
user.profile.location = user.profile.location || profile._json.location.name;
|
||||||
User.findOne(
|
user.profile.picture = user.profile.picture || profile._json.pictureUrl;
|
||||||
{ email: profile._json.email }, function(err, existingEmailUser) {
|
user.profile.website = user.profile.website || profile._json.publicProfileUrl;
|
||||||
if (err) { return done(err); }
|
user.save(function(err) {
|
||||||
|
req.flash('info', { msg: 'LinkedIn account has been linked.' });
|
||||||
var user = existingEmailUser || new User();
|
done(err, user);
|
||||||
user.email = user.email || profile._json.email;
|
|
||||||
user.facebook = profile.id;
|
|
||||||
user.tokens.push({
|
|
||||||
kind: 'facebook',
|
|
||||||
accessToken: accessToken
|
|
||||||
});
|
|
||||||
user.profile.name = user.profile.name || profile.displayName;
|
|
||||||
|
|
||||||
user.profile.gender =
|
|
||||||
user.profile.gender || profile._json.gender;
|
|
||||||
|
|
||||||
user.profile.picture =
|
|
||||||
user.profile.picture ||
|
|
||||||
'https://graph.facebook.com/' +
|
|
||||||
profile.id +
|
|
||||||
'/picture?type=large';
|
|
||||||
|
|
||||||
user.profile.location =
|
|
||||||
user.profile.location ||
|
|
||||||
(profile._json.location) ? profile._json.location.name : '';
|
|
||||||
|
|
||||||
user.challengesComplete = user.challengesCompleted || [];
|
|
||||||
user.save(function(err) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
done(null, user);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Sign in with GitHub.
|
|
||||||
|
|
||||||
passport.use(
|
|
||||||
new GitHubStrategy(
|
|
||||||
secrets.github, function(req, accessToken, refreshToken, profile, done) {
|
|
||||||
if (req.user) {
|
|
||||||
User.findOne({ github: profile.id }, function(err, existingUser) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
|
|
||||||
if (existingUser) {
|
|
||||||
req.flash('errors', {
|
|
||||||
msg: [
|
|
||||||
'There is already a GitHub account that belongs to you.',
|
|
||||||
'Sign in with that account or delete it, then link it with',
|
|
||||||
'your current account.'
|
|
||||||
].join(' ')
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
User.findById(req.user.id, function(err, user) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
|
|
||||||
user.github = profile.id;
|
|
||||||
user.tokens.push({ kind: 'github', accessToken: accessToken });
|
|
||||||
user.profile.name = user.profile.name || profile.displayName;
|
|
||||||
|
|
||||||
user.profile.picture =
|
|
||||||
user.profile.picture || profile._json.avatar_url;
|
|
||||||
|
|
||||||
user.profile.location =
|
|
||||||
user.profile.location || profile._json.location;
|
|
||||||
|
|
||||||
user.profile.website =
|
|
||||||
user.profile.website || profile._json.blog;
|
|
||||||
|
|
||||||
user.save(function(err) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
|
|
||||||
req.flash('info', { msg: 'GitHub account has been linked.' });
|
|
||||||
done(null, user);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
User.findOne({ github: profile.id }, function(err, existingUser) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
|
|
||||||
if (existingUser) { return done(null, existingUser); }
|
|
||||||
User.findOne(
|
|
||||||
{ email: profile._json.email }, function(err, existingEmailUser) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
|
|
||||||
var user = existingEmailUser || new User();
|
|
||||||
user.email = user.email || profile._json.email;
|
|
||||||
user.github = profile.id;
|
|
||||||
user.tokens.push({
|
|
||||||
kind: 'github',
|
|
||||||
accessToken: accessToken
|
|
||||||
});
|
|
||||||
user.profile.name = user.profile.name || profile.displayName;
|
|
||||||
|
|
||||||
user.profile.picture =
|
|
||||||
user.profile.picture || profile._json.avatar_url;
|
|
||||||
|
|
||||||
user.profile.location =
|
|
||||||
user.profile.location || profile._json.location;
|
|
||||||
|
|
||||||
user.profile.website =
|
|
||||||
user.profile.website || profile._json.blog;
|
|
||||||
|
|
||||||
user.save(function(err) {
|
|
||||||
if (err) { return done(err); }
|
|
||||||
done(null, user);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
User.findOne({ linkedin: profile.id }, function(err, existingUser) {
|
||||||
|
if (existingUser) return done(null, existingUser);
|
||||||
|
User.findOne({ email: profile._json.emailAddress }, function(err, existingEmailUser) {
|
||||||
|
if (existingEmailUser) {
|
||||||
|
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with LinkedIn manually from Account Settings.' });
|
||||||
|
done(err);
|
||||||
|
} else {
|
||||||
|
var user = new User();
|
||||||
|
user.linkedin = profile.id;
|
||||||
|
user.tokens.push({ kind: 'linkedin', accessToken: accessToken });
|
||||||
|
user.email = profile._json.emailAddress;
|
||||||
|
user.profile.name = profile.displayName;
|
||||||
|
user.profile.location = profile._json.location.name;
|
||||||
|
user.profile.picture = profile._json.pictureUrl;
|
||||||
|
user.profile.website = profile._json.publicProfileUrl;
|
||||||
|
user.save(function(err) {
|
||||||
|
done(err, user);
|
||||||
|
});
|
||||||
|
var transporter = nodemailer.createTransport({
|
||||||
|
service: 'Mandrill',
|
||||||
|
auth: {
|
||||||
|
user: secrets.mandrill.user,
|
||||||
|
pass: secrets.mandrill.password
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var mailOptions = {
|
||||||
|
to: user.email,
|
||||||
|
from: 'Team@freecodecamp.com',
|
||||||
|
subject: 'Welcome to Free Code Camp!',
|
||||||
|
text: [
|
||||||
|
'Greetings from San Francisco!\n\n',
|
||||||
|
'Thank you for joining our community.\n',
|
||||||
|
'Feel free to email us at this address if you have any questions about Free Code Camp.\n',
|
||||||
|
"And if you have a moment, check out our blog: blog.freecodecamp.com.\n",
|
||||||
|
'Good luck with the challenges!\n\n',
|
||||||
|
'- the Volunteer Camp Counselor Team'
|
||||||
|
].join('')
|
||||||
|
};
|
||||||
|
transporter.sendMail(mailOptions, function(err) {
|
||||||
|
if (err) { return err; }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Login Required middleware.
|
||||||
|
|
||||||
|
exports.isAuthenticated = function(req, res, next) {
|
||||||
|
|
||||||
function isAuthenticated(req, res, next) {
|
|
||||||
if (req.isAuthenticated()) return next();
|
if (req.isAuthenticated()) return next();
|
||||||
res.redirect('/login');
|
res.redirect('/login');
|
||||||
}
|
};
|
||||||
|
|
||||||
function hasEmail(req, res) {
|
|
||||||
if (req.user) {
|
|
||||||
if (req.user.email) {
|
|
||||||
res.redirect('/');
|
|
||||||
} else {
|
|
||||||
req.flash('info', {
|
|
||||||
msg: 'Please add your email address before starting our challenges.'
|
|
||||||
});
|
|
||||||
res.redirect('/account');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authorization Required middleware.
|
// Authorization Required middleware.
|
||||||
function isAuthorized(req, res, next) {
|
|
||||||
|
exports.isAuthorized = function(req, res, next) {
|
||||||
var provider = req.path.split('/').slice(-1)[0];
|
var provider = req.path.split('/').slice(-1)[0];
|
||||||
|
|
||||||
if (_.find(req.user.tokens, { kind: provider })) {
|
if (_.find(req.user.tokens, { kind: provider })) {
|
||||||
@ -499,4 +394,4 @@ function isAuthorized(req, res, next) {
|
|||||||
} else {
|
} else {
|
||||||
res.redirect('/auth/' + provider);
|
res.redirect('/auth/' + provider);
|
||||||
}
|
}
|
||||||
}
|
};
|
@ -70,7 +70,7 @@ exports.logout = function(req, res) {
|
|||||||
|
|
||||||
exports.getEmailSignin = function(req, res) {
|
exports.getEmailSignin = function(req, res) {
|
||||||
if (req.user) return res.redirect('/');
|
if (req.user) return res.redirect('/');
|
||||||
res.render('account/email-signup', {
|
res.render('account/email-signin', {
|
||||||
title: 'Sign in to your Free Code Camp Account'
|
title: 'Sign in to your Free Code Camp Account'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -82,7 +82,7 @@ exports.getEmailSignin = function(req, res) {
|
|||||||
|
|
||||||
exports.getEmailSignup = function(req, res) {
|
exports.getEmailSignup = function(req, res) {
|
||||||
if (req.user) return res.redirect('/');
|
if (req.user) return res.redirect('/');
|
||||||
res.render('account/email-signin', {
|
res.render('account/email-signup', {
|
||||||
title: 'Create Your Free Code Camp Account'
|
title: 'Create Your Free Code Camp Account'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -93,6 +93,7 @@ exports.getEmailSignup = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.postEmailSignup = function(req, res, next) {
|
exports.postEmailSignup = function(req, res, next) {
|
||||||
|
console.log('post email signup called');
|
||||||
req.assert('email', 'Email is not valid').isEmail();
|
req.assert('email', 'Email is not valid').isEmail();
|
||||||
req.assert('password', 'Password must be at least 4 characters long').len(4);
|
req.assert('password', 'Password must be at least 4 characters long').len(4);
|
||||||
req.assert('confirmPassword', 'Passwords do not match')
|
req.assert('confirmPassword', 'Passwords do not match')
|
||||||
@ -103,11 +104,15 @@ exports.postEmailSignup = function(req, res, next) {
|
|||||||
if (errors) {
|
if (errors) {
|
||||||
req.flash('errors', errors);
|
req.flash('errors', errors);
|
||||||
return res.redirect('/email-signup');
|
return res.redirect('/email-signup');
|
||||||
|
console.log(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = new User({
|
var user = new User({
|
||||||
email: req.body.email,
|
email: req.body.email,
|
||||||
password: req.body.password
|
password: req.body.password,
|
||||||
|
profile : {
|
||||||
|
username: req.body.username
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
User.findOne({ email: req.body.email }, function(err, existingUser) {
|
User.findOne({ email: req.body.email }, function(err, existingUser) {
|
||||||
@ -166,6 +171,33 @@ exports.getAccount = function(req, res) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique username check API Call
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.checkUniqueUsername = function(req, res) {
|
||||||
|
User.count({'profile.username': req.params.username.toLowerCase()}, function (err, data) {
|
||||||
|
if (data == 1) {
|
||||||
|
return res.send(true);
|
||||||
|
} else {
|
||||||
|
return res.send(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Unique email check API Call
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.checkUniqueEmail = function(req, res) {
|
||||||
|
User.count({'email': decodeURIComponent(req.params.email).toLowerCase()}, function (err, data) {
|
||||||
|
if (data == 1) {
|
||||||
|
return res.send(true);
|
||||||
|
} else {
|
||||||
|
return res.send(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /campers/:username
|
* GET /campers/:username
|
||||||
@ -173,7 +205,7 @@ exports.getAccount = function(req, res) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
exports.returnUser = function(req, res, next) {
|
exports.returnUser = function(req, res, next) {
|
||||||
User.find({'profile.username': req.params.username}, function(err, user) {
|
User.find({'profile.username': req.params.username.toLowerCase()}, function(err, user) {
|
||||||
if (err) { debug('Username err: ', err); next(err); }
|
if (err) { debug('Username err: ', err); next(err); }
|
||||||
if (user[0]) {
|
if (user[0]) {
|
||||||
var user = user[0];
|
var user = user[0];
|
||||||
|
@ -24,7 +24,6 @@ $(document).ready(function() {
|
|||||||
|
|
||||||
l = location.pathname.split('/');
|
l = location.pathname.split('/');
|
||||||
cn = l[l.length - 1];
|
cn = l[l.length - 1];
|
||||||
console.log(cn);
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: {challengeNumber: cn},
|
data: {challengeNumber: cn},
|
||||||
@ -47,7 +46,65 @@ profileValidation.controller('profileValidationController', ['$scope', '$http',
|
|||||||
function($scope, $http) {
|
function($scope, $http) {
|
||||||
$http.get('/account/api').success(function(data) {
|
$http.get('/account/api').success(function(data) {
|
||||||
$scope.user = data.user;
|
$scope.user = data.user;
|
||||||
|
$scope.user.profile.username = $scope.user.profile.username.toLowerCase();
|
||||||
|
$scope.storedUsername = data.user.profile.username;
|
||||||
|
$scope.storedEmail = data.user.email;
|
||||||
|
$scope.user.email = $scope.user.email.toLowerCase();
|
||||||
|
$scope.user.profile.twitterHandle = $scope.user.profile.twitterHandle.toLowerCase();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
profileValidation.controller('emailSignUpController', ['$scope',
|
||||||
|
function($scope) {
|
||||||
|
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
profileValidation.controller('emailSignInController', ['$scope',
|
||||||
|
function($scope) {
|
||||||
|
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
profileValidation.directive('uniqueUsername', function($http) {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
require: 'ngModel',
|
||||||
|
link: function (scope, element, attrs, ngModel) {
|
||||||
|
element.bind("keyup", function (event) {
|
||||||
|
ngModel.$setValidity('unique', true);
|
||||||
|
if (element.val()) {
|
||||||
|
$http.get("/api/checkUniqueUsername/" + element.val()).success(function (data) {
|
||||||
|
if (element.val() == scope.storedUsername) {
|
||||||
|
ngModel.$setValidity('unique', true);
|
||||||
|
} else if (data) {
|
||||||
|
ngModel.$setValidity('unique', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
profileValidation.directive('uniqueEmail', function($http) {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
require: 'ngModel',
|
||||||
|
link: function (scope, element, attrs, ngModel) {
|
||||||
|
element.bind("keyup", function (event) {
|
||||||
|
ngModel.$setValidity('unique', true);
|
||||||
|
if (element.val()) {
|
||||||
|
$http.get("/api/checkUniqueEmail/" + encodeURIComponent(element.val())).success(function (data) {
|
||||||
|
if (element.val() == scope.storedEmail) {
|
||||||
|
ngModel.$setValidity('unique', true);
|
||||||
|
} else if (data) {
|
||||||
|
ngModel.$setValidity('unique', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -1,29 +1,27 @@
|
|||||||
extends ../layout
|
extends ../layout
|
||||||
block content
|
block content
|
||||||
.jumbotron.text-center
|
.jumbotron.text-center(ng-controller="emailSignInController")
|
||||||
h2 Sign up with an email address here:
|
h2 Sign in with an email address here:
|
||||||
form.form-horizontal(method='POST')
|
form(method='POST', action='/email-signin')
|
||||||
input(type='hidden', name='_csrf', value=_csrf)
|
input(type='hidden', name='_csrf', value=_csrf)
|
||||||
.form-group
|
.col-sm-6.col-sm-offset-3
|
||||||
.col-sm-6.col-sm-offset-3
|
.form-group
|
||||||
input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus)
|
input.form-control(type='email', name='email', id='email', placeholder='Email', ng-model='email', autofocus=true)
|
||||||
.form-group
|
| {{ $scope.email }}
|
||||||
.col-sm-6.col-sm-offset-3
|
.form-group
|
||||||
input.form-control(type='password', name='password', id='password', placeholder='Password')
|
input.form-control(type='password', name='password', id='password', placeholder='Password', ng-model='password')
|
||||||
.form-group
|
.form-group
|
||||||
.col-sm-6.col-sm-offset-3
|
button.btn.btn-primary(type='submit')
|
||||||
input.form-control(type='password', name='confirmPassword', id='confirmPassword', placeholder='Confirm Password')
|
span.ion-android-hand
|
||||||
.form-group
|
| Login
|
||||||
.col-sm-offset-3.col-sm-6
|
span
|
||||||
button.btn.btn-success(type='submit')
|
a.btn.btn-info(href='/forgot') Forgot your password?
|
||||||
span.ion-person-add
|
br
|
||||||
| Signup
|
br
|
||||||
br
|
br
|
||||||
br
|
br
|
||||||
br
|
br
|
||||||
br
|
br
|
||||||
br
|
br
|
||||||
br
|
br
|
||||||
br
|
br
|
||||||
br
|
|
||||||
br
|
|
@ -1,26 +1,60 @@
|
|||||||
extends ../layout
|
extends ../layout
|
||||||
block content
|
block content
|
||||||
.jumbotron.text-center
|
.jumbotron.text-center
|
||||||
h2 Sign in with an email address here:
|
h2 Sign up with an email address here:
|
||||||
form(method='POST')
|
form.form-horizontal(method='POST', action='/email-signup', name="signupForm", novalidate="novalidate")
|
||||||
input(type='hidden', name='_csrf', value=_csrf)
|
input(type='hidden', name='_csrf', value=_csrf)
|
||||||
.col-sm-6.col-sm-offset-3
|
.form-group
|
||||||
.form-group
|
.col-sm-6.col-sm-offset-3
|
||||||
input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus=true)
|
input.form-control(type='email', ng-model='email', ng-keypress='', name='email', id='email', placeholder='email', autofocus, required, autocomplete="off", unique-email='')
|
||||||
.form-group
|
.col-sm-6.col-sm-offset-3(ng-show="signupForm.email.$error.unique && !signupForm.email.$pristine")
|
||||||
input.form-control(type='password', name='password', id='password', placeholder='Password')
|
alert(type='danger')
|
||||||
.form-group
|
span.ion-close-circled
|
||||||
button.btn.btn-primary(type='submit')
|
| This email is taken.
|
||||||
span.ion-android-hand
|
.form-group
|
||||||
| Login
|
.col-sm-6.col-sm-offset-3
|
||||||
span
|
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_]+$/")
|
||||||
a.btn.btn-info(href='/forgot') Forgot your password?
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.username.$error.pattern && !signupForm.username.$pristine")
|
||||||
br
|
alert(type='danger')
|
||||||
br
|
span.ion-close-circled
|
||||||
br
|
| Your username should only contain letters, numbers and underscores (az10_).
|
||||||
br
|
.col-sm-6.col-sm-offset-3(ng-show="signupForm.username.$error.unique && !signupForm.username.$pristine")
|
||||||
br
|
alert(type='danger')
|
||||||
br
|
span.ion-close-circled
|
||||||
br
|
| This username is taken.
|
||||||
br
|
.col-sm-6.col-sm-offset-3(ng-show="signupForm.username.$error.minlength && !signupForm.username.$pristine")
|
||||||
br
|
alert(type='danger')
|
||||||
|
span.ion-close-circled
|
||||||
|
| Your username must be at least 5 characters long.
|
||||||
|
.col-sm-6.col-sm-offset-3(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=5)
|
||||||
|
.col-sm-6.col-sm-offset-3(ng-show="signupForm.password.$error.minlength && !signupForm.password.$pristine")
|
||||||
|
alert(type='danger')
|
||||||
|
span.ion-close-circled
|
||||||
|
| Your password must be at least 8 characters long.
|
||||||
|
.form-group
|
||||||
|
.col-sm-6.col-sm-offset-3
|
||||||
|
input.form-control(type='password', ng-model='confirmPassword', name='confirmPassword', id='confirmPassword', placeholder='confirm password', required, ng-minlength=5)
|
||||||
|
.col-sm-6.col-sm-offset-3(ng-show="(confirmPassword !== password) && !signupForm.confirmPassword.$pristine")
|
||||||
|
alert(type='danger')
|
||||||
|
span.ion-close-circled
|
||||||
|
| Passwords must match.
|
||||||
|
.form-group
|
||||||
|
.col-sm-offset-3.col-sm-6
|
||||||
|
button.btn.btn-success(type='submit')
|
||||||
|
span.ion-person-add
|
||||||
|
| Signup
|
||||||
|
br
|
||||||
|
br
|
||||||
|
br
|
||||||
|
br
|
||||||
|
br
|
||||||
|
br
|
||||||
|
br
|
||||||
|
br
|
||||||
|
br
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
extends ../layout
|
extends ../layout
|
||||||
|
|
||||||
block content
|
block content
|
||||||
.col-sm-8.col-sm-offset-2
|
.jumbotron
|
||||||
form(method='POST')
|
.col-sm-8.col-sm-offset-2
|
||||||
legend Forgot Password
|
form(method='POST')
|
||||||
input(type='hidden', name='_csrf', value=_csrf)
|
h1 Forgot Password
|
||||||
.form-group
|
input(type='hidden', name='_csrf', value=_csrf)
|
||||||
p Enter your email address below and we will send you password reset instructions.
|
.form-group
|
||||||
label.control-label(for='email') Email
|
p Enter your email address below and we will send you password reset instructions.
|
||||||
input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus=true)
|
label.control-label(for='email') Email
|
||||||
.form-group
|
input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus=true)
|
||||||
button.btn.btn-primary(type='submit')
|
.form-group
|
||||||
i.fa.fa-key
|
button.btn.btn-primary(type='submit')
|
||||||
| Reset Password
|
i.fa.fa-key
|
||||||
|
| Reset Password
|
||||||
|
@ -8,9 +8,9 @@ block content
|
|||||||
a.btn.btn-lg.btn-block.btn-facebook.btn-social(href='/auth/facebook')
|
a.btn.btn-lg.btn-block.btn-facebook.btn-social(href='/auth/facebook')
|
||||||
i.fa.fa-facebook
|
i.fa.fa-facebook
|
||||||
| Sign in with Facebook
|
| Sign in with Facebook
|
||||||
a.btn.btn-lg.btn-block.btn-github.btn-social(href='/auth/github')
|
//a.btn.btn-lg.btn-block.btn-github.btn-social(href='/auth/github')
|
||||||
i.fa.fa-github
|
// i.fa.fa-github
|
||||||
| Sign in with GitHub
|
// | Sign in with GitHub
|
||||||
a.btn.btn-lg.btn-block.btn-linkedin.btn-social(href='/auth/linkedin')
|
a.btn.btn-lg.btn-block.btn-linkedin.btn-social(href='/auth/linkedin')
|
||||||
i.fa.fa-linkedin
|
i.fa.fa-linkedin
|
||||||
| Sign in with LinkedIn
|
| Sign in with LinkedIn
|
||||||
|
@ -13,10 +13,10 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='name') Name *
|
label.col-sm-3.col-sm-offset-2.control-label(for='name') Name *
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', placeholder='Name', name='name', ng-model='user.profile.name', ng-minlength='3', ng-maxlength='50', required='required', id='name')
|
input.form-control(type='text', placeholder='Name', name='name', autocomplete="off", ng-model='user.profile.name', ng-minlength='3', ng-maxlength='50', required='required', id='name')
|
||||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.name.$invalid && !profileForm.name.$pristine && profileForm.name.$error.required")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.name.$invalid && profileForm.name.$error.required")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled(id='#name-error')
|
||||||
| Your name is required.
|
| Your name is required.
|
||||||
.col-sm-4.col-sm-offset-5(ng-show='profileForm.name.$error.minlength && !profileForm.name.$pristine')
|
.col-sm-4.col-sm-offset-5(ng-show='profileForm.name.$error.minlength && !profileForm.name.$pristine')
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
@ -30,11 +30,15 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='username') Username (path to public profile) *
|
label.col-sm-3.col-sm-offset-2.control-label(for='username') Username (path to public profile) *
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', placeholder='username' name='username', id='username', ng-model='user.profile.username', required='required', ng-minlength='5', ng-maxlength='20')
|
input.form-control(type='text', placeholder='username' name='username', autocomplete="off", id='username', ng-model='user.profile.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-show="profileForm.username.$error.required && !profileForm.username.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.username.$error.pattern")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
| Please enter a username.
|
| Your username should only contain letters, numbers and underscores (az10_).
|
||||||
|
.col-sm-4.col-sm-offset-5(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-show="profileForm.username.$error.minlength && !profileForm.username.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.username.$error.minlength && !profileForm.username.$pristine")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
@ -43,24 +47,32 @@ block content
|
|||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
| Your username must be fewer than 15 characters.
|
| Your username must be fewer than 15 characters.
|
||||||
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.username.$error.unique && !profileForm.username.$pristine && $scope.storedUsername !== user.profile.username")
|
||||||
|
alert(type='danger')
|
||||||
|
span.ion-close-circled
|
||||||
|
| That username is already taken.
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') Email *
|
label.col-sm-3.col-sm-offset-2.control-label(for='email') Email *
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='email', name='email', id='email', ng-model='user.email', required='required')
|
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-show="profileForm.email.$error.required && !profileForm.email.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.email.$error.required")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
| An email address is required.
|
| Your email address is required.
|
||||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.$error.email && !profileForm.email.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.$error.email && !profileForm.email.$pristine")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
| Please enter a valid email format.
|
| Please enter a valid email format.
|
||||||
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.email.$error.unique && !profileForm.email.$pristine")
|
||||||
|
alert(type='danger')
|
||||||
|
span.ion-close-circled
|
||||||
|
| That email is already taken.
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='location') Location
|
label.col-sm-3.col-sm-offset-2.control-label(for='location') Location
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='location', id='location', ng-model='user.profile.location')
|
input.form-control(type='text', name='location', autocomplete="off", id='location', ng-model='user.profile.location')
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') Link to Profile Photo (1:1 ratio)
|
label.col-sm-3.col-sm-offset-2.control-label(for='email') Link to Profile Photo (1:1 ratio)
|
||||||
@ -74,7 +86,7 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='bio') Bio (140 characters)
|
label.col-sm-3.col-sm-offset-2.control-label(for='bio') Bio (140 characters)
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='bio', ng-model='user.profile.bio', ng-maxlength='140', id='bio')
|
input.form-control(type='text', name='bio', autocomplete="off", ng-model='user.profile.bio', ng-maxlength='140', id='bio')
|
||||||
.col-sm-4.col-sm-offset-5(ng-show='profileForm.bio.$error.maxlength && !profileForm.bio.$pristine')
|
.col-sm-4.col-sm-offset-5(ng-show='profileForm.bio.$error.maxlength && !profileForm.bio.$pristine')
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
@ -94,11 +106,11 @@ block content
|
|||||||
.col-sm-4
|
.col-sm-4
|
||||||
.input-group.twitter-input
|
.input-group.twitter-input
|
||||||
span.input-group-addon @
|
span.input-group-addon @
|
||||||
input.form-control(type='text', name='twitterHandle', id='twitterHandle', ng-model='user.profile.twitterHandle', ng-maxlength='15', ng-pattern="/^[A-z0-9_]+$/")
|
input.form-control(type='text', name='twitterHandle', autocomplete="off", id='twitterHandle', ng-model='user.profile.twitterHandle', ng-maxlength='15', ng-pattern="/^[A-z0-9_]+$/")
|
||||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.twitterHandle.$error.pattern")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.twitterHandle.$error.pattern")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
| Your Twitter handle should only contain letters, numbers and underscores (@az10_).
|
| Your Twitter handle should only contain letters, numbers and underscores (az10_).
|
||||||
.col-sm-4.col-sm-offset-5(ng-show='profileForm.twitterHandle.$error.maxlength && !profileForm.twitterHandle.$pristine')
|
.col-sm-4.col-sm-offset-5(ng-show='profileForm.twitterHandle.$error.maxlength && !profileForm.twitterHandle.$pristine')
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
@ -106,7 +118,7 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') Github
|
label.col-sm-3.col-sm-offset-2.control-label(for='email') Github
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='url', name='githubProfile', id='githubProfile', ng-model='user.profile.githubProfile', placeholder='http://')
|
input.form-control(type='url', name='githubProfile', id='githubProfile', autocomplete="off", ng-model='user.profile.githubProfile', placeholder='http://')
|
||||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.githubProfile.$error.url && !profileForm.githubProfile.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.githubProfile.$error.url && !profileForm.githubProfile.$pristine")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
@ -115,7 +127,7 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') CodePen
|
label.col-sm-3.col-sm-offset-2.control-label(for='email') CodePen
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='url', name='codepenProfile', id='codepenProfile', ng-model='user.profile.codepenProfile', placeholder='http://')
|
input.form-control(type='url', name='codepenProfile', id='codepenProfile', autocomplete="off", ng-model='user.profile.codepenProfile', placeholder='http://')
|
||||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.codepenProfile.$error.url && !profileForm.codepenProfile.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.codepenProfile.$error.url && !profileForm.codepenProfile.$pristine")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
@ -124,7 +136,7 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') CoderByte
|
label.col-sm-3.col-sm-offset-2.control-label(for='email') CoderByte
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='url', name='coderbyteProfile', id='coderbyteProfile', ng-model='user.profile.coderbyteProfile', placeholder='http://')
|
input.form-control(type='url', name='coderbyteProfile', id='coderbyteProfile', autocomplete="off", ng-model='user.profile.coderbyteProfile', placeholder='http://')
|
||||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.coderbyteProfile.$error.url && !profileForm.coderbyteProfile.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.coderbyteProfile.$error.url && !profileForm.coderbyteProfile.$pristine")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
@ -133,7 +145,7 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='email') LinkedIn
|
label.col-sm-3.col-sm-offset-2.control-label(for='email') LinkedIn
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='url', name='linkedinProfile', id='linkedinProfile', ng-model='user.profile.linkedinProfile', placeholder='http://')
|
input.form-control(type='url', name='linkedinProfile', id='linkedinProfile', autocomplete="off", ng-model='user.profile.linkedinProfile', placeholder='http://')
|
||||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.linkedinProfile.$error.url && !profileForm.linkedinProfile.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.linkedinProfile.$error.url && !profileForm.linkedinProfile.$pristine")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
@ -154,7 +166,7 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='website1Title') Title
|
label.col-sm-3.col-sm-offset-2.control-label(for='website1Title') Title
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='website1Title', id='website1Title', ng-model='user.portfolio.website1Title', ng-maxlength='140')
|
input.form-control(type='text', name='website1Title', id='website1Title', autocomplete="off", ng-model='user.portfolio.website1Title', ng-maxlength='140')
|
||||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website1Title.$error.maxlength && !profileForm.website1Title.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website1Title.$error.maxlength && !profileForm.website1Title.$pristine")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
@ -163,12 +175,12 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='website1Link') Link
|
label.col-sm-3.col-sm-offset-2.control-label(for='website1Link') Link
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='website1Link', id='website1Link', ng-model='user.portfolio.website1Link', placeholder='http://')
|
input.form-control(type='text', name='website1Link', id='website1Link', autocomplete="off", ng-model='user.portfolio.website1Link', placeholder='http://')
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='website1Image') Image Link (4:3 ratio)
|
label.col-sm-3.col-sm-offset-2.control-label(for='website1Image') Image Link (4:3 ratio)
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='website1Image', id='website1Image', ng-model='user.portfolio.website1Image', placeholder='http://')
|
input.form-control(type='text', name='website1Image', id='website1Image', autocomplete="off", ng-model='user.portfolio.website1Image', placeholder='http://')
|
||||||
|
|
||||||
.col-sm-4.col-sm-offset-5.flat-top
|
.col-sm-4.col-sm-offset-5.flat-top
|
||||||
h3 Second Portfolio Project
|
h3 Second Portfolio Project
|
||||||
@ -176,7 +188,7 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='website2Title') Title
|
label.col-sm-3.col-sm-offset-2.control-label(for='website2Title') Title
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='website2Title', id='website2Title', ng-model='user.portfolio.website2Title', ng-maxlength='140')
|
input.form-control(type='text', name='website2Title', id='website2Title', autocomplete="off", ng-model='user.portfolio.website2Title', ng-maxlength='140')
|
||||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website2Title.$error.maxlength && !profileForm.website2Title.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website2Title.$error.maxlength && !profileForm.website2Title.$pristine")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
@ -185,12 +197,12 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='website2Link') Link
|
label.col-sm-3.col-sm-offset-2.control-label(for='website2Link') Link
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='website2Link', id='website2Link', ng-model='user.portfolio.website2Link', placeholder='http://')
|
input.form-control(type='text', name='website2Link', id='website2Link', autocomplete="off", ng-model='user.portfolio.website2Link', placeholder='http://')
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='website2Image') Image Link (4:3 ratio)
|
label.col-sm-3.col-sm-offset-2.control-label(for='website2Image') Image Link (4:3 ratio)
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='website2Image', id='website2Image', ng-model='user.portfolio.website2Image', placeholder='http://')
|
input.form-control(type='text', name='website2Image', id='website2Image', autocomplete="off", ng-model='user.portfolio.website2Image', placeholder='http://')
|
||||||
|
|
||||||
.col-sm-4.col-sm-offset-5.flat-top
|
.col-sm-4.col-sm-offset-5.flat-top
|
||||||
h3 Third Portfolio Project
|
h3 Third Portfolio Project
|
||||||
@ -198,7 +210,7 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='website3Title') Title
|
label.col-sm-3.col-sm-offset-2.control-label(for='website3Title') Title
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='website3Title', id='website3Title', ng-model='user.portfolio.website3Title', ng-maxlength='140')
|
input.form-control(type='text', name='website3Title', id='website3Title', autocomplete="off", ng-model='user.portfolio.website3Title', ng-maxlength='140')
|
||||||
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website3Title.$error.maxlength && !profileForm.website3Title.$pristine")
|
.col-sm-4.col-sm-offset-5(ng-show="profileForm.website3Title.$error.maxlength && !profileForm.website3Title.$pristine")
|
||||||
alert(type='danger')
|
alert(type='danger')
|
||||||
span.ion-close-circled
|
span.ion-close-circled
|
||||||
@ -207,12 +219,12 @@ block content
|
|||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='website3Link') Link
|
label.col-sm-3.col-sm-offset-2.control-label(for='website3Link') Link
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='website3Link', id='website3Link', ng-model='user.portfolio.website3Link', placeholder='http://')
|
input.form-control(type='text', name='website3Link', id='website3Link', autocomplete="off", ng-model='user.portfolio.website3Link', placeholder='http://')
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
label.col-sm-3.col-sm-offset-2.control-label(for='website3Image') Image Link (4:3 ratio)
|
label.col-sm-3.col-sm-offset-2.control-label(for='website3Image') Image Link (4:3 ratio)
|
||||||
.col-sm-4
|
.col-sm-4
|
||||||
input.form-control(type='text', name='website3Image', id='website3Image', ng-model='user.portfolio.website3Image', placeholder='http://')
|
input.form-control(type='text', name='website3Image', id='website3Image', autocomplete="off", ng-model='user.portfolio.website3Image', placeholder='http://')
|
||||||
|
|
||||||
.form-group
|
.form-group
|
||||||
.col-sm-offset-5.col-sm-4
|
.col-sm-offset-5.col-sm-4
|
||||||
@ -229,7 +241,7 @@ block content
|
|||||||
a.btn.btn-lg.btn-block.btn-primary.btn-link-social(href='/') Take me to my current challenge
|
a.btn.btn-lg.btn-block.btn-primary.btn-link-social(href='/') Take me to my current challenge
|
||||||
a.btn.btn-lg.btn-block.btn-warning.btn-link-social(href='/logout') Sign out
|
a.btn.btn-lg.btn-block.btn-warning.btn-link-social(href='/logout') Sign out
|
||||||
br
|
br
|
||||||
- if (!user.google || !user.facebook || !user.github || !user.linkedin || !user.twitter)
|
- if (!user.google || !user.facebook || /*!user.github ||*/ !user.linkedin || !user.twitter)
|
||||||
.panel.panel-primary
|
.panel.panel-primary
|
||||||
.panel-heading.text-center Link other services to your account:
|
.panel-heading.text-center Link other services to your account:
|
||||||
.panel-body
|
.panel-body
|
||||||
@ -308,7 +320,3 @@ block content
|
|||||||
button.btn.btn-danger.btn-block(type='submit')
|
button.btn.btn-danger.btn-block(type='submit')
|
||||||
span.ion-trash-b
|
span.ion-trash-b
|
||||||
| Yes, Delete my account
|
| Yes, Delete my account
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
doctype html
|
doctype html
|
||||||
html(ng-app='profileValidation')
|
html(ng-app='profileValidation')
|
||||||
head
|
head
|
||||||
script(src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js")
|
script(src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js")
|
||||||
script(src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js")
|
script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.8/angular.min.js")
|
||||||
script(src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.0/ui-bootstrap-tpls.min.js")
|
script(src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.12.0/ui-bootstrap-tpls.min.js")
|
||||||
script(src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js')
|
script(src='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js')
|
||||||
link(rel='shortcut icon', href='https://s3.amazonaws.com/freecodecamp/favicon.ico')
|
link(rel='shortcut icon', href='//s3.amazonaws.com/freecodecamp/favicon.ico')
|
||||||
link(rel='stylesheet', href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css')
|
link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css')
|
||||||
link(rel='stylesheet', href='https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css')
|
link(rel='stylesheet', href='//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css')
|
||||||
link(rel='stylesheet', href='https://code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css')
|
link(rel='stylesheet', href='//code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css')
|
||||||
include partials/meta
|
include partials/meta
|
||||||
title #{title} | Free Code Camp
|
title #{title} | Free Code Camp
|
||||||
meta(charset='utf-8')
|
meta(charset='utf-8')
|
||||||
|
Reference in New Issue
Block a user