-
- {
- isCertClaimed ?
- :
- null
- }
-
-
- );
- })
+ {
+ projects.map(this.buildProjectForms)
+ }
+
+ Legacy Certificate Settings
+
+ {
+ legacyProjects.map(this.buildProjectForms)
}
);
diff --git a/common/app/routes/Settings/utils/legacyProjectData.js b/common/app/routes/Settings/utils/legacyProjectData.js
new file mode 100644
index 0000000000..ed70bee2d7
--- /dev/null
+++ b/common/app/routes/Settings/utils/legacyProjectData.js
@@ -0,0 +1,95 @@
+const legacyFrontEndProjects = {
+ challengeNameIdMap: {
+ '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'
+ },
+ challenges: [
+ 'Build a Personal Portfolio Webpage',
+ 'Build a Random Quote Machine',
+ 'Build a Pomodoro Clock',
+ 'Build a JavaScript Calculator',
+ 'Show the Local Weather',
+ 'Use the Twitchtv JSON API',
+ 'Stylize Stories on Camper News',
+ 'Build a Wikipedia Viewer',
+ 'Build a Tic Tac Toe Game',
+ 'Build a Simon Game'
+ ],
+ projectBlockName: 'Legacy Front End Projects',
+ superBlock: 'legacy-front-end'
+};
+
+const legacyBackEndProjects = {
+ challengeNameIdMap: {
+ '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'
+ },
+ challenges: [
+ 'Timestamp Microservice',
+ 'Request Header Parser Microservice',
+ 'URL Shortener Microservice',
+ 'Image Search Abstraction Layer',
+ 'File Metadata Microservice',
+ 'Build a Voting App',
+ 'Build a Nightlife Coordination App',
+ 'Chart the Stock Market',
+ 'Manage a Book Trading Club',
+ 'Build a Pinterest Clone'
+ ],
+ projectBlockName: 'Legacy Back End Projects',
+ superBlock: 'legacy-back-end'
+};
+
+const legacyDataVisProjects = {
+ challengeNameIdMap: {
+ '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'
+ },
+ challenges: [
+ 'Build a Markdown Previewer',
+ 'Build a Camper Leaderboard',
+ 'Build a Recipe Box',
+ 'Build the Game of Life',
+ 'Build a Roguelike Dungeon Crawler Game',
+ 'Visualize Data with a Bar Chart',
+ 'Visualize Data with a Scatterplot Graph',
+ 'Visualize Data with a Heat Map',
+ 'Show National Contiguity with a Force Directed Graph',
+ 'Map Data Across the Globe'
+ ],
+ projectBlockName: 'Legacy Data Visualization Projects',
+ superBlock: 'legacy-data-visualization'
+};
+
+const legacyProjects = [
+ legacyFrontEndProjects,
+ legacyBackEndProjects,
+ legacyDataVisProjects
+];
+
+export default legacyProjects;
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..da0a9d1f2e
--- /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.tv 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..11243e2aa6 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(
+ '/c/: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..01a76199f1 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/c/#{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..8fcdd5e371 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/c/#{username}/apis-and-microservices
diff --git a/server/views/certificate/data-visualization.jade b/server/views/certificate/data-visualization.jade
index 0782d408aa..c0570958e1 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/c/#{username}/data-visualization
diff --git a/server/views/certificate/front-end-libraries.jade b/server/views/certificate/front-end-libraries.jade
index f5d4206fd2..62a4e99e7d 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/c/#{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..9215bc4902 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/c/#{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..2c821113e4 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/c/#{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 95%
rename from server/views/certificate/back-end.jade
rename to server/views/certificate/legacy/back-end.jade
index e94d39a4ad..56c3a29266 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/c/#{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..640bf815d8
--- /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/c/#{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..d68947129e 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/c/#{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..c8f4f233a4 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/c/#{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..cc4eb37871 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/c/#{username}/responsive-web-design
From 2fe66f9e1881eab23c9774facedbb68b51418e98 Mon Sep 17 00:00:00 2001
From: Stuart Taylor
Date: Tue, 27 Feb 2018 14:55:12 +0000
Subject: [PATCH 02/11] fix(show-url): Update profile show cert urls
---
common/app/routes/Profile/components/Certificates.jsx | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/common/app/routes/Profile/components/Certificates.jsx b/common/app/routes/Profile/components/Certificates.jsx
index 06d3ed6891..dc1f47c4f8 100644
--- a/common/app/routes/Profile/components/Certificates.jsx
+++ b/common/app/routes/Profile/components/Certificates.jsx
@@ -61,7 +61,7 @@ const mapStateToProps = createSelector(
{
show: is2018DataVisCert,
title: 'Data Visualization Certificate:',
- showURL: 'data-visualization-2018'
+ showURL: 'data-visualization'
},
{
show: isApisMicroservicesCert,
@@ -78,22 +78,22 @@ const mapStateToProps = createSelector(
{
show: isFullStackCert,
title: 'Full Stack Certificate:',
- showURL: 'full-stack'
+ showURL: 'legacy-full-stack'
},
{
show: isFrontEndCert,
title: 'Front End Certificate:',
- showURL: 'front-end'
+ showURL: 'legacy-front-end'
},
{
show: isBackEndCert,
title: 'Back End Certificate:',
- showURL: 'back-end'
+ showURL: 'legacy-back-end'
},
{
show: isDataVisCert,
title: 'Data Visualization Certificate:',
- showURL: 'data-visualization'
+ showURL: 'legacy-data-visualization'
}
]
})
From 7d9057dbdd49d0455fa971b8318e39ff906627f8 Mon Sep 17 00:00:00 2001
From: Stuart Taylor
Date: Mon, 5 Mar 2018 10:04:08 +0000
Subject: [PATCH 03/11] fix(DRY): Dedupe Cert-Settings submit
---
.../Settings/components/Cert-Settings.jsx | 85 ++++++-------------
1 file changed, 25 insertions(+), 60 deletions(-)
diff --git a/common/app/routes/Settings/components/Cert-Settings.jsx b/common/app/routes/Settings/components/Cert-Settings.jsx
index 40af0960e3..80afd6a2f3 100644
--- a/common/app/routes/Settings/components/Cert-Settings.jsx
+++ b/common/app/routes/Settings/components/Cert-Settings.jsx
@@ -1,6 +1,6 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
-import { values as _values, isString, findIndex } from 'lodash';
+import _ from 'lodash';
import { createSelector } from 'reselect';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
@@ -102,15 +102,12 @@ const propTypes = {
username: PropTypes.string
};
-const compareSuperBlockWith = id => p => p.superBlock === id;
-
class CertificationSettings extends PureComponent {
constructor(props) {
super(props);
this.buildProjectForms = this.buildProjectForms.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
- this.isProjectSectionCompleted = this.isProjectSectionCompleted.bind(this);
}
componentDidMount() {
@@ -169,9 +166,9 @@ class CertificationSettings extends PureComponent {
[current]: ''
}), {});
- const completedProjects = _values(userValues)
+ const completedProjects = _.values(userValues)
.filter(Boolean)
- .filter(isString)
+ .filter(_.isString)
// minus 1 to account for the id
.length - 1;
@@ -209,65 +206,33 @@ class CertificationSettings extends PureComponent {
);
}
- isProjectSectionCompleted(values) {
- const { id } = values;
- const { projects } = this.props;
- const whereSuperBlockIsId = compareSuperBlockWith(id);
-
- let pIndex = findIndex(projects, whereSuperBlockIsId);
- let projectChallenges = [];
-
- if (pIndex === -1) {
- // submitted projects might be in a legacy certificate
- pIndex = findIndex(legacyProjects, whereSuperBlockIsId);
- projectChallenges = legacyProjects[pIndex].challenges;
- if (pIndex === -1) {
- // the submitted projects do not belong to current/legacy certificates
- return this.props.createError(
- new Error(
- 'Submitted projects do not belong to either current or ' +
- 'legacy certificates'
- )
- );
- }
- } else {
- projectChallenges = projects[pIndex].challenges;
- }
- const valuesSaved = _values(this.props.userProjects[id])
- .filter(Boolean)
- .filter(isString);
- // minus 1 due to the form id being in values
- return (valuesSaved.length - 1) === projectChallenges.length;
- }
-
handleSubmit(values) {
const { id } = values;
- if (this.isProjectSectionCompleted(values)) {
+ const { projects } = this.props;
+ const allProjects = [ ...projects, ...legacyProjects ];
+ let project = _.find(allProjects, { superBlock: id });
+ if (!project) {
+ // the submitted projects do not belong to current/legacy certificates
+ return this.props.createError(
+ new Error(
+ 'Submitted projects do not belong to either current or ' +
+ 'legacy certificates'
+ )
+ );
+ }
+ const valuesSaved = _.values(this.props.userProjects[id])
+ .filter(Boolean)
+ .filter(_.isString);
+
+ // minus 1 due to the form id being in values
+ const isProjectSectionComplete =
+ (valuesSaved.length - 1) === project.challenges.length;
+
+ if (isProjectSectionComplete) {
return this.props.claimCert(id);
}
- const { projects } = this.props;
- const whereSuperBlockIsId = compareSuperBlockWith(id);
- let pIndex = findIndex(projects, whereSuperBlockIsId);
- let projectNameIdMap = {};
-
- if (pIndex === -1) {
- // submitted projects might be in a legacy certificate
- pIndex = findIndex(legacyProjects, whereSuperBlockIsId);
- projectNameIdMap = legacyProjects[pIndex].challengeNameIdMap;
- if (pIndex === -1) {
- // the submitted projects do not belong to current/legacy certificates
- return this.props.createError(
- new Error(
- 'Submitted projects do not belong to either current or ' +
- 'legacy certificates'
- )
- );
- }
- } else {
- projectNameIdMap = projects[pIndex].challengeNameIdMap;
- }
- values.nameToIdMap = projectNameIdMap;
+ values.nameToIdMap = project.challengeNameIdMap;
return this.props.updateUserBackend({
projects: {
[id]: values
From 15473616c975ba3dba19943bda68638dfaa8a76b Mon Sep 17 00:00:00 2001
From: Stuart Taylor
Date: Mon, 5 Mar 2018 10:34:53 +0000
Subject: [PATCH 04/11] chore(legacy): Add legacy projects
---
.../take-home-interview-projects.json | 183 ++++++++++++++++++
1 file changed, 183 insertions(+)
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..1a119f5c29 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
@@ -4,6 +4,68 @@
"time": "",
"helpRoom": "HelpFrontEnd",
"challenges": [
+ {
+ "id": "bd7158d8c242eddfaeb5bd13",
+ "title": "Build a Personal Portfolio Webpage",
+ "description": [
+ "Objective: Build a CodePen.io app that is functionally similar to this: https://codepen.io/FreeCodeCamp/full/YqLyXB/.",
+ "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 you need. Give it your own personal style.",
+ "Rule #3: You can use Bootstrap, or any other framework of your choice.",
+ "User Story: I can access all of the portfolio webpage's content just by scrolling.",
+ "User Story: I can click different buttons that will take me to the portfolio creator's different social media pages.",
+ "User Story: I can see thumbnail images of different projects the portfolio creator has built (if you haven't built any websites before, use placeholders.)",
+ "User Story: I navigate to different sections of the webpage by clicking buttons in the navigation.",
+ "Don't worry if you don't have anything to showcase on your portfolio yet - you will build several apps on the next few CodePen challenges, and can come back and update your portfolio later.",
+ "There are many great portfolio templates out there already. However, you should consider building your portfolio page as much as you can from the ground up. Using Bootstrap can help make this process much easier for you.",
+ "Remember to use Read-Search-Ask if you get stuck.",
+ "When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen. ",
+ "You can get feedback on your project by sharing it with your friends on Facebook."
+ ],
+ "challengeSeed": [
+ "V72o34gY4Lw"
+ ],
+ "tests": [],
+ "type": "zipline",
+ "isRequired": true,
+ "challengeType": 3,
+ "translations": {
+ "es": {
+ "title": "Construye una página web para tu portafolio",
+ "description": [
+ "Objetivo: Crea una aplicación con CodePen.io cuya funcionalidad sea similar a la de esta: https://codepen.io/FreeCodeCamp/full/QNmvEL/.",
+ "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 que necesites. Dale tu estilo personal.",
+ "Historia de usuario: Puedo acceder a todo el contenido de la página del portafolio con sólo desplazarme en la ventana.",
+ "Historia de usuario: Puedo pulsar diferentes botones que me llevarán a las páginas de las diferentes cuentas de redes sociales del creador del portafolio.",
+ "Historia de usuario: Puedo ver una imagenes en miniatura de los diferentes proyectos que el creador del portafolio ha construido (si no has construido ningún sitio web antes, usa marcadores de posición.)",
+ "Historia de usuario: Puedo navegar a las diferentes secciones de la página web pulsando botones de navegación.",
+ "No te preocupes si no tienes nada que mostrar en tu portafolio todavía - en los siguientes desafíos crearás varias aplicaciones en CodePen, así que puedes regresar luego para actualizar tu portafolio.",
+ "Hay varias plantillas buenas, pero para este desafío, tendrás que construir la página web de tu portafolio completamente por tu cuenta. Usar Bootstrap hará el trabajo mucho más fácil para ti.",
+ "Recuerda utilizar Leer-Buscar-Preguntar si te sientes atascado.",
+ "Cuando hayas terminado, pulsa el botón \"I've completed this challenge\" e incluye un link a tu CodePen. ",
+ "Puedes obtener retroalimentación sobre tu proyecto por parte de otros campistas, compartiéndolo en nuestra Sala de chat para revisión de código. También puedes compartirlo en Twitter y en el campamento de tu ciudad (en Facebook)."
+ ]
+ },
+ "ru": {
+ "title": "Создайте сайт-портфолио",
+ "description": [
+ "Задание: Создайте приложение CodePen.io которое функционально соответствует вот этому: https://codepen.io/FreeCodeCamp/full/QNmvEL/.",
+ "Правило #1: Не подсматривайте код приведенного на CodePen примера. Напишите его самостоятельно.",
+ "Правило #2: Реализуйте следующие пользовательские истории. Используйте любые библиотеки, которые потребуются. Оформите приложение в вашем собственном стиле.",
+ "Пользовательская история: Я могу получить доступ ко всей информации на странице просто прокрутив ее сверху вниз.",
+ "Пользовательская история: Я могу нажать на различные кнопки и перейти к социальным страницам владельца портфолио.",
+ "Пользовательская история: Я могу увидеть эскизы проектов созданных владельцем портфолио (используйте временную картинку если у вас пока нет собственных веб-страниц).",
+ "Пользовательская история: Я могу перемещаться к различным частям страницы нажимая на соответствующие навигационные кнопки.",
+ "Не переживайте если вам пока нечего показать в портфолио - вы создадите несколько веб приложений в следующих заданиях, а затем вернетесь и обновите портфолио.",
+ "В сети существует много шаблонов для портфолио, но в этом задании вам необходимо создать собственную уникальную страницу. Используя Bootstrap, сделать это будет намного проще.",
+ "Если что-то не получается, воспользуйтесь Read-Search-Ask.",
+ "Когда выполните задание кликните кнопку \"I've completed this challenge\" и добавьте ссылку на ваш CodePen.",
+ "Вы можете получить отзыв о вашем проекте от коллег, поделившись ссылкой на него в нашем чате для рассмотрения кода. Также вы можете поделиться ею через Twitter и на странице Free Code Camp вашего города на Facebook."
+ ]
+ }
+ }
+ },
{
"id": "bd7158d8c442eddfaeb5bd10",
"title": "Show the Local Weather",
@@ -682,6 +744,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",
From 630141167319b306c9882e95bbd26ac6edb2a018 Mon Sep 17 00:00:00 2001
From: Stuart Taylor
Date: Mon, 5 Mar 2018 14:15:30 +0000
Subject: [PATCH 05/11] fix(cert-settings): Uncouple Cert-Settings from
dashedName's
---
common/app/entities/index.js | 38 +++--
.../Settings/components/Cert-Settings.jsx | 70 +++++----
.../Settings/utils/buildUserProjectsMap.js | 11 +-
.../Settings/utils/legacyProjectData.js | 133 +++++++++---------
common/models/user.js | 24 ++--
.../take-home-interview-projects.json | 64 +--------
6 files changed, 148 insertions(+), 192 deletions(-)
diff --git a/common/app/entities/index.js b/common/app/entities/index.js
index bb8aeda1c1..534d831e2e 100644
--- a/common/app/entities/index.js
+++ b/common/app/entities/index.js
@@ -1,4 +1,4 @@
-import { findIndex, invert, pick, property, merge, union } from 'lodash';
+import { findIndex, property, merge, union } from 'lodash';
import uuid from 'uuid/v4';
import {
combineActions,
@@ -12,6 +12,7 @@ import { themes } from '../../utils/themes';
import { usernameSelector, types as app } from '../redux';
import { types as challenges } from '../routes/Challenges/redux';
import { types as map } from '../Map/redux';
+import legacyProjects from '../routes/Settings/utils/legacyProjectData';
export const ns = 'entities';
export const getNS = state => state[ns];
@@ -91,10 +92,6 @@ const defaultState = {
fullBlocks: []
};
-export function selectiveChallengeTitleSelector(state, dashedName) {
- return getNS(state).challenge[dashedName].title;
-}
-
export function portfolioSelector(state, props) {
const username = usernameSelector(state);
const { portfolio } = getNS(state).user[username];
@@ -103,27 +100,42 @@ export function portfolioSelector(state, props) {
}
export function projectsSelector(state) {
- const blocks = getNS(state).block;
- const challengeNameToIdMap = invert(challengeIdToNameMapSelector(state));
+ const {
+ block: blocks,
+ challenge: challengeMap
+ } = getNS(state);
+ const idToNameMap = challengeIdToNameMapSelector(state);
+ const legacyWithDashedNames = legacyProjects
+ .reduce((list, current) => ([
+ ...list,
+ {
+ ...current,
+ challenges: current.challenges.map(id => idToNameMap[id])
+ }
+ ]),
+ []
+ );
return Object.keys(blocks)
.filter(key =>
key.includes('projects') && !key.includes('coding-interview')
)
.map(key => blocks[key])
+ .concat(legacyWithDashedNames)
.map(({ title, challenges, superBlock }) => {
const projectChallengeDashNames = challenges
+ // challengeIdToName is not available on appMount
+ .filter(Boolean)
// remove any project intros
.filter(chal => !chal.includes('get-set-for'));
const projectChallenges = projectChallengeDashNames
- .map(dashedName => selectiveChallengeTitleSelector(state, dashedName));
+ .map(dashedName => {
+ const { id, title } = challengeMap[dashedName];
+ return { id, title, dashedName };
+ });
return {
projectBlockName: title,
superBlock,
- challenges: projectChallenges,
- challengeNameIdMap: pick(
- challengeNameToIdMap,
- projectChallengeDashNames
- )
+ challenges: projectChallenges
};
});
}
diff --git a/common/app/routes/Settings/components/Cert-Settings.jsx b/common/app/routes/Settings/components/Cert-Settings.jsx
index 80afd6a2f3..0002d616dd 100644
--- a/common/app/routes/Settings/components/Cert-Settings.jsx
+++ b/common/app/routes/Settings/components/Cert-Settings.jsx
@@ -22,7 +22,6 @@ import {
buildUserProjectsMap,
jsProjectSuperBlock
} from '../utils/buildUserProjectsMap';
-import legacyProjects from '../utils/legacyProjectData';
const mapStateToProps = createSelector(
userSelector,
@@ -43,8 +42,10 @@ const mapStateToProps = createSelector(
},
projects
) => ({
- projects,
- userProjects: projects.concat(legacyProjects)
+ allProjects: projects,
+ legacyProjects: projects.filter(p => p.superBlock.includes('legacy')),
+ modernProjects: projects.filter(p => !p.superBlock.includes('legacy')),
+ userProjects: projects
.map(block => buildUserProjectsMap(block, challengeMap))
.reduce((projects, current) => ({
...projects,
@@ -77,18 +78,28 @@ function mapDispatchToProps(dispatch) {
}, dispatch);
}
+const projectsTypes = PropTypes.arrayOf(
+ PropTypes.shape({
+ projectBlockName: PropTypes.string,
+ challenges: PropTypes.arrayOf(
+ PropTypes.shape({
+ dashedName: PropTypes.string,
+ id: PropTypes.string,
+ title: PropTypes.string
+ })
+ )
+ }),
+);
+
const propTypes = {
+ allProjects: projectsTypes,
blockNameIsCertMap: PropTypes.objectOf(PropTypes.bool),
claimCert: PropTypes.func.isRequired,
createError: PropTypes.func.isRequired,
fetchChallenges: PropTypes.func.isRequired,
hardGoTo: PropTypes.func.isRequired,
- projects: PropTypes.arrayOf(
- PropTypes.shape({
- projectBlockName: PropTypes.string,
- challenges: PropTypes.arrayOf(PropTypes.string)
- })
- ),
+ legacyProjects: projectsTypes,
+ modernProjects: projectsTypes,
superBlock: PropTypes.string,
updateUserBackend: PropTypes.func.isRequired,
userProjects: PropTypes.objectOf(
@@ -111,8 +122,8 @@ class CertificationSettings extends PureComponent {
}
componentDidMount() {
- const { projects } = this.props;
- if (!projects.length) {
+ const { modernProjects } = this.props;
+ if (!modernProjects.length) {
this.props.fetchChallenges();
}
}
@@ -130,10 +141,12 @@ class CertificationSettings extends PureComponent {
username
} = this.props;
const isCertClaimed = blockNameIsCertMap[projectBlockName];
+ const challengeTitles = challenges
+ .map(challenge => challenge.title || 'Unknown Challenge');
if (superBlock === jsProjectSuperBlock) {
return (
);
}
- const options = challenges
+ const options = challengeTitles
.reduce((options, current) => {
options.types[current] = 'url';
return options;
@@ -160,7 +173,7 @@ class CertificationSettings extends PureComponent {
userValues.id = superBlock;
}
- const initialValues = challenges
+ const initialValues = challengeTitles
.reduce((accu, current) => ({
...accu,
[current]: ''
@@ -172,14 +185,14 @@ class CertificationSettings extends PureComponent {
// minus 1 to account for the id
.length - 1;
- const fullForm = completedProjects === challenges.length;
+ const fullForm = completedProjects === challengeTitles.length;
return (
{ projectBlockName }
{
- projects.map(this.buildProjectForms)
+ modernProjects.map(this.buildProjectForms)
}
Legacy Certificate Settings
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/app/routes/Settings/utils/legacyProjectData.js b/common/app/routes/Settings/utils/legacyProjectData.js
index ed70bee2d7..0d7d77dd93 100644
--- a/common/app/routes/Settings/utils/legacyProjectData.js
+++ b/common/app/routes/Settings/utils/legacyProjectData.js
@@ -1,88 +1,81 @@
const legacyFrontEndProjects = {
- challengeNameIdMap: {
- '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'
- },
challenges: [
- 'Build a Personal Portfolio Webpage',
- 'Build a Random Quote Machine',
- 'Build a Pomodoro Clock',
- 'Build a JavaScript Calculator',
- 'Show the Local Weather',
- 'Use the Twitchtv JSON API',
- 'Stylize Stories on Camper News',
- 'Build a Wikipedia Viewer',
- 'Build a Tic Tac Toe Game',
- 'Build a Simon Game'
+ // 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'
],
- projectBlockName: 'Legacy Front End Projects',
+ title: 'Legacy Front End Projects',
superBlock: 'legacy-front-end'
};
const legacyBackEndProjects = {
- challengeNameIdMap: {
- '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'
- },
challenges: [
- 'Timestamp Microservice',
- 'Request Header Parser Microservice',
- 'URL Shortener Microservice',
- 'Image Search Abstraction Layer',
- 'File Metadata Microservice',
- 'Build a Voting App',
- 'Build a Nightlife Coordination App',
- 'Chart the Stock Market',
- 'Manage a Book Trading Club',
- 'Build a Pinterest Clone'
+ // 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'
],
- projectBlockName: 'Legacy Back End Projects',
+ title: 'Legacy Back End Projects',
superBlock: 'legacy-back-end'
};
const legacyDataVisProjects = {
- challengeNameIdMap: {
- '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'
- },
challenges: [
- 'Build a Markdown Previewer',
- 'Build a Camper Leaderboard',
- 'Build a Recipe Box',
- 'Build the Game of Life',
- 'Build a Roguelike Dungeon Crawler Game',
- 'Visualize Data with a Bar Chart',
- 'Visualize Data with a Scatterplot Graph',
- 'Visualize Data with a Heat Map',
- 'Show National Contiguity with a Force Directed Graph',
- 'Map Data Across the Globe'
+ // 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'
],
- projectBlockName: 'Legacy Data Visualization Projects',
+ title: 'Legacy Data Visualization Projects',
superBlock: 'legacy-data-visualization'
};
diff --git a/common/models/user.js b/common/models/user.js
index 8b1401a4b1..15e88b29c1 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,25 @@ function destroyAll(id, Model) {
}
function buildChallengeMapUpdate(challengeMap, project) {
+ const key = Object.keys(project)[0];
+ const solutions = project[key];
+ console.log(solutions);
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 +70,7 @@ function buildChallengeMapUpdate(challengeMap, project) {
...update,
[currentId]: {
id: currentId,
- solution: incomingUpdate[current],
+ solution: solutions[currentId],
challengeType: 3,
completedDate: now,
numOfAttempts: 1
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 1a119f5c29..e87a6a4a26 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
@@ -4,68 +4,6 @@
"time": "",
"helpRoom": "HelpFrontEnd",
"challenges": [
- {
- "id": "bd7158d8c242eddfaeb5bd13",
- "title": "Build a Personal Portfolio Webpage",
- "description": [
- "Objective: Build a CodePen.io app that is functionally similar to this: https://codepen.io/FreeCodeCamp/full/YqLyXB/.",
- "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 you need. Give it your own personal style.",
- "Rule #3: You can use Bootstrap, or any other framework of your choice.",
- "User Story: I can access all of the portfolio webpage's content just by scrolling.",
- "User Story: I can click different buttons that will take me to the portfolio creator's different social media pages.",
- "User Story: I can see thumbnail images of different projects the portfolio creator has built (if you haven't built any websites before, use placeholders.)",
- "User Story: I navigate to different sections of the webpage by clicking buttons in the navigation.",
- "Don't worry if you don't have anything to showcase on your portfolio yet - you will build several apps on the next few CodePen challenges, and can come back and update your portfolio later.",
- "There are many great portfolio templates out there already. However, you should consider building your portfolio page as much as you can from the ground up. Using Bootstrap can help make this process much easier for you.",
- "Remember to use Read-Search-Ask if you get stuck.",
- "When you are finished, click the \"I've completed this challenge\" button and include a link to your CodePen. ",
- "You can get feedback on your project by sharing it with your friends on Facebook."
- ],
- "challengeSeed": [
- "V72o34gY4Lw"
- ],
- "tests": [],
- "type": "zipline",
- "isRequired": true,
- "challengeType": 3,
- "translations": {
- "es": {
- "title": "Construye una página web para tu portafolio",
- "description": [
- "Objetivo: Crea una aplicación con CodePen.io cuya funcionalidad sea similar a la de esta: https://codepen.io/FreeCodeCamp/full/QNmvEL/.",
- "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 que necesites. Dale tu estilo personal.",
- "Historia de usuario: Puedo acceder a todo el contenido de la página del portafolio con sólo desplazarme en la ventana.",
- "Historia de usuario: Puedo pulsar diferentes botones que me llevarán a las páginas de las diferentes cuentas de redes sociales del creador del portafolio.",
- "Historia de usuario: Puedo ver una imagenes en miniatura de los diferentes proyectos que el creador del portafolio ha construido (si no has construido ningún sitio web antes, usa marcadores de posición.)",
- "Historia de usuario: Puedo navegar a las diferentes secciones de la página web pulsando botones de navegación.",
- "No te preocupes si no tienes nada que mostrar en tu portafolio todavía - en los siguientes desafíos crearás varias aplicaciones en CodePen, así que puedes regresar luego para actualizar tu portafolio.",
- "Hay varias plantillas buenas, pero para este desafío, tendrás que construir la página web de tu portafolio completamente por tu cuenta. Usar Bootstrap hará el trabajo mucho más fácil para ti.",
- "Recuerda utilizar Leer-Buscar-Preguntar si te sientes atascado.",
- "Cuando hayas terminado, pulsa el botón \"I've completed this challenge\" e incluye un link a tu CodePen. ",
- "Puedes obtener retroalimentación sobre tu proyecto por parte de otros campistas, compartiéndolo en nuestra Sala de chat para revisión de código. También puedes compartirlo en Twitter y en el campamento de tu ciudad (en Facebook)."
- ]
- },
- "ru": {
- "title": "Создайте сайт-портфолио",
- "description": [
- "Задание: Создайте приложение CodePen.io которое функционально соответствует вот этому: https://codepen.io/FreeCodeCamp/full/QNmvEL/.",
- "Правило #1: Не подсматривайте код приведенного на CodePen примера. Напишите его самостоятельно.",
- "Правило #2: Реализуйте следующие пользовательские истории. Используйте любые библиотеки, которые потребуются. Оформите приложение в вашем собственном стиле.",
- "Пользовательская история: Я могу получить доступ ко всей информации на странице просто прокрутив ее сверху вниз.",
- "Пользовательская история: Я могу нажать на различные кнопки и перейти к социальным страницам владельца портфолио.",
- "Пользовательская история: Я могу увидеть эскизы проектов созданных владельцем портфолио (используйте временную картинку если у вас пока нет собственных веб-страниц).",
- "Пользовательская история: Я могу перемещаться к различным частям страницы нажимая на соответствующие навигационные кнопки.",
- "Не переживайте если вам пока нечего показать в портфолио - вы создадите несколько веб приложений в следующих заданиях, а затем вернетесь и обновите портфолио.",
- "В сети существует много шаблонов для портфолио, но в этом задании вам необходимо создать собственную уникальную страницу. Используя Bootstrap, сделать это будет намного проще.",
- "Если что-то не получается, воспользуйтесь Read-Search-Ask.",
- "Когда выполните задание кликните кнопку \"I've completed this challenge\" и добавьте ссылку на ваш CodePen.",
- "Вы можете получить отзыв о вашем проекте от коллег, поделившись ссылкой на него в нашем чате для рассмотрения кода. Также вы можете поделиться ею через Twitter и на странице Free Code Camp вашего города на Facebook."
- ]
- }
- }
- },
{
"id": "bd7158d8c442eddfaeb5bd10",
"title": "Show the Local Weather",
@@ -163,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.",
From 0bd9c3a6d09d803b180b4d7f37c1636a8e9edded Mon Sep 17 00:00:00 2001
From: Stuart Taylor
Date: Mon, 5 Mar 2018 14:25:51 +0000
Subject: [PATCH 06/11] chore(twitch): Make twitch title compatable with
DynamicForm
---
.../take-home-interview-projects.json | 4 ++--
.../09-certificates/legacy-front-end-certificate.json | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
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 e87a6a4a26..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
@@ -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 примера. Напишите его самостоятельно.",
diff --git a/seed/challenges/09-certificates/legacy-front-end-certificate.json b/seed/challenges/09-certificates/legacy-front-end-certificate.json
index da0a9d1f2e..d1fd11ebad 100644
--- a/seed/challenges/09-certificates/legacy-front-end-certificate.json
+++ b/seed/challenges/09-certificates/legacy-front-end-certificate.json
@@ -33,7 +33,7 @@
},
{
"id": "bd7158d8c442eddfaeb5bd1f",
- "title": "Use the Twitch.tv JSON API"
+ "title": "Use the Twitch JSON API"
},
{
"id": "bd7158d8c442eddfaeb5bd18",
From 10148ce5b37ccb9a69c3d9df099250722cf36a86 Mon Sep 17 00:00:00 2001
From: Stuart Taylor
Date: Mon, 5 Mar 2018 14:26:07 +0000
Subject: [PATCH 07/11] fix(challengeId): Use correct id for Portfolio
challenge
---
.../responsive-web-design-projects.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 9d6c23c158..842d166b56 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.",
From e897c46cb83feb09b80eec996cf8d3a3f123cebb Mon Sep 17 00:00:00 2001
From: Stuart Taylor
Date: Mon, 5 Mar 2018 14:26:57 +0000
Subject: [PATCH 08/11] chore(cleanup): Remove console.log
---
common/models/user.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/common/models/user.js b/common/models/user.js
index 15e88b29c1..0e3386aaf7 100644
--- a/common/models/user.js
+++ b/common/models/user.js
@@ -44,7 +44,6 @@ function destroyAll(id, Model) {
function buildChallengeMapUpdate(challengeMap, project) {
const key = Object.keys(project)[0];
const solutions = project[key];
- console.log(solutions);
const currentChallengeMap = { ...challengeMap };
const currentCompletedProjects = _.pick(
currentChallengeMap,
From 6bf435da47fcd69122713315f0f0fc379885fa1c Mon Sep 17 00:00:00 2001
From: Stuart Taylor
Date: Tue, 6 Mar 2018 10:36:45 +0000
Subject: [PATCH 09/11] chore(legacyProjects): Move legacyProjectData to
common/utils
---
common/app/entities/index.js | 2 +-
common/{app/routes/Settings => }/utils/legacyProjectData.js | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename common/{app/routes/Settings => }/utils/legacyProjectData.js (100%)
diff --git a/common/app/entities/index.js b/common/app/entities/index.js
index 534d831e2e..e479dfaa4a 100644
--- a/common/app/entities/index.js
+++ b/common/app/entities/index.js
@@ -12,7 +12,7 @@ import { themes } from '../../utils/themes';
import { usernameSelector, types as app } from '../redux';
import { types as challenges } from '../routes/Challenges/redux';
import { types as map } from '../Map/redux';
-import legacyProjects from '../routes/Settings/utils/legacyProjectData';
+import legacyProjects from '../../utils/legacyProjectData';
export const ns = 'entities';
export const getNS = state => state[ns];
diff --git a/common/app/routes/Settings/utils/legacyProjectData.js b/common/utils/legacyProjectData.js
similarity index 100%
rename from common/app/routes/Settings/utils/legacyProjectData.js
rename to common/utils/legacyProjectData.js
From 2281c85df69cfda507ba1283b9644c713a4d1338 Mon Sep 17 00:00:00 2001
From: Stuart Taylor
Date: Tue, 6 Mar 2018 10:38:37 +0000
Subject: [PATCH 10/11] chore(certs): Rename endpoint /c/ => /certificates/
---
common/app/routes/Profile/components/Certificates.jsx | 2 +-
common/app/routes/Settings/components/Cert-Settings.jsx | 2 +-
common/app/routes/Settings/components/JSAlgoAndDSForm.jsx | 2 +-
server/boot/certificate.js | 2 +-
server/views/certificate/advanced-front-end.jade | 2 +-
server/views/certificate/apis-and-microservices.jade | 2 +-
server/views/certificate/data-visualization.jade | 2 +-
server/views/certificate/front-end-libraries.jade | 2 +-
.../certificate/information-security-and-quality-assurance.jade | 2 +-
.../certificate/javascript-algorithms-and-data-structures.jade | 2 +-
server/views/certificate/legacy/back-end.jade | 2 +-
server/views/certificate/legacy/data-visualization.jade | 2 +-
server/views/certificate/legacy/front-end.jade | 2 +-
server/views/certificate/legacy/full-stack.jade | 2 +-
server/views/certificate/responsive-web-design.jade | 2 +-
15 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/common/app/routes/Profile/components/Certificates.jsx b/common/app/routes/Profile/components/Certificates.jsx
index dc1f47c4f8..cecca5d961 100644
--- a/common/app/routes/Profile/components/Certificates.jsx
+++ b/common/app/routes/Profile/components/Certificates.jsx
@@ -134,7 +134,7 @@ function renderCertShow(username, cert) {
block={ true }
bsSize='lg'
bsStyle='primary'
- href={ `/c/${username}/${cert.showURL}`}
+ href={ `/certificates/${username}/${cert.showURL}`}
>
Show
diff --git a/common/app/routes/Settings/components/Cert-Settings.jsx b/common/app/routes/Settings/components/Cert-Settings.jsx
index 0002d616dd..8661c4e876 100644
--- a/common/app/routes/Settings/components/Cert-Settings.jsx
+++ b/common/app/routes/Settings/components/Cert-Settings.jsx
@@ -208,7 +208,7 @@ class CertificationSettings extends PureComponent {
block={ true }
bsSize='lg'
bsStyle='primary'
- href={ `/c/${username}/${superBlock}`}
+ href={ `/certificates/${username}/${superBlock}`}
>
Show Certificate
:
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/server/boot/certificate.js b/server/boot/certificate.js
index 11243e2aa6..cd22c68838 100644
--- a/server/boot/certificate.js
+++ b/server/boot/certificate.js
@@ -197,7 +197,7 @@ export default function certificate(app) {
verifyCert
);
router.get(
- '/c/:username/:cert',
+ '/certificates/:username/:cert',
showCert
);
diff --git a/server/views/certificate/advanced-front-end.jade b/server/views/certificate/advanced-front-end.jade
index 01a76199f1..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
\ No newline at end of file
+ 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 8fcdd5e371..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
+ 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 c0570958e1..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
+ 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 62a4e99e7d..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
+ 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 9215bc4902..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
+ 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 2c821113e4..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
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/javascript-algorithms-and-data-structures
diff --git a/server/views/certificate/legacy/back-end.jade b/server/views/certificate/legacy/back-end.jade
index 56c3a29266..c2cce165da 100644
--- a/server/views/certificate/legacy/back-end.jade
+++ b/server/views/certificate/legacy/back-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}/legacy-back-end
+ 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
index 640bf815d8..bf9537fca4 100644
--- a/server/views/certificate/legacy/data-visualization.jade
+++ b/server/views/certificate/legacy/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}/legacy-data-visualization
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/legacy-data-visualization
diff --git a/server/views/certificate/legacy/front-end.jade b/server/views/certificate/legacy/front-end.jade
index d68947129e..56cd262af4 100644
--- a/server/views/certificate/legacy/front-end.jade
+++ b/server/views/certificate/legacy/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}/legacy-front-end
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/legacy-front-end
diff --git a/server/views/certificate/legacy/full-stack.jade b/server/views/certificate/legacy/full-stack.jade
index c8f4f233a4..5fd4ab7920 100644
--- a/server/views/certificate/legacy/full-stack.jade
+++ b/server/views/certificate/legacy/full-stack.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}/legacy-full-stack
\ No newline at end of file
+ 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 cc4eb37871..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
+ p.verify Verify this certificate at: https://freecodecamp.org/certificates/#{username}/responsive-web-design
From 33d26aaa6cfade14f246673442eb29c7aa88ba1e Mon Sep 17 00:00:00 2001
From: Stuart Taylor
Date: Wed, 7 Mar 2018 23:35:23 +0000
Subject: [PATCH 11/11] fix(showCert): Target _blank the link for Show Cert
---
common/app/routes/Settings/components/Cert-Settings.jsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/common/app/routes/Settings/components/Cert-Settings.jsx b/common/app/routes/Settings/components/Cert-Settings.jsx
index 8661c4e876..c926c4cb51 100644
--- a/common/app/routes/Settings/components/Cert-Settings.jsx
+++ b/common/app/routes/Settings/components/Cert-Settings.jsx
@@ -209,6 +209,7 @@ class CertificationSettings extends PureComponent {
bsSize='lg'
bsStyle='primary'
href={ `/certificates/${username}/${superBlock}`}
+ target='_blank'
>
Show Certificate
: