-
- {
- isCertClaimed ?
- :
- null
- }
-
-
- );
- })
+ {
+ modernProjects.map(this.buildProjectForms)
+ }
+
+ Legacy Certificate Settings
+
+ {
+ legacyProjects.map(this.buildProjectForms)
}
);
diff --git a/common/app/routes/Settings/components/JSAlgoAndDSForm.jsx b/common/app/routes/Settings/components/JSAlgoAndDSForm.jsx
index 8cc19d2bd4..4b8558bf21 100644
--- a/common/app/routes/Settings/components/JSAlgoAndDSForm.jsx
+++ b/common/app/routes/Settings/components/JSAlgoAndDSForm.jsx
@@ -43,7 +43,7 @@ class JSAlgoAndDSForm extends PureComponent {
e.preventDefault();
const { username, superBlock, isCertClaimed } = this.props;
if (isCertClaimed) {
- return this.props.hardGoTo(`/c/${username}/${superBlock}`);
+ return this.props.hardGoTo(`/certificates/${username}/${superBlock}`);
}
return this.props.claimCert(superBlock);
}
diff --git a/common/app/routes/Settings/utils/buildUserProjectsMap.js b/common/app/routes/Settings/utils/buildUserProjectsMap.js
index 770099af38..5629a94939 100644
--- a/common/app/routes/Settings/utils/buildUserProjectsMap.js
+++ b/common/app/routes/Settings/utils/buildUserProjectsMap.js
@@ -1,19 +1,14 @@
-import { dasherize } from '../../../../../server/utils/index';
-
export const jsProjectSuperBlock = 'javascript-algorithms-and-data-structures';
export function buildUserProjectsMap(projectBlock, challengeMap) {
const {
- challengeNameIdMap,
challenges,
superBlock
} = projectBlock;
return {
[superBlock]: challenges.reduce((solutions, current) => {
- const dashedName = dasherize(current)
- .replace('java-script', 'javascript')
- .replace('metric-imperial', 'metricimperial');
- const completed = challengeMap[challengeNameIdMap[dashedName]];
+ const { id } = current;
+ const completed = challengeMap[id];
let solution = '';
if (superBlock === jsProjectSuperBlock) {
solution = {};
@@ -25,7 +20,7 @@ export function buildUserProjectsMap(projectBlock, challengeMap) {
}
return {
...solutions,
- [current]: solution
+ [current.title]: solution
};
}, {})
};
diff --git a/common/models/user.js b/common/models/user.js
index 8b1401a4b1..0e3386aaf7 100644
--- a/common/models/user.js
+++ b/common/models/user.js
@@ -9,7 +9,6 @@ import loopback from 'loopback';
import _ from 'lodash';
import { themes } from '../utils/themes';
-import { dasherize } from '../../server/utils';
import { saveUser, observeMethod } from '../../server/utils/rx.js';
import { blacklistedUsernames } from '../../server/utils/constants.js';
import { wrapHandledError } from '../../server/utils/create-handled-error.js';
@@ -43,28 +42,24 @@ function destroyAll(id, Model) {
}
function buildChallengeMapUpdate(challengeMap, project) {
+ const key = Object.keys(project)[0];
+ const solutions = project[key];
const currentChallengeMap = { ...challengeMap };
- const { nameToIdMap } = _.values(project)[0];
- const incomingUpdate = _.pickBy(
- _.omit(_.values(project)[0], [ 'id', 'nameToIdMap' ]),
- Boolean
+ const currentCompletedProjects = _.pick(
+ currentChallengeMap,
+ Object.keys(solutions)
);
- const currentCompletedProjects = _.pick(challengeMap, _.values(nameToIdMap));
const now = Date.now();
- const update = Object.keys(incomingUpdate).reduce((update, current) => {
- const dashedName = dasherize(current)
- .replace('java-script', 'javascript')
- .replace('metric-imperial', 'metricimperial');
- const currentId = nameToIdMap[dashedName];
+ const update = Object.keys(solutions).reduce((update, currentId) => {
if (
currentId in currentCompletedProjects &&
- currentCompletedProjects[currentId].solution !== incomingUpdate[current]
+ currentCompletedProjects[currentId].solution !== solutions[currentId]
) {
return {
...update,
[currentId]: {
...currentCompletedProjects[currentId],
- solution: incomingUpdate[current],
+ solution: solutions[currentId],
numOfAttempts: currentCompletedProjects[currentId].numOfAttempts + 1
}
};
@@ -74,7 +69,7 @@ function buildChallengeMapUpdate(challengeMap, project) {
...update,
[currentId]: {
id: currentId,
- solution: incomingUpdate[current],
+ solution: solutions[currentId],
challengeType: 3,
completedDate: now,
numOfAttempts: 1
diff --git a/common/utils/legacyProjectData.js b/common/utils/legacyProjectData.js
new file mode 100644
index 0000000000..0d7d77dd93
--- /dev/null
+++ b/common/utils/legacyProjectData.js
@@ -0,0 +1,88 @@
+const legacyFrontEndProjects = {
+ challenges: [
+ // build-a-personal-portfolio-webpage
+ 'bd7158d8c242eddfaeb5bd13',
+ // build-a-random-quote-machine
+ 'bd7158d8c442eddfaeb5bd13',
+ // build-a-pomodoro-clock
+ 'bd7158d8c442eddfaeb5bd0f',
+ // build-a-javascript-calculator
+ 'bd7158d8c442eddfaeb5bd17',
+ // show-the-local-weather
+ 'bd7158d8c442eddfaeb5bd10',
+ // use-the-twitchtv-json-api
+ 'bd7158d8c442eddfaeb5bd1f',
+ // stylize-stories-on-camper-news
+ 'bd7158d8c442eddfaeb5bd18',
+ // build-a-wikipedia-viewer
+ 'bd7158d8c442eddfaeb5bd19',
+ // build-a-tic-tac-toe-game
+ 'bd7158d8c442eedfaeb5bd1c',
+ // build-a-simon-game
+ 'bd7158d8c442eddfaeb5bd1c'
+ ],
+ title: 'Legacy Front End Projects',
+ superBlock: 'legacy-front-end'
+};
+
+const legacyBackEndProjects = {
+ challenges: [
+ // timestamp microservice
+ 'bd7158d8c443edefaeb5bdef',
+ // request-header-parser-microservice
+ 'bd7158d8c443edefaeb5bdff',
+ // url-shortener-microservice
+ 'bd7158d8c443edefaeb5bd0e',
+ // image-search-abstraction-layer
+ 'bd7158d8c443edefaeb5bdee',
+ // file-metadata-microservice
+ 'bd7158d8c443edefaeb5bd0f',
+ // build-a-voting-app
+ 'bd7158d8c443eddfaeb5bdef',
+ // build-a-nightlife-coordination-app
+ 'bd7158d8c443eddfaeb5bdff',
+ // chart-the-stock-market
+ 'bd7158d8c443eddfaeb5bd0e',
+ // manage-a-book-trading-club
+ 'bd7158d8c443eddfaeb5bd0f',
+ // build-a-pinterest-clone
+ 'bd7158d8c443eddfaeb5bdee'
+ ],
+ title: 'Legacy Back End Projects',
+ superBlock: 'legacy-back-end'
+};
+
+const legacyDataVisProjects = {
+ challenges: [
+ // build-a-markdown-previewer
+ 'bd7157d8c242eddfaeb5bd13',
+ // build-a-camper-leaderboard
+ 'bd7156d8c242eddfaeb5bd13',
+ // build-a-recipe-box
+ 'bd7155d8c242eddfaeb5bd13',
+ // build-the-game-of-life
+ 'bd7154d8c242eddfaeb5bd13',
+ // build-a-roguelike-dungeon-crawler-game
+ 'bd7153d8c242eddfaeb5bd13',
+ // visualize-data-with-a-bar-chart
+ 'bd7168d8c242eddfaeb5bd13',
+ // visualize-data-with-a-scatterplot-graph
+ 'bd7178d8c242eddfaeb5bd13',
+ // visualize-data-with-a-heat-map
+ 'bd7188d8c242eddfaeb5bd13',
+ // show-national-contiguity-with-a-force-directed-graph
+ 'bd7198d8c242eddfaeb5bd13',
+ // map-data-across-the-globe
+ 'bd7108d8c242eddfaeb5bd13'
+ ],
+ title: 'Legacy Data Visualization Projects',
+ superBlock: 'legacy-data-visualization'
+};
+
+const legacyProjects = [
+ legacyFrontEndProjects,
+ legacyBackEndProjects,
+ legacyDataVisProjects
+];
+
+export default legacyProjects;
diff --git a/seed/challenges/01-responsive-web-design/responsive-web-design-projects.json b/seed/challenges/01-responsive-web-design/responsive-web-design-projects.json
index e7f40bc06d..e8a983fac6 100644
--- a/seed/challenges/01-responsive-web-design/responsive-web-design-projects.json
+++ b/seed/challenges/01-responsive-web-design/responsive-web-design-projects.json
@@ -273,7 +273,7 @@
"translations": {}
},
{
- "id": "587d78b0367417b2b2512b06",
+ "id": "bd7158d8c242eddfaeb5bd13",
"title": "Build a Personal Portfolio Webpage",
"description": [
"Fulfill the user stories by getting all of the tests to pass. Use whichever libraries you need. Give it your own personal style.",
diff --git a/seed/challenges/08-coding-interview-questions-and-take-home-assignments/take-home-interview-projects.json b/seed/challenges/08-coding-interview-questions-and-take-home-assignments/take-home-interview-projects.json
index 3ba3cbe2cf..5dc7275e6c 100644
--- a/seed/challenges/08-coding-interview-questions-and-take-home-assignments/take-home-interview-projects.json
+++ b/seed/challenges/08-coding-interview-questions-and-take-home-assignments/take-home-interview-projects.json
@@ -101,7 +101,7 @@
},
{
"id": "bd7158d8c442eddfaeb5bd1f",
- "title": "Use the Twitch.tv JSON API",
+ "title": "Use the Twitch JSON API",
"description": [
"Objective: Build a CodePen.io app that is functionally similar to this: https://codepen.io/freeCodeCamp/full/Myvqmo/.",
"Fulfill the below user stories. Use whichever libraries or APIs you need. Give it your own personal style.",
@@ -123,7 +123,7 @@
"isRequired": false,
"translations": {
"es": {
- "title": "Usa el API JSON de Twitch.tv",
+ "title": "Usa el API JSON de Twitch",
"description": [
"Objetivo: Crea una aplicación con CodePen.io cuya funcionalidad sea similar a la de esta: https://codepen.io/freeCodeCamp/full/Myvqmo/.",
"Satisface las siguientes historias de usuario. Usa cualquier librería o APIs que necesites. Dale tu estilo personal.",
@@ -139,7 +139,7 @@
]
},
"ru": {
- "title": "Используйте Twitch.tv JSON API",
+ "title": "Используйте Twitch JSON API",
"description": [
"Задание: Создайте CodePen.io который успешно копирует вот этот: https://codepen.io/freeCodeCamp/full/Myvqmo/.",
"Правило #1: Не подсматривайте код приведенного на CodePen примера. Напишите его самостоятельно.",
@@ -682,6 +682,127 @@
"isRequired": false,
"titleEs": "Crea un clon de Pinterest"
},
+ {
+ "id": "bd7158d8c443eddfaeb5bdff",
+ "title": "Build a Nightlife Coordination App",
+ "description": [
+ "Objective: Build a full stack JavaScript app that is functionally similar to this: http://whatsgoinontonight.herokuapp.com/ and deploy it to Heroku.",
+ "Note that for each project, you should create a new GitHub repository and a new Heroku project. If you can't remember how to do this, revisit https://freecodecamp.com/challenges/get-set-for-our-dynamic-web-application-projects.",
+ "Here are the specific user stories you should implement for this project:",
+ "User Story: As an unauthenticated user, I can view all bars in my area.",
+ "User Story: As an authenticated user, I can add myself to a bar to indicate I am going there tonight.",
+ "User Story: As an authenticated user, I can remove myself from a bar if I no longer want to go there.",
+ "User Story: As an unauthenticated user, when I login I should not have to search again.",
+ "Hint: Try using the Yelp API to find venues in the cities your users search for. If you use Yelp's API, be sure to mention so in your app.",
+ "Once you've finished implementing these user stories, click the \"I've completed this challenge\" button and enter the URLs for both your GitHub repository and your live app running on Heroku.",
+ "You can get feedback on your project by sharing it with your friends on Facebook."
+ ],
+ "challengeSeed": [
+ "Gei7QfPmcMw"
+ ],
+ "tests": [],
+ "type": "basejump",
+ "challengeType": 4,
+ "isRequired": true,
+ "translations": {
+ "es": {
+ "description": [
+ "Objetivo: Construye una aplicación de pila completa (full stack) en JavaScript que funcione de forma similar al siguiente proyecto: http://whatsgoinontonight.herokuapp.com/ y despliégala en Heroku.",
+ "Ten en cuenta que para cada proyecto, debes crear un nuevo repositorio en GitHub y un nuevo proyecto en Heroku. Si no recuerdas cómo hacerlo, visita de nuevo https://freecodecamp.com/challenges/get-set-for-our-dynamic-web-application-projects.",
+ "Estas son las Historias de usuario que debes satisfacer para este Basejump:",
+ "Historia de usuario: Como un usuario no autenticado, puedo ver todos los bares en mi área.",
+ "Historia de usuario: Como un usuario autenticado, puedo agregarme a mí mismo a un bar para indicar que voy a estar allí esta noche.",
+ "Historia de usuario: Como un usuario autenticado, puedo removerme de un bar si ya no pienso ir allí.",
+ "Historia de usuario: Como un usuario no autenticado, cuando accedo a mi cuenta no debo tener la necesidad de buscar de nuevo.",
+ "Pista: Prueba utilizar el API de Yelp para encontrar lugares en las ciudades donde tus usuarios buscan. Si utilizas el API de Yelp, asegúrate de mencionarlo en tu aplicación.",
+ "Una vez hayas terminado de implementar estas historias de usuario, pulsa el botón de \"I've completed this challenge\" e incluye las URLs de tu repositorio GitHub y de tu aplicación corriendo en Heroku.",
+ "Puedes obtener retroalimentación acerca de tu proyecto de parte de tus compañeros campistas compartiéndolo en nuestro Cuarto de revisión de código. También puedes compartirlo en Twitter y en el campamento de tu ciudad (en Facebook)."
+ ]
+ }
+ }
+ },
+ {
+ "id": "bd7158d8c443eddfaeb5bd0e",
+ "title": "Chart the Stock Market",
+ "description": [
+ "Objective: Build a full stack JavaScript app that is functionally similar to this: http://watchstocks.herokuapp.com/ and deploy it to Heroku.",
+ "Note that for each project, you should create a new GitHub repository and a new Heroku project. If you can't remember how to do this, revisit https://freecodecamp.com/challenges/get-set-for-our-dynamic-web-application-projects.",
+ "Here are the specific user stories you should implement for this project:",
+ "User Story: I can view a graph displaying the recent trend lines for each added stock.",
+ "User Story: I can add new stocks by their symbol name.",
+ "User Story: I can remove stocks.",
+ "User Story: I can see changes in real-time when any other user adds or removes a stock. For this you will need to use Web Sockets.",
+ "Once you've finished implementing these user stories, click the \"I've completed this challenge\" button and enter the URLs for both your GitHub repository and your live app running on Heroku.",
+ "You can get feedback on your project by sharing it with your friends on Facebook."
+ ],
+ "challengeSeed": [
+ "CENs50cnRgM"
+ ],
+ "tests": [],
+ "type": "basejump",
+ "challengeType": 4,
+ "isRequired": true,
+ "translations": {
+ "es": {
+ "description": [
+ "Objetivo: Construye una aplicación de pila completa (full stack) en JavaScript que funcione de forma similar al siguiente proyecto: http://watchstocks.herokuapp.com/ y despliégalo en Heroku.",
+ "Ten en cuenta que para cada proyecto, debes crear un nuevo repositorio en GitHub y un nuevo proyecto en Heroku. Si no recuerdas cómo hacerlo, visita de nuevo https://freecodecamp.com/challenges/get-set-for-our-dynamic-web-application-projects.",
+ "Estas son las Historias de usuario que debes satisfacer para este Basejump:",
+ "Historia de usuario: Como usuario, puedo ver un gráfico que me muestre las líneas de tendencia recientes para cada acción agregada.",
+ "Historia de usuario: Como usuario, puedo agregar nuevas acciones por su símbolo.",
+ "Historia de usuario: Como usuario, puedo remover acciones.",
+ "Historia de usuario: Como usuario, puedo ver cambios en tiempo real cuando algún otro usuario agrega o remueve una acción. Puedes usar Web Sockets para hacer esto.",
+ "Una vez hayas terminado de implementar estas historias de usuario, pulsa el botón de \"I've completed this challenge\" e incluye las URLs de tu repositorio GitHub y de tu aplicación corriendo en Heroku.",
+ "Puedes obtener retroalimentación acerca de tu proyecto de parte de tus compañeros campistas compartiéndolo en nuestro Cuarto de revisión de código. También puedes compartirlo en Twitter y en el campamento de tu ciudad (en Facebook)."
+ ]
+ }
+ }
+ },
+ {
+ "id": "bd7158d8c443eddfaeb5bdef",
+ "title": "Build a Voting App",
+ "description": [
+ "Objective: Build a full stack JavaScript app that is functionally similar to this: https://fcc-voting-arthow4n.herokuapp.com/ and deploy it to Heroku.",
+ "Note that for each project, you should create a new GitHub repository and a new Heroku project. If you can't remember how to do this, revisit https://freecodecamp.com/challenges/get-set-for-our-dynamic-web-application-projects.",
+ "Here are the specific user stories you should implement for this project:",
+ "User Story: As an authenticated user, I can keep my polls and come back later to access them.",
+ "User Story: As an authenticated user, I can share my polls with my friends.",
+ "User Story: As an authenticated user, I can see the aggregate results of my polls.",
+ "User Story: As an authenticated user, I can delete polls that I decide I don't want anymore.",
+ "User Story: As an authenticated user, I can create a poll with any number of possible items.",
+ "User Story: As an unauthenticated or authenticated user, I can see and vote on everyone's polls.",
+ "User Story: As an unauthenticated or authenticated user, I can see the results of polls in chart form. (This could be implemented using Chart.js or Google Charts.)",
+ "User Story: As an authenticated user, if I don't like the options on a poll, I can create a new option.",
+ "Once you've finished implementing these user stories, click the \"I've completed this challenge\" button and enter the URLs for both your GitHub repository and your live app running on Heroku.",
+ "You can get feedback on your project by sharing it with your friends on Facebook."
+ ],
+ "challengeSeed": [
+ "JBKnbY_fdg4"
+ ],
+ "tests": [],
+ "type": "basejump",
+ "challengeType": 4,
+ "isRequired": true,
+ "translations": {
+ "es": {
+ "description": [
+ "Objetivo: Construye una aplicación de pila completa (full stack) en JavaScript que funcione de forma similar al siguiente proyecto: https://fcc-voting-arthow4n.herokuapp.com/ y despliégala en Heroku.",
+ "Ten en cuenta que para cada proyecto, debes crear un nuevo repositorio en GitHub y un nuevo proyecto en Heroku. Si no recuerdas cómo hacerlo, visita de nuevo https://freecodecamp.com/challenges/get-set-for-our-dynamic-web-application-projects.",
+ "Estas son las Historias de usuario que debes satisfacer para este proyecto:",
+ "Historia de usuario: Como un usuario autenticado, puedo guardar mis votaciones y acceder a ellas posteriormente.",
+ "Historia de usuario: Como un usuario autenticado, puedo compartir mis votaciones con mis amigos.",
+ "Historia de usuario: Como un usuario autenticado, puedo ver los resultados agregados de mis votaciones.",
+ "Historia de usuario: Como un usuario autenticado, puedo eliminar votaciones que ya no quiero tener guardadas.",
+ "Historia de usuario: Como un usuario autenticado, puedo crear una votación con cualquier número de opciones.",
+ "Historia de usuario: Como un usuario autenticado o no autenticado, puedo ver y votar en las votaciones de otros.",
+ "Historia de usuario: Como un usuario autenticado o no autenticado, puedo ver los resultados de las votaciones por medio de un gráfico. (Esto podría implementarse utilizando Chart.js o Google Charts.)",
+ "Historia de usuario: Como un usuario autenticado, si no me gustan las opciones en una votación, puedo crear una nueva opción.",
+ "Una vez hayas terminado de implementar estas historias de usuario, pulsa el botón de \"I've completed this challenge\" e incluye las URLs de tu repositorio GitHub y de tu aplicación corriendo en Heroku.",
+ "Puedes obtener retroalimentación acerca de tu proyecto de parte de tus compañeros campistas compartiéndolo en nuestro Cuarto de revisión de código. También puedes compartirlo en Twitter y en el campamento de tu ciudad (en Facebook)."
+ ]
+ }
+ }
+ },
{
"id": "5a4b7fcdb66f799f199e11db",
"title": "Build a Pong Game",
diff --git a/seed/challenges/09-certificates/legacy-back-end-certificate.json b/seed/challenges/09-certificates/legacy-back-end-certificate.json
new file mode 100644
index 0000000000..8c09367c6c
--- /dev/null
+++ b/seed/challenges/09-certificates/legacy-back-end-certificate.json
@@ -0,0 +1,57 @@
+{
+ "name": "Legacy Back End Certificate",
+ "order": 1,
+ "isPrivate": true,
+ "challenges": [
+ {
+ "id": "660add10cb82ac38a17513be",
+ "title": "Legacy Back End Certificate",
+ "challengeType": 7,
+ "description": [],
+ "challengeSeed": [],
+ "isPrivate": true,
+ "tests": [
+ {
+ "id": "bd7158d8c443edefaeb5bdef",
+ "title": "Timestamp Microservice"
+ },
+ {
+ "id": "bd7158d8c443edefaeb5bdff",
+ "title": "Request Header Parser Microservice"
+ },
+ {
+ "id": "bd7158d8c443edefaeb5bd0e",
+ "title": "URL Shortener Microservice"
+ },
+ {
+ "id": "bd7158d8c443edefaeb5bdee",
+ "title": "Image Search Abstraction Layer"
+ },
+ {
+ "id": "bd7158d8c443edefaeb5bd0f",
+ "title": "File Metadata Microservice"
+ },
+ {
+ "id": "bd7158d8c443eddfaeb5bdef",
+ "title": "Build a Voting App"
+ },
+ {
+ "id": "bd7158d8c443eddfaeb5bdff",
+ "title": "Build a Nightlife Coordination App"
+ },
+ {
+ "id": "bd7158d8c443eddfaeb5bd0e",
+ "title": "Chart the Stock Market"
+ },
+ {
+ "id": "bd7158d8c443eddfaeb5bd0f",
+ "title": "Manage a Book Trading Club"
+ },
+ {
+ "id": "bd7158d8c443eddfaeb5bdee",
+ "title": "Build a Pinterest Clone"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/09-certificates/legacy-data-visualization-certificate.json b/seed/challenges/09-certificates/legacy-data-visualization-certificate.json
new file mode 100644
index 0000000000..673c901d27
--- /dev/null
+++ b/seed/challenges/09-certificates/legacy-data-visualization-certificate.json
@@ -0,0 +1,57 @@
+{
+ "name": "Legacy Data Visualization Certificate",
+ "order": 1,
+ "isPrivate": true,
+ "challenges": [
+ {
+ "id": "561add10cb82ac39a17513bc",
+ "title": "Legacy Data Visualization Certificate",
+ "challengeType": 7,
+ "description": [],
+ "challengeSeed": [],
+ "isPrivate": true,
+ "tests": [
+ {
+ "id": "bd7157d8c242eddfaeb5bd13",
+ "title": "Build a Markdown Previewer"
+ },
+ {
+ "id": "bd7156d8c242eddfaeb5bd13",
+ "title": "Build a Camper Leaderboard"
+ },
+ {
+ "id": "bd7155d8c242eddfaeb5bd13",
+ "title": "Build a Recipe Box"
+ },
+ {
+ "id": "bd7154d8c242eddfaeb5bd13",
+ "title": "Build the Game of Life"
+ },
+ {
+ "id": "bd7153d8c242eddfaeb5bd13",
+ "title": "Build a Roguelike Dungeon Crawler Game"
+ },
+ {
+ "id": "bd7168d8c242eddfaeb5bd13",
+ "title": "Visualize Data with a Bar Chart"
+ },
+ {
+ "id": "bd7178d8c242eddfaeb5bd13",
+ "title": "Visualize Data with a Scatterplot Graph"
+ },
+ {
+ "id": "bd7188d8c242eddfaeb5bd13",
+ "title": "Visualize Data with a Heat Map"
+ },
+ {
+ "id": "bd7198d8c242eddfaeb5bd13",
+ "title": "Show National Contiguity with a Force Directed Graph"
+ },
+ {
+ "id": "bd7108d8c242eddfaeb5bd13",
+ "title": "Map Data Across the Globe"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/seed/challenges/09-certificates/legacy-front-end-certificate.json b/seed/challenges/09-certificates/legacy-front-end-certificate.json
new file mode 100644
index 0000000000..d1fd11ebad
--- /dev/null
+++ b/seed/challenges/09-certificates/legacy-front-end-certificate.json
@@ -0,0 +1,57 @@
+{
+ "name": "Legacy Front End Certificate",
+ "order": 1,
+ "isPrivate": true,
+ "challenges": [
+ {
+ "id": "561add10cb82ac38a17513be",
+ "title": "Legacy Front End Certificate",
+ "challengeType": 7,
+ "description": [],
+ "challengeSeed": [],
+ "isPrivate": true,
+ "tests": [
+ {
+ "id": "bd7158d8c242eddfaeb5bd13",
+ "title": "Build a Personal Portfolio Webpage"
+ },
+ {
+ "id": "bd7158d8c442eddfaeb5bd13",
+ "title": "Build a Random Quote Machine"
+ },
+ {
+ "id": "bd7158d8c442eddfaeb5bd0f",
+ "title": "Build a Pomodoro Clock"
+ },
+ {
+ "id": "bd7158d8c442eddfaeb5bd17",
+ "title": "Build a JavaScript Calculator"
+ },
+ {
+ "id": "bd7158d8c442eddfaeb5bd10",
+ "title": "Show the Local Weather"
+ },
+ {
+ "id": "bd7158d8c442eddfaeb5bd1f",
+ "title": "Use the Twitch JSON API"
+ },
+ {
+ "id": "bd7158d8c442eddfaeb5bd18",
+ "title": "Stylize Stories on Camper News"
+ },
+ {
+ "id": "bd7158d8c442eddfaeb5bd19",
+ "title": "Build a Wikipedia Viewer"
+ },
+ {
+ "id": "bd7158d8c442eedfaeb5bd1c",
+ "title": "Build a Tic Tac Toe Game"
+ },
+ {
+ "id": "bd7158d8c442eddfaeb5bd1c",
+ "title": "Build a Simon Game"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/server/boot/certificate.js b/server/boot/certificate.js
index ce4162f614..cd22c68838 100644
--- a/server/boot/certificate.js
+++ b/server/boot/certificate.js
@@ -1,5 +1,6 @@
import _ from 'lodash';
import loopback from 'loopback';
+import moment from 'moment-timezone';
import path from 'path';
import dedent from 'dedent';
import { Observable } from 'rx';
@@ -13,26 +14,23 @@ import {
import { observeQuery } from '../utils/rx';
import {
- // legacy
- frontEndChallengeId,
- backEndChallengeId,
- dataVisId,
+ legacyFrontEndChallengeId,
+ legacyBackEndChallengeId,
+ legacyDataVisId,
- // modern
respWebDesignId,
frontEndLibsId,
- dataVis2018Id,
jsAlgoDataStructId,
+ dataVis2018Id,
apisMicroservicesId,
infosecQaId
} from '../utils/constantStrings.json';
+import certTypes from '../utils/certTypes.json';
+import superBlockCertTypeMap from '../utils/superBlockCertTypeMap';
import {
completeCommitment$
} from '../utils/commit';
-import certTypes from '../utils/certTypes.json';
-import superBlockCertTypeMap from '../utils/superBlockCertTypeMap';
-
const log = debug('fcc:certification');
const renderCertifedEmail = loopback.template(path.join(
__dirname,
@@ -46,6 +44,48 @@ function isCertified(ids, challengeMap = {}) {
return _.every(ids, ({ id }) => _.has(challengeMap, id));
}
+const certIds = {
+ [certTypes.frontEnd]: legacyFrontEndChallengeId,
+ [certTypes.backEnd]: legacyBackEndChallengeId,
+ [certTypes.dataVis]: legacyDataVisId,
+ [certTypes.respWebDesign]: respWebDesignId,
+ [certTypes.frontEndLibs]: frontEndLibsId,
+ [certTypes.jsAlgoDataStruct]: jsAlgoDataStructId,
+ [certTypes.dataVis2018]: dataVis2018Id,
+ [certTypes.apisMicroservices]: apisMicroservicesId,
+ [certTypes.infosecQa]: infosecQaId
+};
+
+const certViews = {
+ [certTypes.frontEnd]: 'certificate/legacy/front-end.jade',
+ [certTypes.backEnd]: 'certificate/legacy/back-end.jade',
+ [certTypes.dataVis]: 'certificate/legacy/data-visualization.jade',
+ [certTypes.fullStack]: 'certificate/legacy/full-stack.jade',
+
+ [certTypes.respWebDesign]: 'certificate/responsive-web-design.jade',
+ [certTypes.frontEndLibs]: 'certificate/front-end-libraries.jade',
+ [certTypes.jsAlgoDataStruct]:
+ 'certificate/javascript-algorithms-and-data-structures.jade',
+ [certTypes.dataVis2018]: 'certificate/data-visualization.jade',
+ [certTypes.apisMicroservices]: 'certificate/apis-and-microservices.jade',
+ [certTypes.infosecQa]:
+ 'certificate/information-security-and-quality-assurance.jade'
+};
+
+const certText = {
+ [certTypes.frontEnd]: 'Legacy Front End certified',
+ [certTypes.backEnd]: 'Legacy Back End Certified',
+ [certTypes.dataVis]: 'Legacy Data Visualization Certified',
+ [certTypes.fullStack]: 'Legacy Full Stack Certified',
+ [certTypes.respWebDesign]: 'Responsive Web Design Certified',
+ [certTypes.frontEndLibs]: 'Front End Libraries Certified',
+ [certTypes.jsAlgoDataStruct]:
+ 'JavaScript Algorithms and Data Structures Certified',
+ [certTypes.dataVis2018]: 'Data Visualization Certified',
+ [certTypes.apisMicroservices]: 'APIs and Microservices Certified',
+ [certTypes.infosecQa]: 'Information Security and Quality Assurance Certified'
+};
+
function getIdsForCert$(id, Challenge) {
return observeQuery(
Challenge,
@@ -117,13 +157,24 @@ function sendCertifiedEmail(
export default function certificate(app) {
const router = app.loopback.Router();
- const { Email, Challenge } = app.models;
+ const { Email, Challenge, User } = app.models;
+
+ function findUserByUsername$(username, fields) {
+ return observeQuery(
+ User,
+ 'findOne',
+ {
+ where: { username },
+ fields
+ }
+ );
+ }
const certTypeIds = {
// legacy
- [certTypes.frontEnd]: getIdsForCert$(frontEndChallengeId, Challenge),
- [certTypes.backEnd]: getIdsForCert$(backEndChallengeId, Challenge),
- [certTypes.dataVis]: getIdsForCert$(dataVisId, Challenge),
+ [certTypes.frontEnd]: getIdsForCert$(legacyFrontEndChallengeId, Challenge),
+ [certTypes.backEnd]: getIdsForCert$(legacyBackEndChallengeId, Challenge),
+ [certTypes.dataVis]: getIdsForCert$(legacyDataVisId, Challenge),
// modern
[certTypes.respWebDesign]: getIdsForCert$(respWebDesignId, Challenge),
@@ -145,6 +196,10 @@ export default function certificate(app) {
ifNoSuperBlock404,
verifyCert
);
+ router.get(
+ '/certificates/:username/:cert',
+ showCert
+ );
app.use(router);
@@ -172,16 +227,12 @@ export default function certificate(app) {
function verifyCert(req, res, next) {
const { body: { superBlock }, user } = req;
-
+ log(superBlock);
let certType = superBlockCertTypeMap[superBlock];
log(certType);
- if (certType === 'isDataVisCert') {
- certType = 'is2018DataVisCert';
- log(certType);
- }
return user.getChallengeMap$()
- .flatMap(() => certTypeIds[certType])
- .flatMap(challenge => {
+ .flatMap(() => certTypeIds[certType])
+ .flatMap(challenge => {
const {
id,
tests,
@@ -251,4 +302,101 @@ export default function certificate(app) {
}
return res.status(404).end();
}
+
+ function showCert(req, res, next) {
+ let { username, cert } = req.params;
+ username = username.toLowerCase();
+ const certType = superBlockCertTypeMap[cert];
+ const certId = certIds[certType];
+ return findUserByUsername$(
+ username,
+ {
+ isCheater: true,
+ isLocked: true,
+ isFrontEndCert: true,
+ isBackEndCert: true,
+ isFullStackCert: true,
+ isRespWebDesignCert: true,
+ isFrontEndLibsCert: true,
+ isJsAlgoDataStructCert: true,
+ isDataVisCert: true,
+ is2018DataVisCert: true,
+ isApisMicroservicesCert: true,
+ isInfosecQaCert: true,
+ isHonest: true,
+ username: true,
+ name: true,
+ challengeMap: true
+ }
+ )
+ .subscribe(
+ user => {
+ const profile = `/${user.username}`;
+ if (!user) {
+ req.flash(
+ 'danger',
+ `We couldn't find a user with the username ${username}`
+ );
+ return res.redirect('/');
+ }
+
+ if (!user.name) {
+ req.flash(
+ 'danger',
+ dedent`
+ This user needs to add their name to their account
+ in order for others to be able to view their certificate.
+ `
+ );
+ return res.redirect(profile);
+ }
+
+ if (user.isCheater) {
+ return res.redirect(`/${user.username}`);
+ }
+
+ if (user.isLocked) {
+ req.flash(
+ 'danger',
+ dedent`
+ ${username} has chosen to make their profile
+ private. They will need to make their profile public
+ in order for others to be able to view their certificate.
+ `
+ );
+ return res.redirect('/');
+ }
+
+ if (!user.isHonest) {
+ req.flash(
+ 'danger',
+ dedent`
+ ${username} has not yet agreed to our Academic Honesty Pledge.
+ `
+ );
+ return res.redirect(profile);
+ }
+
+ if (user[certType]) {
+ const { challengeMap = {} } = user;
+ const { completedDate = new Date() } = challengeMap[certId] || {};
+
+ return res.render(
+ certViews[certType],
+ {
+ username: user.username,
+ date: moment(new Date(completedDate)).format('MMMM D, YYYY'),
+ name: user.name
+ }
+ );
+ }
+ req.flash(
+ 'danger',
+ `Looks like user ${username} is not ${certText[certType]}`
+ );
+ return res.redirect(profile);
+ },
+ next
+ );
+ }
}
diff --git a/server/boot/user.js b/server/boot/user.js
index add4f91cb5..1e901c31a7 100644
--- a/server/boot/user.js
+++ b/server/boot/user.js
@@ -1,88 +1,22 @@
import dedent from 'dedent';
-import moment from 'moment-timezone';
import debugFactory from 'debug';
import { curry } from 'lodash';
-import {
- frontEndChallengeId,
- backEndChallengeId,
- respWebDesignId,
- frontEndLibsId,
- jsAlgoDataStructId,
- dataVisId,
- dataVis2018Id,
- apisMicroservicesId,
- infosecQaId
-} from '../utils/constantStrings.json';
-import certTypes from '../utils/certTypes.json';
-import superBlockCertTypeMap from '../utils/superBlockCertTypeMap';
import {
ifNoUser401,
ifNoUserRedirectTo,
ifNotVerifiedRedirectToSettings
} from '../utils/middleware';
-import { observeQuery } from '../utils/rx';
const debug = debugFactory('fcc:boot:user');
const sendNonUserToMap = ifNoUserRedirectTo('/map');
const sendNonUserToMapWithMessage = curry(ifNoUserRedirectTo, 2)('/map');
-const certIds = {
- [certTypes.frontEnd]: frontEndChallengeId,
- [certTypes.backEnd]: backEndChallengeId,
- [certTypes.respWebDesign]: respWebDesignId,
- [certTypes.frontEndLibs]: frontEndLibsId,
- [certTypes.jsAlgoDataStruct]: jsAlgoDataStructId,
- [certTypes.dataVis]: dataVisId,
- [certTypes.dataVis2018]: dataVis2018Id,
- [certTypes.apisMicroservices]: apisMicroservicesId,
- [certTypes.infosecQa]: infosecQaId
-};
-
-const certViews = {
- [certTypes.frontEnd]: 'certificate/front-end.jade',
- [certTypes.backEnd]: 'certificate/back-end.jade',
- [certTypes.fullStack]: 'certificate/full-stack.jade',
- [certTypes.respWebDesign]: 'certificate/responsive-web-design.jade',
- [certTypes.frontEndLibs]: 'certificate/front-end-libraries.jade',
- [certTypes.jsAlgoDataStruct]:
- 'certificate/javascript-algorithms-and-data-structures.jade',
- [certTypes.dataVis]: 'certificate/data-visualization.jade',
- [certTypes.dataVis2018]: 'certificate/data-visualization-2018.jade',
- [certTypes.apisMicroservices]: 'certificate/apis-and-microservices.jade',
- [certTypes.infosecQa]:
- 'certificate/information-security-and-quality-assurance.jade'
-};
-
-const certText = {
- [certTypes.frontEnd]: 'Front End certified',
- [certTypes.backEnd]: 'Back End Certified',
- [certTypes.fullStack]: 'Full Stack Certified',
- [certTypes.respWebDesign]: 'Responsive Web Design Certified',
- [certTypes.frontEndLibs]: 'Front End Libraries Certified',
- [certTypes.jsAlgoDataStruct]:
- 'JavaScript Algorithms and Data Structures Certified',
- [certTypes.dataVis]: 'Data Visualization Certified',
- [certTypes.dataVis2018]: 'Data Visualization Certified',
- [certTypes.apisMicroservices]: 'APIs and Microservices Certified',
- [certTypes.infosecQa]: 'Information Security and Quality Assurance Certified'
-};
module.exports = function(app) {
const router = app.loopback.Router();
const api = app.loopback.Router();
const { Email, User } = app.models;
- function findUserByUsername$(username, fields) {
- return observeQuery(
- User,
- 'findOne',
- {
- where: { username },
- fields
- }
- );
- }
-
api.post(
'/account/delete',
ifNoUser401,
@@ -105,11 +39,6 @@ module.exports = function(app) {
);
// Ensure these are the last routes!
- api.get(
- '/c/:username/:cert',
- showCert
- );
-
router.get(
'/user/:username/report-user/',
sendNonUserToMapWithMessage('You must be signed in to report a user'),
@@ -185,100 +114,6 @@ module.exports = function(app) {
});
}
- function showCert(req, res, next) {
- let { username, cert } = req.params;
- username = username.toLowerCase();
- const certType = superBlockCertTypeMap[cert];
- const certId = certIds[certType];
- return findUserByUsername$(username, {
- isCheater: true,
- isLocked: true,
- isFrontEndCert: true,
- isBackEndCert: true,
- isFullStackCert: true,
- isRespWebDesignCert: true,
- isFrontEndLibsCert: true,
- isJsAlgoDataStructCert: true,
- isDataVisCert: true,
- is2018DataVisCert: true,
- isApisMicroservicesCert: true,
- isInfosecQaCert: true,
- isHonest: true,
- username: true,
- name: true,
- challengeMap: true
- })
- .subscribe(
- user => {
- const profile = `/${user.username}`;
- if (!user) {
- req.flash(
- 'danger',
- `We couldn't find a user with the username ${username}`
- );
- return res.redirect('/');
- }
-
- if (!user.name) {
- req.flash(
- 'danger',
- dedent`
- This user needs to add their name to their account
- in order for others to be able to view their certificate.
- `
- );
- return res.redirect(profile);
- }
-
- if (user.isCheater) {
- return res.redirect(`/${user.username}`);
- }
-
- if (user.isLocked) {
- req.flash(
- 'danger',
- dedent`
- ${username} has chosen to make their profile
- private. They will need to make their profile public
- in order for others to be able to view their certificate.
- `
- );
- return res.redirect('/');
- }
-
- if (!user.isHonest) {
- req.flash(
- 'danger',
- dedent`
- ${username} has not yet agreed to our Academic Honesty Pledge.
- `
- );
- return res.redirect(profile);
- }
-
- if (user[certType]) {
- const { challengeMap = {} } = user;
- const { completedDate = new Date() } = challengeMap[certId] || {};
-
- return res.render(
- certViews[certType],
- {
- username: user.username,
- date: moment(new Date(completedDate)).format('MMMM D, YYYY'),
- name: user.name
- }
- );
- }
- req.flash(
- 'danger',
- `Looks like user ${username} is not ${certText[certType]}`
- );
- return res.redirect(profile);
- },
- next
- );
- }
-
function postDeleteAccount(req, res, next) {
User.destroyById(req.user.id, function(err) {
if (err) { return next(err); }
diff --git a/server/utils/constantStrings.json b/server/utils/constantStrings.json
index 78d72a5a01..74303fe654 100644
--- a/server/utils/constantStrings.json
+++ b/server/utils/constantStrings.json
@@ -1,9 +1,9 @@
{
"gitHubUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1521.3 Safari/537.36",
- "frontEndChallengeId": "561add10cb82ac38a17513be",
- "backEndChallengeId": "660add10cb82ac38a17513be",
- "dataVisId": "561add10cb82ac39a17513bc",
+ "legacyFrontEndChallengeId": "561add10cb82ac38a17513be",
+ "legacyBackEndChallengeId": "660add10cb82ac38a17513be",
+ "legacyDataVisId": "561add10cb82ac39a17513bc",
"respWebDesignId": "561add10cb82ac38a17513bc",
"frontEndLibsId": "561acd10cb82ac38a17513bc",
diff --git a/server/utils/publicUserProps.js b/server/utils/publicUserProps.js
index 964cad0ab9..36a153234d 100644
--- a/server/utils/publicUserProps.js
+++ b/server/utils/publicUserProps.js
@@ -15,6 +15,7 @@ export const publicUserProps = [
'isApisMicroservicesCert',
'isBackEndCert',
'isCheater',
+ 'is2018DataVisCert',
'isDataVisCert',
'isFrontEndCert',
'isFullStackCert',
diff --git a/server/utils/superBlockCertTypeMap.js b/server/utils/superBlockCertTypeMap.js
index 49c91e1aed..9d62df67be 100644
--- a/server/utils/superBlockCertTypeMap.js
+++ b/server/utils/superBlockCertTypeMap.js
@@ -2,16 +2,16 @@ import certTypes from './certTypes.json';
const superBlockCertTypeMap = {
// legacy
- 'front-end': certTypes.frontEnd,
- 'back-end': certTypes.backEnd,
- 'data-visualization': certTypes.dataVis,
- 'full-stack': certTypes.fullStack,
+ 'legacy-front-end': certTypes.frontEnd,
+ 'legacy-back-end': certTypes.backEnd,
+ 'legacy-data-visualization': certTypes.dataVis,
+ 'legacy-full-stack': certTypes.fullStack,
// modern
'responsive-web-design': certTypes.respWebDesign,
'javascript-algorithms-and-data-structures': certTypes.jsAlgoDataStruct,
'front-end-libraries': certTypes.frontEndLibs,
- 'data-visualization-2018': certTypes.dataVis2018,
+ 'data-visualization': certTypes.dataVis2018,
'apis-and-microservices': certTypes.apisMicroservices,
'information-security-and-quality-assurance': certTypes.infosecQa
};
diff --git a/server/views/certificate/advanced-front-end.jade b/server/views/certificate/advanced-front-end.jade
index 78f781f4b4..e25d05cbc7 100644
--- a/server/views/certificate/advanced-front-end.jade
+++ b/server/views/certificate/advanced-front-end.jade
@@ -29,4 +29,4 @@ include styles
strong Quincy Larson
p Executive Director, freeCodeCamp.org
.row
- p.verify Verify this certificate at: https://freecodecamp.org/c/#{username}/advanced-front-end-certification
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/advanced-front-end
\ No newline at end of file
diff --git a/server/views/certificate/apis-and-microservices.jade b/server/views/certificate/apis-and-microservices.jade
index 6a37a5722a..5f5793565f 100644
--- a/server/views/certificate/apis-and-microservices.jade
+++ b/server/views/certificate/apis-and-microservices.jade
@@ -29,4 +29,4 @@ include styles
strong Quincy Larson
p Executive Director, freeCodeCamp.org
.row
- p.verify Verify this certificate at: https://freecodecamp.org/c/#{username}/apis-and-microservices-certification
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/apis-and-microservices
diff --git a/server/views/certificate/data-visualization.jade b/server/views/certificate/data-visualization.jade
index 0782d408aa..e4c34c3543 100644
--- a/server/views/certificate/data-visualization.jade
+++ b/server/views/certificate/data-visualization.jade
@@ -29,4 +29,4 @@ include styles
strong Quincy Larson
p Executive Director, freeCodeCamp.org
.row
- p.verify Verify this certificate at: https://freecodecamp.org/c/#{username}/data-visualization-certification
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/data-visualization
diff --git a/server/views/certificate/front-end-libraries.jade b/server/views/certificate/front-end-libraries.jade
index f5d4206fd2..1108949a40 100644
--- a/server/views/certificate/front-end-libraries.jade
+++ b/server/views/certificate/front-end-libraries.jade
@@ -29,4 +29,4 @@ include styles
strong Quincy Larson
p Executive Director, freeCodeCamp.org
.row
- p.verify Verify this certificate at: https://freecodecamp.org/c/#{username}/front-end-libraries-certification
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/front-end-libraries
diff --git a/server/views/certificate/information-security-and-quality-assurance.jade b/server/views/certificate/information-security-and-quality-assurance.jade
index 0a71cf3b97..a6a7ecbd62 100644
--- a/server/views/certificate/information-security-and-quality-assurance.jade
+++ b/server/views/certificate/information-security-and-quality-assurance.jade
@@ -29,4 +29,4 @@ include styles
strong Quincy Larson
p Executive Director, freeCodeCamp.org
.row
- p.verify Verify this certificate at: https://freecodecamp.org/c/#{username}/information-security-and-quality-assurance-certification
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/information-security-and-quality-assurance
diff --git a/server/views/certificate/javascript-algorithms-and-data-structures.jade b/server/views/certificate/javascript-algorithms-and-data-structures.jade
index ebe72a1f44..0033d50e2d 100644
--- a/server/views/certificate/javascript-algorithms-and-data-structures.jade
+++ b/server/views/certificate/javascript-algorithms-and-data-structures.jade
@@ -29,4 +29,4 @@ include styles
strong Quincy Larson
p Executive Director, freeCodeCamp.org
.row
- p.verify Verify this certificate at: https://freecodecamp.org/c/#{username}/javascript-algorithms-and-data-structures-certification
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/javascript-algorithms-and-data-structures
diff --git a/server/views/certificate/back-end.jade b/server/views/certificate/legacy/back-end.jade
similarity index 94%
rename from server/views/certificate/back-end.jade
rename to server/views/certificate/legacy/back-end.jade
index e94d39a4ad..c2cce165da 100644
--- a/server/views/certificate/back-end.jade
+++ b/server/views/certificate/legacy/back-end.jade
@@ -1,6 +1,6 @@
meta(name='viewport', content='width=device-width, initial-scale=1')
link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css')
-include styles
+include ../styles
.certificate-wrapper.container
.row
@@ -29,4 +29,4 @@ include styles
strong Quincy Larson
p Executive Director, freeCodeCamp.org
.row
- p.verify Verify this certificate at: https://freecodecamp.org/c/#{username}/back-end-certification
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/legacy-back-end
diff --git a/server/views/certificate/legacy/data-visualization.jade b/server/views/certificate/legacy/data-visualization.jade
new file mode 100644
index 0000000000..bf9537fca4
--- /dev/null
+++ b/server/views/certificate/legacy/data-visualization.jade
@@ -0,0 +1,32 @@
+meta(name='viewport', content='width=device-width, initial-scale=1')
+link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css')
+include ../styles
+
+.certificate-wrapper.container
+ .row
+ header
+ .col-md-5.col-sm-12
+ .logo
+ img(class='img-responsive', src='https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg', alt="freeCodeCamp's Logo")
+ .col-md-7.col-sm-12
+ .issue-date Issued
+ strong #{date}
+
+ section.information
+ .information-container
+ h3 This certifies that
+ h1
+ strong= name
+ h3 has successfully completed freeCodeCamp's
+ h1
+ strong Data Visualization Projects
+ h4 1 of 3 legacy freeCodeCamp certificates, representing approximately 400 hours of coursework
+
+ footer
+ .row.signatures
+ img(class='img-responsive', src='https://i.imgur.com/OJFVJKg.png', alt="Quincy Larson's Signature")
+ p
+ strong Quincy Larson
+ p Executive Director, freeCodeCamp.org
+ .row
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/legacy-data-visualization
diff --git a/server/views/certificate/front-end.jade b/server/views/certificate/legacy/front-end.jade
similarity index 94%
rename from server/views/certificate/front-end.jade
rename to server/views/certificate/legacy/front-end.jade
index 1179721bb0..56cd262af4 100644
--- a/server/views/certificate/front-end.jade
+++ b/server/views/certificate/legacy/front-end.jade
@@ -1,6 +1,6 @@
meta(name='viewport', content='width=device-width, initial-scale=1')
link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css')
-include styles
+include ../styles
.certificate-wrapper.container
.row
@@ -29,4 +29,4 @@ include styles
strong Quincy Larson
p Executive Director, freeCodeCamp.org
.row
- p.verify Verify this certificate at: https://freecodecamp.org/c/#{username}/front-end-certification
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/legacy-front-end
diff --git a/server/views/certificate/full-stack.jade b/server/views/certificate/legacy/full-stack.jade
similarity index 80%
rename from server/views/certificate/full-stack.jade
rename to server/views/certificate/legacy/full-stack.jade
index 30782f0137..5fd4ab7920 100644
--- a/server/views/certificate/full-stack.jade
+++ b/server/views/certificate/legacy/full-stack.jade
@@ -1,6 +1,6 @@
meta(name='viewport', content='width=device-width, initial-scale=1')
link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css')
-include styles
+include ../styles
.certificate-wrapper.container
.row
@@ -19,8 +19,8 @@ include styles
strong= name
h3 has successfully completed freeCodeCamp's
h1
- strong Full Stack Development Projects
- h4 1 of 3 legacy freeCodeCamp certificates, representing approximately 400 hours of coursework
+ strong Legacy Full Stack Development Program
+ h4 All three of the legacy freeCodeCamp certificates, representing approximately 12000 hours of coursework
footer
.row.signatures
@@ -29,4 +29,4 @@ include styles
strong Quincy Larson
p Executive Director, freeCodeCamp.org
.row
- p.verify Verify this certificate at: https://freecodecamp.org/c/#{username}/full-stack-certification
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/legacy-full-stack
\ No newline at end of file
diff --git a/server/views/certificate/responsive-web-design.jade b/server/views/certificate/responsive-web-design.jade
index 2873106ba0..5030b879d4 100644
--- a/server/views/certificate/responsive-web-design.jade
+++ b/server/views/certificate/responsive-web-design.jade
@@ -29,4 +29,4 @@ include styles
strong Quincy Larson
p Executive Director, freeCodeCamp.org
.row
- p.verify Verify this certificate at: https://freecodecamp.org/c/#{username}/responsive-web-design-certification
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/responsive-web-design