diff --git a/.eslintrc b/.eslintrc
index 5e924f47ed..216a7b76a9 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -174,7 +174,7 @@
"one-var": 0,
"operator-assignment": 0,
"padded-blocks": 0,
- "quote-props": 0,
+ "quote-props": [2, "as-needed"],
"quotes": [
2,
"single",
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a4b97c4e40..7b3ab1b860 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -34,8 +34,13 @@ If you've found a bug that is not on the board, [follow these steps](#found-a-bu
## Prerequisites
-- [MongoDB](http://www.mongodb.org/downloads)
-- [Node.js](http://nodejs.org)
+| Prerequisite | Version |
+|--------------|---------|
+| [MongoDB](http://www.mongodb.org/downloads) | `~ ^3` |
+| [Node.js](http://nodejs.org) | `~ ^4` |
+| npm | `~ ^2` |
+
+> _Updating to the latest releases is recomended_.
## Getting Started
diff --git a/client/main.js b/client/main.js
index b7ed7c9bd0..476e280681 100644
--- a/client/main.js
+++ b/client/main.js
@@ -97,7 +97,11 @@ main = (function(main, global) {
});
- $('#nav-chat-btn').on('click', toggleMainChat);
+ $('#nav-chat-btn').on('click', function(event) {
+ if (!(event.ctrlKey || event.metaKey)) {
+ toggleMainChat();
+ }
+ });
function showMainChat() {
if (!main.chat.isOpen) {
@@ -350,7 +354,11 @@ $(document).ready(function() {
var mapFilter = $('#map-filter');
var mapShowAll = $('#showAll');
- $('#nav-map-btn').on('click', toggleMap);
+ $('#nav-map-btn').on('click', function(event) {
+ if (!(event.ctrlKey || event.metaKey)) {
+ toggleMap();
+ }
+ });
$('.map-aside-action-collapse').on('click', collapseMap);
@@ -381,7 +389,11 @@ $(document).ready(function() {
}
}
- $('#nav-wiki-btn').on('click', toggleWiki);
+ $('#nav-wiki-btn').on('click', function(event) {
+ if (!(event.ctrlKey || event.metaKey)) {
+ toggleWiki();
+ }
+ });
$('.wiki-aside-action-collapse').on('click', collapseWiki);
diff --git a/common/app/components/Nav/NavItem.jsx b/common/app/components/Nav/NavItem.jsx
index 4e4d084dcc..465cabf7df 100644
--- a/common/app/components/Nav/NavItem.jsx
+++ b/common/app/components/Nav/NavItem.jsx
@@ -57,7 +57,7 @@ export default React.createClass({
const linkClassName = classNames(className, {
// 'active': active, we don't actually use the active class
// but it is used for a11y below
- 'disabled': disabled
+ disabled: disabled
});
let linkProps = {
diff --git a/common/app/redux/fetch-user-saga.js b/common/app/redux/fetch-user-saga.js
index 4f01aea99c..83b1fdf0ba 100644
--- a/common/app/redux/fetch-user-saga.js
+++ b/common/app/redux/fetch-user-saga.js
@@ -11,7 +11,7 @@ export default ({ services }) => ({ dispatch }) => next => {
.map(({
username,
picture,
- progressTimestamps = [],
+ points,
isFrontEndCert,
isBackEndCert,
isFullStackCert
@@ -21,7 +21,7 @@ export default ({ services }) => ({ dispatch }) => next => {
payload: {
username,
picture,
- points: progressTimestamps.length,
+ points,
isFrontEndCert,
isBackEndCert,
isFullStackCert,
diff --git a/common/app/routes/Jobs/components/NewJob.jsx b/common/app/routes/Jobs/components/NewJob.jsx
index 5cb6c990ac..3645d8c1e9 100644
--- a/common/app/routes/Jobs/components/NewJob.jsx
+++ b/common/app/routes/Jobs/components/NewJob.jsx
@@ -48,7 +48,7 @@ const certTypes = {
};
function isValidURL(data) {
- return isURL(data, { 'require_protocol': true });
+ return isURL(data, { require_protocol: true });
}
const fields = [
diff --git a/common/app/routes/Jobs/utils.js b/common/app/routes/Jobs/utils.js
index f2fc37d593..6a8f1705b7 100644
--- a/common/app/routes/Jobs/utils.js
+++ b/common/app/routes/Jobs/utils.js
@@ -1,5 +1,5 @@
const defaults = {
- 'string': {
+ string: {
value: '',
valid: false,
pristine: true,
diff --git a/common/models/user.js b/common/models/user.js
index 4c48f5e3ab..3f99fbba51 100644
--- a/common/models/user.js
+++ b/common/models/user.js
@@ -44,7 +44,7 @@ module.exports = function(User) {
// username should not be in blacklist
User.validatesExclusionOf('username', {
- 'in': blacklistedUsernames,
+ in: blacklistedUsernames,
message: 'is taken'
});
diff --git a/common/utils/ajax-stream.js b/common/utils/ajax-stream.js
index 1a95784a53..9ad5034b11 100644
--- a/common/utils/ajax-stream.js
+++ b/common/utils/ajax-stream.js
@@ -274,7 +274,7 @@ export function postJSON$(url, body) {
responseType: 'json',
headers: {
'Content-Type': 'application/json',
- 'Accept': 'application/json'
+ Accept: 'application/json'
}
})
.map(({ response }) => response);
@@ -303,7 +303,7 @@ export function getJSON$(url) {
responseType: 'json',
headers: {
'Content-Type': 'application/json',
- 'Accept': 'application/json'
+ Accept: 'application/json'
}
}).map(({ response }) => response);
}
diff --git a/package.json b/package.json
index 186a18056c..4ac012c6c1 100644
--- a/package.json
+++ b/package.json
@@ -91,6 +91,7 @@
"morgan": "^1.6.1",
"node-uuid": "^1.4.3",
"nodemailer": "^2.1.0",
+ "nodemailer-ses-transport": "^1.3.0",
"normalize-url": "^1.3.1",
"normalizr": "^2.0.0",
"object.assign": "^4.0.3",
diff --git a/seed/challenges/02-data-visualization-certification/data-visualization-projects.json b/seed/challenges/02-data-visualization-certification/data-visualization-projects.json
index 2490ae64c1..18c3ad4883 100644
--- a/seed/challenges/02-data-visualization-certification/data-visualization-projects.json
+++ b/seed/challenges/02-data-visualization-certification/data-visualization-projects.json
@@ -8,7 +8,7 @@
"id": "bd7168d8c242eddfaeb5bd13",
"title": "Visualize Data with a Bar Chart",
"description": [
- "Objective: Build a CodePen.io app that is functionally similar to this: https://codepen.io/FreeCodeCamp/full/adBBWd.",
+ "Objective: Build a CodePen.io app that is functionally similar to this: https://codepen.io/FreeCodeCamp/full/vGjLVZ.",
"Rule #1: Don't look at the example project's code. Figure it out for yourself.",
"Rule #2: Fulfill the below user stories. Use whichever libraries or APIs you need. Give it your own personal style.",
"Rule #3: You must use D3.js to build this project.",
@@ -29,7 +29,7 @@
"challengeType": 3,
"titleEs": "Visualiza datos utilizando un gráfico de barras",
"descriptionEs": [
- "Objetivo: Construye una aplicación en CodePen.io que funcione de forma similar al siguiente ejemplo: https://codepen.io/FreeCodeCamp/full/adBBWd.",
+ "Objetivo: Construye una aplicación en CodePen.io que funcione de forma similar al siguiente ejemplo: https://codepen.io/FreeCodeCamp/full/vGjLVZ.",
"Regla #1: No veas el código del proyecto de ejemplo. Encuentra la forma de hacerlo por tu cuenta.",
"Regla #2: Satisface las siguientes historias de usuario. Usa cualquier librería o API que sea necesaria. ¡Ponle un toque personal!.",
"Regla #3: Debes utilizar D3.js para construir este proyecto.",
diff --git a/seed/challenges/04-video-challenges/accessibility.json b/seed/challenges/04-video-challenges/accessibility.json
new file mode 100644
index 0000000000..96c5c6ed7b
--- /dev/null
+++ b/seed/challenges/04-video-challenges/accessibility.json
@@ -0,0 +1,18 @@
+{
+ "name": "Accessibility",
+ "order": 5.5,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bddd",
+ "title": "Learn Accessibility Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Accessibility"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/agile.json b/seed/challenges/04-video-challenges/agile.json
new file mode 100644
index 0000000000..039394e2db
--- /dev/null
+++ b/seed/challenges/04-video-challenges/agile.json
@@ -0,0 +1,18 @@
+{
+ "name": "Agile",
+ "order": 6,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbec5bdde",
+ "title": "Learn Agile Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Agile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/computer-science.json b/seed/challenges/04-video-challenges/computer-science.json
new file mode 100644
index 0000000000..f95ff9050b
--- /dev/null
+++ b/seed/challenges/04-video-challenges/computer-science.json
@@ -0,0 +1,18 @@
+{
+ "name": "Computer Science",
+ "order": 7,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bddc",
+ "title": "Learn Computer Science Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Computer Science"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/data-visualization.json b/seed/challenges/04-video-challenges/data-visualization.json
new file mode 100644
index 0000000000..638ebfd463
--- /dev/null
+++ b/seed/challenges/04-video-challenges/data-visualization.json
@@ -0,0 +1,18 @@
+{
+ "name": "Data Visualization",
+ "order": 8,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdde",
+ "title": "Learn Data Visualization Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Data Visualization"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/devops.json b/seed/challenges/04-video-challenges/devops.json
new file mode 100644
index 0000000000..3a07097d48
--- /dev/null
+++ b/seed/challenges/04-video-challenges/devops.json
@@ -0,0 +1,18 @@
+{
+ "name": "DevOps",
+ "order": 16,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdd5",
+ "title": "Learn DevOps Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "DevOps"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/embedded-and-internet-of-things.json b/seed/challenges/04-video-challenges/embedded-and-internet-of-things.json
new file mode 100644
index 0000000000..f7b0007072
--- /dev/null
+++ b/seed/challenges/04-video-challenges/embedded-and-internet-of-things.json
@@ -0,0 +1,18 @@
+{
+ "name": "Embedded and Internet of Things",
+ "order": 9,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdda",
+ "title": "Learn Embedded and Internet of Things Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Embedded and Internet of Things"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/game-development.json b/seed/challenges/04-video-challenges/game-development.json
new file mode 100644
index 0000000000..2be58e0274
--- /dev/null
+++ b/seed/challenges/04-video-challenges/game-development.json
@@ -0,0 +1,18 @@
+{
+ "name": "Game Development",
+ "order": 10,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdd0",
+ "title": "Learn Game Development Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Game Development"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/gamification.json b/seed/challenges/04-video-challenges/gamification.json
new file mode 100644
index 0000000000..64aaa1c743
--- /dev/null
+++ b/seed/challenges/04-video-challenges/gamification.json
@@ -0,0 +1,18 @@
+{
+ "name": "Gamification",
+ "order": 12,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdd9",
+ "title": "Learn Gamification Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Gamification"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/machine-learning.json b/seed/challenges/04-video-challenges/machine-learning.json
new file mode 100644
index 0000000000..fae9b6e1f0
--- /dev/null
+++ b/seed/challenges/04-video-challenges/machine-learning.json
@@ -0,0 +1,18 @@
+{
+ "name": "Machine Learning",
+ "order": 13,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdd8",
+ "title": "Learn Machine Learning Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Machine Learning"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/math-for-programmers.json b/seed/challenges/04-video-challenges/math-for-programmers.json
new file mode 100644
index 0000000000..c9c3460c20
--- /dev/null
+++ b/seed/challenges/04-video-challenges/math-for-programmers.json
@@ -0,0 +1,18 @@
+{
+ "name": "Math for Programmers",
+ "order": 14,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdd7",
+ "title": "Learn Math for Programmers Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Math for Programmers"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/mobile-javascript-development.json b/seed/challenges/04-video-challenges/mobile-javascript-development.json
new file mode 100644
index 0000000000..8189631bd0
--- /dev/null
+++ b/seed/challenges/04-video-challenges/mobile-javascript-development.json
@@ -0,0 +1,18 @@
+{
+ "name": "Mobile JavaScript Development",
+ "order": 15,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdd6",
+ "title": "Learn Mobile JavaScript Development Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Mobile JavaScript Development"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/software-engineering-principles.json b/seed/challenges/04-video-challenges/software-engineering-principles.json
new file mode 100644
index 0000000000..67a082f0f5
--- /dev/null
+++ b/seed/challenges/04-video-challenges/software-engineering-principles.json
@@ -0,0 +1,18 @@
+{
+ "name": "Software Engineering Principles",
+ "order": 17,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441ecdfbeb5bdd5",
+ "title": "Learn Software Engineering Principles Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Software Engineering Principles"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/statistics.json b/seed/challenges/04-video-challenges/statistics.json
new file mode 100644
index 0000000000..c604adb98d
--- /dev/null
+++ b/seed/challenges/04-video-challenges/statistics.json
@@ -0,0 +1,18 @@
+{
+ "name": "Statistics",
+ "order": 18,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdd4",
+ "title": "Learn Statistics Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Statistics"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/tools.json b/seed/challenges/04-video-challenges/tools.json
new file mode 100644
index 0000000000..625976986b
--- /dev/null
+++ b/seed/challenges/04-video-challenges/tools.json
@@ -0,0 +1,18 @@
+{
+ "name": "Tools",
+ "order": 19,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdd3",
+ "title": "Learn Tools Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Tools"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/user-experience-design.json b/seed/challenges/04-video-challenges/user-experience-design.json
new file mode 100644
index 0000000000..449b373895
--- /dev/null
+++ b/seed/challenges/04-video-challenges/user-experience-design.json
@@ -0,0 +1,18 @@
+{
+ "name": "User Experience Design",
+ "order": 20,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdd2",
+ "title": "Learn User Experience Design Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "User Experience Design"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/04-video-challenges/visual-design.json b/seed/challenges/04-video-challenges/visual-design.json
new file mode 100644
index 0000000000..54811ff1eb
--- /dev/null
+++ b/seed/challenges/04-video-challenges/visual-design.json
@@ -0,0 +1,18 @@
+{
+ "name": "Visual Design",
+ "order": 21,
+ "time": "0 hours",
+ "isComingSoon": true,
+ "challenges": [
+ {
+ "id": "bd7128d8c441eddfbeb5bdd1",
+ "title": "Learn Visual Design Challenges",
+ "description": [],
+ "challengeSeed": [],
+ "tests": [],
+ "type": "hike",
+ "challengeType": 6,
+ "nameEs": "Visual Design"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/server/datasources.local.js b/server/datasources.local.js
index c1881a0fef..94ef628b36 100644
--- a/server/datasources.local.js
+++ b/server/datasources.local.js
@@ -8,13 +8,10 @@ module.exports = {
},
mail: {
connector: 'mail',
- transports: [{
- type: 'smtp',
- service: 'Mandrill',
- auth: {
- user: secrets.mandrill.user,
- pass: secrets.mandrill.password
- }
- }]
+ transport: {
+ type: 'ses',
+ accessKeyId: process.env.SES_ID,
+ secretAccessKey: process.env.SES_SECRET
+ }
}
};
diff --git a/server/middlewares/migrate-completed-challenges.js b/server/middlewares/migrate-completed-challenges.js
index 9085b98f23..fa2952ddd1 100644
--- a/server/middlewares/migrate-completed-challenges.js
+++ b/server/middlewares/migrate-completed-challenges.js
@@ -85,7 +85,7 @@ function buildChallengeMap(userId, completedChallenges = [], User) {
}, {})
.flatMap(challengeMap => {
const updateData = {
- '$set': {
+ $set: {
challengeMap,
isChallengeMapMigrated: true
}
diff --git a/server/services/hikes.js b/server/services/hikes.js
index 5a99bb5b3b..a6117c8a1d 100644
--- a/server/services/hikes.js
+++ b/server/services/hikes.js
@@ -10,7 +10,10 @@ export default function hikesService(app) {
name: 'hikes',
read: (req, resource, { dashedName } = {}, config, cb) => {
const query = {
- where: { challengeType: '6' },
+ where: {
+ challengeType: '6',
+ isComingSoon: false
+ },
order: ['order ASC', 'suborder ASC' ]
};
diff --git a/server/utils/auth.js b/server/utils/auth.js
index 2dd6a4b90d..52afd45861 100644
--- a/server/utils/auth.js
+++ b/server/utils/auth.js
@@ -24,9 +24,9 @@ export function setProfileFromGithub(
},
{
id: githubId,
- 'avatar_url': picture,
+ avatar_url: picture,
email: githubEmail,
- 'created_at': joinedGithubOn,
+ created_at: joinedGithubOn,
blog: website,
location,
name
diff --git a/server/views/partials/navbar.jade b/server/views/partials/navbar.jade
index 5e527ddc8d..edbc4b72c0 100644
--- a/server/views/partials/navbar.jade
+++ b/server/views/partials/navbar.jade
@@ -8,15 +8,15 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height
.collapse.navbar-collapse
ul.nav.navbar-nav.navbar-right.hamburger-dropdown
li.hidden-xs
- a#nav-map-btn(href='#' onclick='return false') Map
+ a#nav-map-btn(href='/map' onclick='if (!(event.ctrlKey || event.metaKey)) {return false;}') Map
li.visible-xs
a(href='/map') Map
li.hidden-xs
- a#nav-chat-btn(href='#' onclick="return false") Chat
+ a#nav-chat-btn(href='//gitter.im/freecodecamp/freecodecamp' onclick="if (!(event.ctrlKey || event.metaKey)) {return false;}") Chat
li.visible-xs
a(href="//gitter.im/freecodecamp/freecodecamp" target="_blank") Chat
li.hidden-xs
- a#nav-wiki-btn(href='#' onclick='return false') Wiki
+ a#nav-wiki-btn(href='/wiki' onclick='if (!(event.ctrlKey || event.metaKey)) {return false;}') Wiki
li.visible-xs
a(href='/wiki', target='_blank') Wiki
li