@ -125,7 +125,7 @@ window.common = (function({ $, common = { init: [] }}) {
|
||||
// assume api returns string when fails
|
||||
return $el.parent()
|
||||
.find('.disabled')
|
||||
.replaceWith('<p>' + data + '</p>');
|
||||
.replaceWith('<p class="col-sm-4 col-xs-12">' + data + '</p>');
|
||||
})
|
||||
.fail(function() {
|
||||
console.log('failed');
|
||||
|
@ -64,7 +64,6 @@ body.top-and-bottom-margins {
|
||||
}
|
||||
|
||||
body.no-top-and-bottom-margins {
|
||||
display: none;
|
||||
margin: 75px 20px 0px 20px;
|
||||
}
|
||||
|
||||
|
@ -165,6 +165,7 @@ main.setMapShare = function setMapShare(id) {
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
const { Observable } = window.Rx;
|
||||
var CSRF_HEADER = 'X-CSRF-Token';
|
||||
|
||||
var setCSRFToken = function(securityToken) {
|
||||
@ -548,51 +549,68 @@ $(document).ready(function() {
|
||||
// keyboard shortcuts: open map
|
||||
window.Mousetrap.bind('g m', toggleMap);
|
||||
|
||||
// Night Mode
|
||||
function changeMode() {
|
||||
var newValue = false;
|
||||
try {
|
||||
newValue = !JSON.parse(localStorage.getItem('nightMode'));
|
||||
} catch (e) {
|
||||
console.error('Error parsing value form local storage:', 'nightMode', e);
|
||||
}
|
||||
localStorage.setItem('nightMode', String(newValue));
|
||||
toggleNightMode(newValue);
|
||||
function addAlert(message = '', type = 'alert-info') {
|
||||
return $('.flashMessage').append($(`
|
||||
<div class='alert ${type}'>
|
||||
<button class='close' type='button', data-dismiss='alert'>
|
||||
<span class='ion-close-circled' />
|
||||
</Button>
|
||||
<div>${message}</div>
|
||||
</div>
|
||||
`));
|
||||
}
|
||||
|
||||
function toggleNightMode(nightModeEnabled) {
|
||||
var iframe = document.getElementById('map-aside-frame');
|
||||
if (iframe) {
|
||||
iframe.src = iframe.src;
|
||||
function toggleNightMode() {
|
||||
if (!main.userId) {
|
||||
return addAlert('You must be logged in to use our themes.');
|
||||
}
|
||||
var body = $('body');
|
||||
body.hide();
|
||||
if (nightModeEnabled) {
|
||||
body.addClass('night');
|
||||
const iframe$ = document.getElementById('map-aside-frame');
|
||||
const body$ = $('body');
|
||||
if (iframe$) {
|
||||
iframe$.src = iframe$.src;
|
||||
}
|
||||
body$.hide();
|
||||
let updateThemeTo;
|
||||
if (body$.hasClass('night')) {
|
||||
body$.removeClass('night');
|
||||
updateThemeTo = 'default';
|
||||
} else {
|
||||
body.removeClass('night');
|
||||
body$.addClass('night');
|
||||
updateThemeTo = 'night';
|
||||
}
|
||||
body.fadeIn('100');
|
||||
body$.fadeIn('100');
|
||||
const options = {
|
||||
url: `/api/users/${main.userId}/update-theme`,
|
||||
type: 'POST',
|
||||
data: { theme: updateThemeTo },
|
||||
dataType: 'json'
|
||||
};
|
||||
return $.ajax(options)
|
||||
.success(() => console.log('theme updated successfully'))
|
||||
.fail(err => {
|
||||
let message;
|
||||
try {
|
||||
message = JSON.parse(err.responseText).error.message;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
return addAlert(message);
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof localStorage.getItem('nightMode') !== 'undefined') {
|
||||
var oldVal = false;
|
||||
try {
|
||||
oldVal = JSON.parse(localStorage.getItem('nightMode'));
|
||||
} catch (e) {
|
||||
console.error('Error parsing value form local storage:', 'nightMode', e);
|
||||
}
|
||||
toggleNightMode(oldVal);
|
||||
$('.nightMode-btn').on('click', function() {
|
||||
changeMode();
|
||||
});
|
||||
} else {
|
||||
localStorage.setItem('nightMode', 'false');
|
||||
toggleNightMode('false');
|
||||
}
|
||||
Observable.merge(
|
||||
Observable.fromEvent($('#night-mode'), 'click'),
|
||||
Observable.create(observer => {
|
||||
window.Mousetrap.bind('g t n', () => observer.onNext());
|
||||
})
|
||||
)
|
||||
.debounce(500)
|
||||
.subscribe(toggleNightMode, err => console.error(err));
|
||||
|
||||
// Hot Keys
|
||||
window.Mousetrap.bind('g t n', changeMode);
|
||||
window.Mousetrap.bind('g n n', () => {
|
||||
// Next Challenge
|
||||
window.location = '/challenges/next-challenge';
|
||||
|
@ -3,6 +3,7 @@ import uuid from 'node-uuid';
|
||||
import moment from 'moment';
|
||||
import dedent from 'dedent';
|
||||
import debugFactory from 'debug';
|
||||
import { isEmail } from 'validator';
|
||||
|
||||
import { saveUser, observeMethod } from '../../server/utils/rx';
|
||||
import { blacklistedUsernames } from '../../server/utils/constants';
|
||||
@ -62,6 +63,9 @@ module.exports = function(User) {
|
||||
|
||||
User.observe('before save', function({ instance: user }, next) {
|
||||
if (user) {
|
||||
if (user.email && !isEmail(user.email)) {
|
||||
return next(new Error('Email format is not valid'));
|
||||
}
|
||||
user.username = user.username.trim().toLowerCase();
|
||||
user.email = typeof user.email === 'string' ?
|
||||
user.email.trim().toLowerCase() :
|
||||
@ -75,7 +79,7 @@ module.exports = function(User) {
|
||||
user.progressTimestamps.push({ timestamp: Date.now() });
|
||||
}
|
||||
}
|
||||
next();
|
||||
return next();
|
||||
});
|
||||
|
||||
debug('setting up user hooks');
|
||||
@ -93,6 +97,9 @@ module.exports = function(User) {
|
||||
if (!req.body.email) {
|
||||
return next();
|
||||
}
|
||||
if (!isEmail(req.body.email)) {
|
||||
return next(new Error('Email format is not valid'));
|
||||
}
|
||||
return User.doesExist(null, req.body.email)
|
||||
.then(exists => {
|
||||
if (!exists) {
|
||||
@ -118,6 +125,10 @@ module.exports = function(User) {
|
||||
});
|
||||
|
||||
User.on('resetPasswordRequest', function(info) {
|
||||
if (!isEmail(info.email)) {
|
||||
console.error(new Error('Email format is not valid'));
|
||||
return null;
|
||||
}
|
||||
let url;
|
||||
const host = User.app.get('host');
|
||||
const { id: token } = info.accessToken;
|
||||
@ -150,7 +161,7 @@ module.exports = function(User) {
|
||||
`
|
||||
};
|
||||
|
||||
User.app.models.Email.send(mailOptions, function(err) {
|
||||
return User.app.models.Email.send(mailOptions, function(err) {
|
||||
if (err) { console.error(err); }
|
||||
debug('email reset sent');
|
||||
});
|
||||
@ -159,9 +170,12 @@ module.exports = function(User) {
|
||||
User.beforeRemote('login', function(ctx, notUsed, next) {
|
||||
const { body } = ctx.req;
|
||||
if (body && typeof body.email === 'string') {
|
||||
if (!isEmail(body.email)) {
|
||||
return next(new Error('Email format is not valid'));
|
||||
}
|
||||
body.email = body.email.toLowerCase();
|
||||
}
|
||||
next();
|
||||
return next();
|
||||
});
|
||||
|
||||
User.afterRemote('login', function(ctx, accessToken, next) {
|
||||
@ -216,7 +230,7 @@ module.exports = function(User) {
|
||||
});
|
||||
|
||||
User.doesExist = function doesExist(username, email) {
|
||||
if (!username && !email) {
|
||||
if (!username && (!email || !isEmail(email))) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
debug('checking existence');
|
||||
@ -309,6 +323,11 @@ module.exports = function(User) {
|
||||
);
|
||||
|
||||
User.prototype.updateEmail = function updateEmail(email) {
|
||||
if (!isEmail(email)) {
|
||||
return Promise.reject(
|
||||
new Error('The submitted email not valid')
|
||||
);
|
||||
}
|
||||
if (this.email && this.email === email) {
|
||||
return Promise.reject(new Error(
|
||||
`${email} is already associated with this account.`
|
||||
@ -467,6 +486,91 @@ module.exports = function(User) {
|
||||
}
|
||||
);
|
||||
|
||||
User.prototype.updateEmail = function updateEmail(email) {
|
||||
if (this.email && this.email === email) {
|
||||
return Promise.reject(new Error(
|
||||
`${email} is already associated with this account.`
|
||||
));
|
||||
}
|
||||
return User.doesExist(null, email)
|
||||
.then(exists => {
|
||||
if (exists) {
|
||||
return Promise.reject(
|
||||
new Error(`${email} is already associated with another account.`)
|
||||
);
|
||||
}
|
||||
return this.update$({ email }).toPromise();
|
||||
});
|
||||
};
|
||||
|
||||
User.remoteMethod(
|
||||
'updateEmail',
|
||||
{
|
||||
isStatic: false,
|
||||
description: 'updates the email of the user object',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'email',
|
||||
type: 'string',
|
||||
required: true
|
||||
}
|
||||
],
|
||||
returns: [
|
||||
{
|
||||
arg: 'status',
|
||||
type: 'object'
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: '/update-email',
|
||||
verb: 'POST'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
User.themes = {
|
||||
night: true,
|
||||
default: true
|
||||
};
|
||||
User.prototype.updateTheme = function updateTheme(theme) {
|
||||
if (!this.constructor.themes[theme]) {
|
||||
const err = new Error(
|
||||
'Theme is not valid.'
|
||||
);
|
||||
err.messageType = 'info';
|
||||
err.userMessage = err.message;
|
||||
return Promise.reject(err);
|
||||
}
|
||||
return this.update$({ theme })
|
||||
.map({ updatedTo: theme })
|
||||
.toPromise();
|
||||
};
|
||||
|
||||
User.remoteMethod(
|
||||
'updateTheme',
|
||||
{
|
||||
isStatic: false,
|
||||
description: 'updates the users chosen theme',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'theme',
|
||||
type: 'string',
|
||||
required: true
|
||||
}
|
||||
],
|
||||
returns: [
|
||||
{
|
||||
arg: 'status',
|
||||
type: 'object'
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: '/update-theme',
|
||||
verb: 'POST'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// user.updateTo$(updateData: Object) => Observable[Number]
|
||||
User.prototype.update$ = function update$(updateData) {
|
||||
const id = this.getId();
|
||||
|
@ -188,6 +188,10 @@
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string"
|
||||
},
|
||||
"theme": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
@ -242,6 +246,13 @@
|
||||
"principalId": "$owner",
|
||||
"permission": "ALLOW",
|
||||
"property": "updateEmail"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$owner",
|
||||
"permission": "ALLOW",
|
||||
"property": "updateTheme"
|
||||
}
|
||||
],
|
||||
"methods": []
|
||||
|
@ -453,7 +453,7 @@
|
||||
"tests": [
|
||||
"assert(myVar === 88, 'message: <code>myVar</code> should equal <code>88</code>');",
|
||||
"assert(/myVar\\s*\\=.*myVar/.test(code) === false, 'message: <code>myVar = myVar</code> should be changed');",
|
||||
"assert(/myVar\\s*[+]{2}/.test(code), 'message: Use the <code>++</code> operator');",
|
||||
"assert(/[+]{2}\\s*myVar|myVar\\s*[+]{2}/.test(code), 'message: Use the <code>++</code> operator');",
|
||||
"assert(/var myVar = 87;/.test(code), 'message: Do not change code above the line');"
|
||||
],
|
||||
"type": "waypoint",
|
||||
@ -497,7 +497,7 @@
|
||||
],
|
||||
"tests": [
|
||||
"assert(myVar === 10, 'message: <code>myVar</code> should equal <code>10</code>');",
|
||||
"assert(/myVar\\s*[-]{2}/.test(code), 'message: Use the <code>--</code> operator on <code>myVar</code>');",
|
||||
"assert(/[-]{2}\\s*myVar|myVar\\s*[-]{2}/.test(code), 'message: Use the <code>--</code> operator on <code>myVar</code>');",
|
||||
"assert(/var myVar = 11;/.test(code), 'message: Do not change code above the line');"
|
||||
],
|
||||
"type": "waypoint",
|
||||
|
@ -365,9 +365,14 @@
|
||||
"id": "bad87fee1348cd8acef08812",
|
||||
"title": "Create a Block Element Bootstrap Button",
|
||||
"description": [
|
||||
"Normally, your <code>button</code> elements are only as wide as the text that they contain. By making them block elements, your button will stretch to fill your page's entire horizontal space and any elements following it will flow onto a \"new line\" below the block.",
|
||||
"This image illustrates the difference between <code>inline</code> elements and <code>block-level</code> elements:",
|
||||
"<a href=\"//i.imgur.com/O32cDWE.png\" data-lightbox=\"img-enlarge\"><img class=\"img-responsive\" src=\"//i.imgur.com/O32cDWE.png\" title=\"Click to enlarge\" alt=\"An \"inline\" button is as small as the text it contains. In this image, it's centered. Below it is a \"block-level\" button, which stretches to fill the entire horizontal space.'></a>",
|
||||
"Normally, your <code>button</code> elements with a class of <code>btn</code> are only as wide as the text that they contain. For example:",
|
||||
"<code><button class=\"btn\">Submit</button></code>",
|
||||
"This button would only be as wide as the word \"Submit\".",
|
||||
"<button class='btn' style='background-color: rgb(0, 100, 0); color: rgb(255, 255, 255);'>Submit</button>",
|
||||
"By making them block elements with the additional class of <code>btn-block</code>, your button will stretch to fill your page's entire horizontal space and any elements following it will flow onto a \"new line\" below the block.",
|
||||
"<code><button class=\"btn btn-block\">Submit</button></code>",
|
||||
"This button would take up 100% of the available width.",
|
||||
"<button class='btn btn-block' style='background-color: rgb(0, 100, 0); color: rgb(255, 255, 255);'>Submit</button>",
|
||||
"Note that these buttons still need the <code>btn</code> class.",
|
||||
"Add Bootstrap's <code>btn-block</code> class to your Bootstrap button."
|
||||
],
|
||||
@ -440,17 +445,27 @@
|
||||
"challengeType": 0,
|
||||
"titleEs": "Crea un elemento botón de bloque con Bootstrap",
|
||||
"descriptionEs": [
|
||||
"Normalmente, tus elementos <code>button</code> son sólo tan anchos como el texto que contienen. Al convertir un botón en elemento a nivel de bloque, este se estirará para llenar todo el espacio horizontal y cualquier elemento que lo siga se desplazará a una \"nueva línea\" debajo del bloque.",
|
||||
"Esta imagen es un ejemplo de la diferencia entre elementos en línea (<code>inline</code>) y elementos a nivel de bloque (<code>block-level</code>):",
|
||||
"<a href=\"//i.imgur.com/O32cDWE.png\" data-lightbox=\"img-enlarge\"><img class=\"img-responsive\" src=\"//i.imgur.com/O32cDWE.png\" title=\"Pulsa para agrandar\" alt=\"Un botón \"en línea\" es tan pequeño como el texto que contiene. En esta imagen, está centrado. Debajo de este hay un botón \"a nivel de bloque\", que se estira hasta llenar todo el espacio horizontal.'></a>",
|
||||
"Normalmente , los elementos de <code>button</code> con una clase de <code>btn</code> sólo son tan ancha como el texto que contienen. Por ejemplo:",
|
||||
"<code><button class=\"btn\">Enviar</button></code>",
|
||||
"Este botón sólo sería tan amplia como la palabra \"Enviar\"",
|
||||
"<button class='btn' style='background-color: rgb(0, 100, 0); color: rgb(255, 255, 255);'>Enviar</button>",
|
||||
"Haciéndolos bloquean elementos con la clase adicional de <code>btn-block</code>, el botón se amplía para llenar toda espacio horizontal de la página y los elementos siguientes fluirá sobre una \"nueva línea\" debajo del bloque .",
|
||||
"<code><button class=\"btn btn-block\">Enviar</button></code>",
|
||||
"Este botón llevaría hasta el 100% de la anchura disponible.",
|
||||
"<button class='btn btn-block' style='background-color: rgb(0, 100, 0); color: rgb(255, 255, 255);'>Enviar</button>",
|
||||
"Ten en cuenta que estos botones todavía necesitan la clase <code>btn</code>.",
|
||||
"Agrega la clase de Bootstrap <code>btn-block</code> a tu botón Bootstrap."
|
||||
],
|
||||
"nameFr": "Créer un bouton bloc Bootstrap",
|
||||
"descriptionFr": [
|
||||
"Normalement, vos éléments <code>button</code> sont aussi large que le texte qu'ils contiennent. En les transformants en éléments blocs, vos boutons vont s'ajuster pour remplir l'intégralité de l'espace horizontal de la page et tous les éléments qui le suivront se placeront sur une \"nouvelle ligne\" en dessous du bloc.",
|
||||
"Cette image illustre la différence entre éléments <code>inline</code> (sans briser la ligne) et éléments <code>block-level</code> (en blocs)",
|
||||
"<a href=\"https://i.imgur.com/O32cDWE.png\" data-lightbox=\"img-enlarge\"><img class=\"img-responsive\" src=\"https://i.imgur.com/O32cDWE.png\" title=\"Cliquez pour agrandir\" alt=\"Un bouton \"inline\" est aussi petit que le texte qu'il contient. Dans cette image, il est centré. En dessous de celui-ci il y a un bouton \"block-level\" , qui s'étire et remplit l'espace horizontal.'></a>",
|
||||
"Normalement , vos éléments de <code>button</code> avec une classe de <code>btn</code> ne sont aussi larges que le texte qu'ils contiennent . Par exemple:",
|
||||
"<code><button class=\"btn\">Soumettre</button></code>",
|
||||
"Ce bouton ne serait plus large que le mot \"Soumettre\" .",
|
||||
"<button class='btn' style='background-color: rgb(0, 100, 0); color: rgb(255, 255, 255);'>Soumettre</button>",
|
||||
"En leur faisant bloquer les éléments avec la classe supplémentaire de <code>btn-block</code>, votre bouton étirer pour remplir tout l'espace horizontal de votre page et tous les éléments suivants, il coulera sur une \"nouvelle ligne\" en dessous du bloc .",
|
||||
"<code><button class=\"btn btn-block\">Soumettre</button></code>",
|
||||
"Ce bouton prendrait 100% de la largeur disponible .",
|
||||
"<button class='btn btn-block' style='background-color: rgb(0, 100, 0); color: rgb(255, 255, 255);'>Soumettre</button>",
|
||||
"Notez que ces boutons ont toujours besoin de la classe <code>btn</code>",
|
||||
"Ajoutez la classe Bootstrap <code>btn-block</code> à votre bouton Bootstrap."
|
||||
]
|
||||
|
@ -1953,8 +1953,8 @@
|
||||
"<p>Top 3 things cats hate:</p>"
|
||||
],
|
||||
"tests": [
|
||||
"assert.equal($(\"ol\").prev().text(), 'Top 3 things cats hate:', 'message: Your should have an ordered list for \"Top 3 things cats hate\"');",
|
||||
"assert.equal($(\"ul\").prev().text(), \"Things cats love:\", 'message: You should have an unordered list for \"Things Cats Love\"');",
|
||||
"assert.equal($(\"ol\").prev().text(), 'Top 3 things cats hate:', 'message: You should have an ordered list for \"Top 3 things cats hate:\"');",
|
||||
"assert.equal($(\"ul\").prev().text(), \"Things cats love:\", 'message: You should have an unordered list for \"Things cats love:\"');",
|
||||
"assert.equal($(\"ul li\").length, 3, 'message: You should have three <code>li</code> elements within your <code>ul</code> element.');",
|
||||
"assert.equal($(\"ol li\").length, 3, 'message: You should have three <code>li</code> elements within your <code>ol</code> element.');",
|
||||
"assert(code.match(/<\\/ul>/g) && code.match(/<\\/ul>/g).length === code.match(/<ul>/g).length, 'message: Make sure your <code>ul</code> element has a closing tag.');",
|
||||
|
@ -481,14 +481,14 @@
|
||||
"Remember to use <a href='//github.com/FreeCodeCamp/freecodecamp/wiki/How-to-get-help-when-you-get-stuck' target='_blank'>Read-Search-Ask</a> if you get stuck. Try to pair program. Write your own code."
|
||||
],
|
||||
"challengeSeed": [
|
||||
"function uniteUnique(arr1, arr2, arr3) {",
|
||||
" return arr1;",
|
||||
"function uniteUnique(arr) {",
|
||||
" return arr;",
|
||||
"}",
|
||||
"",
|
||||
"uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]);"
|
||||
],
|
||||
"solutions": [
|
||||
"function uniteUnique(arr1, arr2, arr3) {\n return [].slice.call(arguments).reduce(function(a, b) {\n return [].concat(a, b.filter(function(e) {return a.indexOf(e) === -1;}));\n }, []);\n}"
|
||||
"function uniteUnique(arr) {\n return [].slice.call(arguments).reduce(function(a, b) {\n return [].concat(a, b.filter(function(e) {return a.indexOf(e) === -1;}));\n }, []);\n}"
|
||||
],
|
||||
"tests": [
|
||||
"assert.deepEqual(uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1]), [1, 3, 2, 5, 4], 'message: <code>uniteUnique([1, 3, 2], [5, 2, 1, 4], [2, 1])</code> should return <code>[1, 3, 2, 5, 4]</code>.');",
|
||||
|
@ -447,7 +447,7 @@
|
||||
"You will see a prompt to allow or block this site from knowing your current location. The challenge can be completed either way, as long as the code is correct.",
|
||||
"By selecting allow you will see the text on the output phone change to your latitude and longitude",
|
||||
"Here's some code that does this:",
|
||||
"<blockquote>if (navigator.geolocation) {</br> navigator.geolocation.getCurrentPosition(function(position) {</br> $(\"#data\").html(\"latitude: \" + position.coords.latitude + \"<br>longitude: \" + position.coords.longitude);</br> });</br>}</blockquote>"
|
||||
"<blockquote>if (navigator.geolocation) {<br> navigator.geolocation.getCurrentPosition(function(position) {<br> $(\"#data\").html(\"latitude: \" + position.coords.latitude + \"<br>longitude: \" + position.coords.longitude);<br> });<br>}</blockquote>"
|
||||
],
|
||||
"challengeSeed": [
|
||||
"fccss",
|
||||
@ -463,7 +463,10 @@
|
||||
"</div>"
|
||||
],
|
||||
"tests": [
|
||||
"assert(code.match(/navigator\\.geolocation\\.getCurrentPosition/gi), 'message: You should make use of the <code>navigator.geolocation</code> to access the users current location.');"
|
||||
"assert(code.match(/navigator\\.geolocation\\.getCurrentPosition/gi), 'message: You should make use of <code>navigator.geolocation</code> to access the users current location.');",
|
||||
"assert(code.match(/position\\.coords\\.latitude/gi), 'message: You should make use of <code>position.coords.latitude</code> to display the users' latitudinal location.');",
|
||||
"assert(code.match(/position\\.coords\\.longitude/gi), 'message: You should make use of <code>position.coords.longitude</code> to display the users' longitudinal location.');",
|
||||
"assert(code.match(/\\$\\(\\s*\"#data\"\\s*\\)\\s*\\.html\\([.\\w\\W]*?\\);/gi), 'message: You should display the users' position within the <code>data</code> div element');"
|
||||
],
|
||||
"type": "waypoint",
|
||||
"challengeType": 0,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Observable } from 'rx';
|
||||
import debugFactory from 'debug';
|
||||
import { isEmail } from 'validator';
|
||||
|
||||
const debug = debugFactory('fcc:user:remote');
|
||||
|
||||
@ -59,7 +60,7 @@ module.exports = function(app) {
|
||||
// send welcome email to new camper
|
||||
User.afterRemote('create', function({ req, res }, user, next) {
|
||||
debug('user created, sending email');
|
||||
if (!user.email) { return next(); }
|
||||
if (!user.email || !isEmail(user.email)) { return next(); }
|
||||
const redirect = req.session && req.session.returnTo ?
|
||||
req.session.returnTo :
|
||||
'/';
|
||||
|
@ -550,6 +550,7 @@ module.exports = function(app) {
|
||||
}
|
||||
|
||||
function postForgot(req, res) {
|
||||
req.validate('email', 'Email format is not valid').isEmail();
|
||||
const errors = req.validationErrors();
|
||||
const email = req.body.email.toLowerCase();
|
||||
|
||||
|
@ -6,6 +6,10 @@ export default function globalLocals() {
|
||||
if (req.csrfToken) {
|
||||
res.expose({ token: res.locals._csrf }, 'csrf');
|
||||
}
|
||||
res.locals.theme = req.user && req.user.theme ||
|
||||
req.cookies.theme ||
|
||||
'default';
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ export function ifNoUserRedirectTo(url, message, type = 'errors') {
|
||||
}
|
||||
|
||||
req.flash(type, {
|
||||
msg: message || `You must be signed to go to ${path}`
|
||||
msg: message || `You must be signed in to access ${path}`
|
||||
});
|
||||
|
||||
return res.redirect(url);
|
||||
|
@ -5,7 +5,7 @@ block content
|
||||
h2.text-center Actions
|
||||
.row
|
||||
.col-xs-12
|
||||
a.btn.btn-lg.btn-block.btn-primary.btn-link-social(class = "nightMode-btn") Night Mode
|
||||
a#night-mode.btn.btn-lg.btn-block.btn-primary.btn-link-social Night Mode
|
||||
.row
|
||||
.col-xs-12
|
||||
if (!user.isGithubCool)
|
||||
@ -133,4 +133,4 @@ block content
|
||||
form(action='/account/delete', method='POST')
|
||||
input(type='hidden', name='_csrf', value=_csrf)
|
||||
button.btn.btn-danger.btn-block(type='submit')
|
||||
| I am 100% sure I want to delete my account and all of my progress
|
||||
| I am 100% sure I want to delete my account and all of my progress
|
||||
|
@ -19,12 +19,12 @@ block content
|
||||
if index === 0
|
||||
.col-sm-4.hidden-xs
|
||||
else
|
||||
.btn.btn-primary.btn-primary-ghost.col-sm-4.col-xs-12.challenge-step-btn-prev.btn-lg(id='#{index - 1}') Go to my previous step
|
||||
button.btn.btn-primary.btn-primary-ghost.col-sm-4.col-xs-12.challenge-step-btn-prev.btn-lg(id='#{index - 1}') Go to my previous step
|
||||
.challenge-step-counter.large-p.col-sm-4.col-xs-12.text-center (#{index + 1} / #{description.length})
|
||||
if index + 1 === description.length
|
||||
.btn.btn-primary.col-sm-4.col-xs-12.challenge-step-btn-finish.btn-lg(id='last' class=step[3] && !isCompleted ? 'disabled' : '') Finish challenge
|
||||
button.btn.btn-primary.col-sm-4.col-xs-12.challenge-step-btn-finish.btn-lg(id='last' class=step[3] && !isCompleted ? 'disabled' : '') Finish challenge
|
||||
else
|
||||
.btn.btn-primary.col-sm-4.col-xs-12.challenge-step-btn-next.btn-lg(id='#{index}' class=step[3] && !isCompleted ? 'disabled' : '') Go to my next step
|
||||
button.btn.btn-primary.col-sm-4.col-xs-12.challenge-step-btn-next.btn-lg(id='#{index}' class=step[3] && !isCompleted ? 'disabled' : '') Go to my next step
|
||||
.clearfix
|
||||
.spacer
|
||||
#challenge-step-modal.modal(tabindex='-1')
|
||||
|
@ -8,9 +8,9 @@ html(lang='en')
|
||||
include partials/scripts
|
||||
block content
|
||||
else
|
||||
body.no-top-and-bottom-margins
|
||||
include partials/scripts
|
||||
include partials/navbar
|
||||
include partials/flash
|
||||
block content
|
||||
include partials/footer
|
||||
body.no-top-and-bottom-margins(class=theme !== 'default' ? theme : '')
|
||||
include partials/scripts
|
||||
include partials/navbar
|
||||
include partials/flash
|
||||
block content
|
||||
include partials/footer
|
||||
|
@ -1,6 +1,6 @@
|
||||
extends ../layout-wide
|
||||
block content
|
||||
.mapWrapper
|
||||
.mapWrapper
|
||||
.text-center.map-fixed-header
|
||||
p Challenges required for certifications are marked with a *
|
||||
.row.map-buttons
|
||||
@ -9,7 +9,7 @@ block content
|
||||
.input-group
|
||||
input#map-filter.form-control(type="text" placeholder="Type a challenge name" autocomplete="off" value="")
|
||||
span.input-group-addon
|
||||
i.fa.fa-search
|
||||
i.fa.fa-search
|
||||
hr
|
||||
include ../partials/flash
|
||||
#accordion.map-accordion
|
||||
@ -122,3 +122,4 @@ block content
|
||||
p.challenge-title.disabled.text-primary.ion-locked.padded-ionic-icon.negative-15(name="Mock Interview #2") Mock Interview #2
|
||||
p.challenge-title.disabled.text-primary.ion-locked.padded-ionic-icon.negative-15(name="Mock Interview #3") Mock Interview #3
|
||||
.spacer
|
||||
include ../partials/flash
|
||||
|
@ -1,6 +1,6 @@
|
||||
.container
|
||||
.row.flashMessage.negative-30
|
||||
.col-xs-12
|
||||
.col-xs-12.col-sm-8.col-sm-offset-2.col-md-6.col-md-offset-3
|
||||
if (messages.errors || messages.error)
|
||||
.alert.alert-danger.fade.in
|
||||
button.close(type='button', data-dismiss='alert')
|
||||
|
@ -7,5 +7,11 @@ script.
|
||||
ga('require', 'displayfeatures');
|
||||
ga('send', 'pageview');
|
||||
// Leave the below lines alone!
|
||||
script.
|
||||
(function(global) {
|
||||
global.main = global.main || {};
|
||||
global.main.isLoggedIn = !{JSON.stringify(!!user)};
|
||||
global.main.userId = !{JSON.stringify(user && user.id || false)};
|
||||
}(window))
|
||||
script(src=rev('/js', 'vendor-main.js'))
|
||||
script(src=rev('/js', 'main.js'))
|
||||
|
Reference in New Issue
Block a user