From b2bf3ce391aa1427f5ba03e51208935fa0be7509 Mon Sep 17 00:00:00 2001 From: ahstro Date: Sat, 3 Oct 2015 11:23:41 +0200 Subject: [PATCH 01/14] Truncate a string: Minor typo The assert message said `1`, instead of the number `11` that was actually being used in the assertion. --- seed/challenges/basic-bonfires.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/basic-bonfires.json b/seed/challenges/basic-bonfires.json index 931c157ce1..12da55a771 100644 --- a/seed/challenges/basic-bonfires.json +++ b/seed/challenges/basic-bonfires.json @@ -378,7 +378,7 @@ "truncate(\"A-tisket a-tasket A green and yellow basket\", 11, \"\");" ], "tests": [ - "assert(truncate(\"A-tisket a-tasket A green and yellow basket\", 11) === \"A-tisket...\", 'message: truncate(\"A-tisket a-tasket A green and yellow basket\", 1) should return \"A-tisket...\".');", + "assert(truncate(\"A-tisket a-tasket A green and yellow basket\", 11) === \"A-tisket...\", 'message: truncate(\"A-tisket a-tasket A green and yellow basket\", 11) should return \"A-tisket...\".');", "assert(truncate(\"Peter Piper picked a peck of pickled peppers\", 14) === \"Peter Piper...\", 'message: truncate(\"Peter Piper picked a peck of pickled peppers\", 14) should return \"Peter Piper...\".');", "assert(truncate(\"A-tisket a-tasket A green and yellow basket\", \"A-tisket a-tasket A green and yellow basket\".length) === \"A-tisket a-tasket A green and yellow basket\", 'message: truncate(\"A-tisket a-tasket A green and yellow basket\", \"A-tisket a-tasket A green and yellow basket\".length) should return \"A-tisket a-tasket A green and yellow basket\".');", "assert(truncate('A-tisket a-tasket A green and yellow basket', 'A-tisket a-tasket A green and yellow basket'.length + 2) === 'A-tisket a-tasket A green and yellow basket', 'message: truncate(\"A-tisket a-tasket A green and yellow basket\", \"A-tisket a-tasket A green and yellow basket\".length + 2) should return \"A-tisket a-tasket A green and yellow basket\".');" From c04832d61846db4131ad566feaac51495a2e8464 Mon Sep 17 00:00:00 2001 From: Florencia Tarditti Date: Mon, 5 Oct 2015 21:26:18 +0200 Subject: [PATCH 02/14] fix bootstrap fluid containers wording issue Closes #2467. --- seed/challenges/bootstrap.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/bootstrap.json b/seed/challenges/bootstrap.json index d405871390..3982fe18d5 100644 --- a/seed/challenges/bootstrap.json +++ b/seed/challenges/bootstrap.json @@ -9,7 +9,7 @@ "Now let's go back to our Cat Photo App. This time, we'll style it using the popular Bootstrap responsive CSS framework.", "Bootstrap will figure out how wide your screen is and respond by resizing your HTML elements - hence the name Responsive Design.", "With responsive design, there is no need to design a mobile version of your website. It will look good on devices with screens of any width.", - "You can add Bootstrap to any app just by including it with <link rel=\"stylesheet\" href=\"//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css\"/> at the top of your HTML. But we've gone ahead and automatically added it to your Cat Photo App for you.", + "You can add Bootstrap to any app just by including it with <link rel=\"stylesheet\" href=\"//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css\"/> at the top of your HTML. But we've added it for you to this page behind the scenes.", "To get started, we should nest all of our HTML in a div element with the class container-fluid." ], "tests": [ From 8c48626f031c40b5ffc6fc20684f846260181f93 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Fri, 2 Oct 2015 11:47:36 -0700 Subject: [PATCH 03/14] Add certification page --- client/commonFramework.js | 51 ++++++- common/models/user.json | 21 ++- public/fonts/saxmono.ttf | Bin 0 -> 103740 bytes .../front-end-development-certificate.json | 17 ++- .../full-stack-development-certificate.json | 16 ++- server/boot/certificate.js | 131 ++++++++++++++++++ server/boot/challenge.js | 9 +- server/boot/user.js | 94 ++++++++++++- server/utils/middleware.js | 48 +------ server/utils/rx.js | 23 +-- server/views/account/show.jade | 7 +- server/views/certificate/font.jade | 45 ++++++ server/views/certificate/front-end.jade | 6 + server/views/certificate/full-stack.jade | 6 + server/views/certificate/index.jade | 7 + server/views/certificate/script.jade | 8 ++ server/views/coursewares/showStep.jade | 8 +- 17 files changed, 415 insertions(+), 82 deletions(-) create mode 100755 public/fonts/saxmono.ttf create mode 100644 server/boot/certificate.js create mode 100644 server/views/certificate/font.jade create mode 100644 server/views/certificate/front-end.jade create mode 100644 server/views/certificate/full-stack.jade create mode 100644 server/views/certificate/index.jade create mode 100644 server/views/certificate/script.jade diff --git a/client/commonFramework.js b/client/commonFramework.js index 8abcb6a9dc..fcb1ccc58d 100644 --- a/client/commonFramework.js +++ b/client/commonFramework.js @@ -865,14 +865,53 @@ common.init.push((function() { } next(); }); - } - function handleActionClick() { - $(this) - .parent() - .find('.disabled') - .removeClass('disabled'); + function handleActionClick(e) { + var props = common.challengeSeed[0] || + { stepIndex: [] }; + + var $el = $(this); + var index = +$el.attr('id'); + var propIndex = props.stepIndex.indexOf(index); + + if (propIndex === -1) { + return $el + .parent() + .find('.disabled') + .removeClass('disabled'); + } + + // an API action + // prevent link from opening + e.preventDefault(); + var prop = props.properties[propIndex]; + var api = props.apis[propIndex]; + if (common[prop]) { + return $el + .parent() + .find('.disabled') + .removeClass('disabled'); + } + $ + .post(api) + .done(function(data) { + // assume a boolean indicates passing + if (typeof data === 'boolean') { + return $el + .parent() + .find('.disabled') + .removeClass('disabled'); + } + // assume api returns string when fails + $el + .parent() + .find('.disabled') + .replaceWith('

' + data + '

'); + }) + .fail(function() { + console.log('failed'); + }); } function handleFinishClick(e) { diff --git a/common/models/user.json b/common/models/user.json index c46d1c2f13..cb34389b80 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -102,14 +102,31 @@ }, "isLocked": { "type": "boolean", - "default": false + "default": false, + "description": "Campers profile does not show challenges to the public" }, "currentChallenge": { "type": {} }, "isUniqMigrated": { "type": "boolean", - "default": false + "default": false, + "description": "Campers completedChallenges array is free of duplicates" + }, + "isHonest": { + "type": "boolean", + "default": false, + "description": "Camper has signed academic honesty policy" + }, + "isFrontEndCert": { + "type": "boolean", + "defaut": false, + "description": "Camper is front end certified" + }, + "isFullStackCert": { + "type": "boolean", + "default": false, + "description": "Campers is full stack certified" }, "completedChallenges": { "type": [ diff --git a/public/fonts/saxmono.ttf b/public/fonts/saxmono.ttf new file mode 100755 index 0000000000000000000000000000000000000000..76c77d6411749741383ddb3d6d95ecb43835b4ae GIT binary patch literal 103740 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̫a4HfJEnEAo2AhAn|(;Na;KZB))$Fr2PC8NWVW0 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 literal 0 HcmV?d00001 diff --git a/seed/challenges/front-end-development-certificate.json b/seed/challenges/front-end-development-certificate.json index fa0bb1ce40..ac8b53ee21 100644 --- a/seed/challenges/front-end-development-certificate.json +++ b/seed/challenges/front-end-development-certificate.json @@ -5,14 +5,25 @@ { "id": "561add10cb82ac38a17513be", "title": "Claim Your Front End Development Certificate", - "difficulty": 0.00, - "challengeSeed": [], + "challengeSeed": [ + { + "properties": ["isHonest", "isFrontEndCert"], + "apis": ["/certificate/honest", "/certificate/verify/front-end"], + "stepIndex": [0, 1] + } + ], "description": [ [ "http://i.imgur.com/RlEk2IF.jpg", "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", - "" + "#" + ], + [ + "http://i.imgur.com/RlEk2IF.jpg", + "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", + "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", + "#" ] ], "type": "Waypoint", diff --git a/seed/challenges/full-stack-development-certificate.json b/seed/challenges/full-stack-development-certificate.json index 901883aebc..bfb2f7df4a 100644 --- a/seed/challenges/full-stack-development-certificate.json +++ b/seed/challenges/full-stack-development-certificate.json @@ -6,13 +6,25 @@ "id": "660add10cb82ac38a17513be", "title": "Claim Your Full Stack Development Certificate", "difficulty": 0.00, - "challengeSeed": [], + "challengeSeed": [ + { + "properties": ["isHonest", "isFullStackCert"], + "apis": ["/certificate/honest", "/certificate/verify/full-stack"], + "stepIndex": [0, 1] + } + ], "description": [ [ "http://i.imgur.com/RlEk2IF.jpg", "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", - "" + "#" + ], + [ + "http://i.imgur.com/RlEk2IF.jpg", + "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", + "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", + "#" ] ], "type": "Waypoint", diff --git a/server/boot/certificate.js b/server/boot/certificate.js new file mode 100644 index 0000000000..b6a3db9461 --- /dev/null +++ b/server/boot/certificate.js @@ -0,0 +1,131 @@ +import _ from 'lodash'; +import dedent from 'dedent'; +import { Observable } from 'rx'; +import debugFactory from 'debug'; + +import { + ifNoUser401, + ifNoUserSend +} from '../utils/middleware'; + +import { + saveUser, + observeQuery +} from '../utils/rx'; + +const frontEndChallangeId = '561add10cb82ac38a17513be'; +const fullStackChallangeId = '660add10cb82ac38a17513be'; +const debug = debugFactory('freecc:certification'); +const sendMessageToNonUser = ifNoUserSend( + 'must be logged in to complete.' +); + +function isCertified(frontEndIds, { completedChallenges, isFrontEndCert }) { + if (isFrontEndCert) { + return true; + } + return _.every(frontEndIds, ({ id }) => _.some(completedChallenges, { id })); +} + +export default function certificate(app) { + const router = app.loopback.Router(); + const { Challenge } = app.models; + + const frontEndChallangeIds$ = observeQuery( + Challenge, + 'findById', + frontEndChallangeId, + { + tests: true + } + ) + .map(({ tests = [] }) => tests) + .shareReplay(); + + const fullStackChallangeIds$ = observeQuery( + Challenge, + 'findById', + fullStackChallangeId, + { + tests: true + } + ) + .map(({ tests = [] }) => tests) + .shareReplay(); + + router.post( + '/certificate/verify/front-end', + ifNoUser401, + verifyCert + ); + + router.post( + '/certificate/verify/full-stack', + ifNoUser401, + verifyCert + ); + + router.post( + '/certificate/honest', + sendMessageToNonUser, + postHonest + ); + + app.use(router); + + function verifyCert(req, res, next) { + const isFront = req.path.split('/').pop() === 'front-end'; + Observable.just({}) + .flatMap(() => { + if (isFront) { + return frontEndChallangeIds$; + } + return fullStackChallangeIds$; + }) + .flatMap((tests) => { + const { user } = req; + if ( + isFront && !user.isFrontEndCert && isCertified(tests, user) || + !isFront && !user.isFullStackCert && isCertified(tests, user) + ) { + debug('certified'); + if (isFront) { + user.isFrontEndCert = true; + } else { + user.isFullStackCert = true; + } + return saveUser(user); + } + return Observable.just(user); + }) + .subscribe( + user => { + if ( + isFront && user.isFrontEndCert || + !isFront && user.isFullStackCert + ) { + return res.status(200).send(true); + } + return res.status(200).send( + dedent` + Looks like you have not completed the neccessary steps, + Please return the map + ` + ); + }, + next + ); + } + + function postHonest(req, res, next) { + const { user } = req; + user.isHonest = true; + saveUser(user) + .subscribe( + (user) => { + res.status(200).send(!!user.isHonest); + }, + next + ); + } +} diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 15cb507855..6a056b705d 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -9,11 +9,10 @@ import utils from '../utils'; import { saveUser, observeMethod, - observableQueryFromModel + observeQuery } from '../utils/rx'; import { - userMigration, ifNoUserSend } from '../utils/middleware'; @@ -147,8 +146,6 @@ module.exports = function(app) { completedBonfire ); - // the follow routes are covered by userMigration - router.use(userMigration); router.get('/map', challengeMap); router.get( '/challenges/next-challenge', @@ -330,7 +327,7 @@ module.exports = function(app) { challengeType: 5 }; - observableQueryFromModel( + observeQuery( User, 'findOne', { where: { username: ('' + completedWith).toLowerCase() } } @@ -458,7 +455,7 @@ module.exports = function(app) { verified: false }; - observableQueryFromModel( + observeQuery( User, 'findOne', { where: { username: completedWith.toLowerCase() } } diff --git a/server/boot/user.js b/server/boot/user.js index 214c0b9264..9567a5066a 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -1,8 +1,11 @@ +import _ from 'lodash'; import dedent from 'dedent'; import moment from 'moment'; +import { Observable } from 'rx'; import debugFactory from 'debug'; import { ifNoUser401, ifNoUserRedirectTo } from '../utils/middleware'; +import { observeQuery } from '../utils/rx'; const debug = debugFactory('freecc:boot:user'); const daysBetween = 1.5; @@ -52,7 +55,16 @@ function dayDiff([head, tail]) { module.exports = function(app) { var router = app.loopback.Router(); var User = app.models.User; - // var Story = app.models.Story; + function findUserByUsername$(username, fields) { + return observeQuery( + User, + 'findOne', + { + where: { username }, + fields + } + ); + } router.get('/login', function(req, res) { res.redirect(301, '/signin'); @@ -85,7 +97,18 @@ module.exports = function(app) { ); router.get('/vote1', vote1); router.get('/vote2', vote2); - // Ensure this is the last route! + + // Ensure these are the last routes! + router.get( + '/:username/front-end-certification', + showCert + ); + + router.get( + '/:username/full-stack-certification', + showCert + ); + router.get('/:username', returnUser); app.use(router); @@ -184,14 +207,20 @@ module.exports = function(app) { return (obj.name || '').match(/^Waypoint/i); }); + debug('user is fec', profileUser.isFrontEndCert); res.render('account/show', { title: 'Camper ' + profileUser.username + '\'s portfolio', username: profileUser.username, name: profileUser.name, + isMigrationGrandfathered: profileUser.isMigrationGrandfathered, isGithubCool: profileUser.isGithubCool, isLocked: !!profileUser.isLocked, + isFrontEndCert: profileUser.isFrontEndCert, + isFullStackCert: profileUser.isFullStackCert, + isHonest: profileUser.isHonest, + location: profileUser.location, calender: data, @@ -216,6 +245,62 @@ module.exports = function(app) { ); } + function showCert(req, res, next) { + const username = req.params.username.toLowerCase(); + const { user } = req; + const showFront = req.path.split('/').pop() === 'front-end-certification'; + Observable.just(user) + .flatMap(user => { + if (user && user.username === username) { + return Observable.just(user); + } + return findUserByUsername$(username, { + isFrontEndCert: true, + isFullStackCert: true, + completedChallenges: true, + username: true, + name: true + }); + }) + .subscribe( + (user) => { + if (!user) { + req.flash('errors', { + msg: `404: We couldn't find the user ${username}` + }); + return res.redirect('/'); + } + if ( + showFront && user.isFrontEndCert || + !showFront && user.isFullStackCert + ) { + var { completedDate } = _.find(user.completedChallenges, { + id: '561add10cb82ac38a17513be' + }); + + return res.render( + showFront ? + 'certificate/front-end.jade' : + 'certificate/full-stack.jade', + { + username: user.username, + date: moment(new Date(completedDate)) + .format('MMMM, Do YYYY'), + name: user.name + } + ); + } + req.flash('errors', { + msg: showFront ? + `Looks like user ${username} is not Front End certified` : + `Looks like user ${username} is not Full Stack certified` + }); + res.redirect('/map'); + }, + next + ); + } + function toggleLockdownMode(req, res, next) { if (req.user.isLocked === true) { req.user.isLocked = false; @@ -297,11 +382,6 @@ module.exports = function(app) { }); } - /** - * POST /forgot - * Create a random token, then the send user an email with a reset link. - */ - function postForgot(req, res) { const errors = req.validationErrors(); const email = req.body.email.toLowerCase(); diff --git a/server/utils/middleware.js b/server/utils/middleware.js index 1edec7a59b..3c541cbb11 100644 --- a/server/utils/middleware.js +++ b/server/utils/middleware.js @@ -1,60 +1,24 @@ -var R = require('ramda'); - -/* - * Middleware to migrate users from fragmented challenge structure to unified - * challenge structure - * - * @param req - * @param res - * @returns null - */ -exports.userMigration = function userMigration(req, res, next) { - if (!req.user || req.user.completedChallenges.length !== 0) { - return next(); - } - req.user.completedChallenges = R.filter(function(elem) { - // getting rid of undefined - return elem; - }, R.concat( - req.user.completedCoursewares, - req.user.completedBonfires.map(function(bonfire) { - return ({ - completedDate: bonfire.completedDate, - id: bonfire.id, - name: bonfire.name, - completedWith: bonfire.completedWith, - solution: bonfire.solution, - githubLink: '', - verified: false, - challengeType: 5 - }); - }) - ) - ); - return next(); -}; - -exports.ifNoUserRedirectTo = function ifNoUserRedirectTo(url) { +export function ifNoUserRedirectTo(url) { return function(req, res, next) { if (req.user) { return next(); } return res.redirect(url); }; -}; +} -exports.ifNoUserSend = function ifNoUserSend(sendThis) { +export function ifNoUserSend(sendThis) { return function(req, res, next) { if (req.user) { return next(); } return res.status(200).send(sendThis); }; -}; +} -exports.ifNoUser401 = function ifNoUser401(req, res, next) { +export function ifNoUser401(req, res, next) { if (req.user) { return next(); } return res.status(401).end(); -}; +} diff --git a/server/utils/rx.js b/server/utils/rx.js index d3b1ac41b0..68086d2563 100644 --- a/server/utils/rx.js +++ b/server/utils/rx.js @@ -1,7 +1,9 @@ -var Rx = require('rx'); -var debug = require('debug')('freecc:rxUtils'); +import Rx from 'rx'; +import debugFactory from 'debug'; -exports.saveInstance = function saveInstance(instance) { +const debug = debugFactory('freecc:rxUtils'); + +export function saveInstance(instance) { return new Rx.Observable.create(function(observer) { if (!instance || typeof instance.save !== 'function') { debug('no instance or save method'); @@ -17,16 +19,15 @@ exports.saveInstance = function saveInstance(instance) { observer.onCompleted(); }); }); -}; +} // alias saveInstance -exports.saveUser = exports.saveInstance; +export const saveUser = saveInstance; -exports.observeQuery = exports.observableQueryFromModel = - function observableQueryFromModel(Model, method, query) { - return Rx.Observable.fromNodeCallback(Model[method], Model)(query); - }; +export function observeQuery(Model, method, query) { + return Rx.Observable.fromNodeCallback(Model[method], Model)(query); +} -exports.observeMethod = function observeMethod(context, methodName) { +export function observeMethod(context, methodName) { return Rx.Observable.fromNodeCallback(context[methodName], context); -}; +} diff --git a/server/views/account/show.jade b/server/views/account/show.jade index 5f07918815..530f74b746 100644 --- a/server/views/account/show.jade +++ b/server/views/account/show.jade @@ -58,8 +58,13 @@ block content h1.flat-top.wrappable= name h1.flat-top.wrappable= location h1.flat-top.text-primary= "[ " + (progressTimestamps.length) + " ]" + if isFrontEndCert + a.btn.btn-primary(href='/' + username + '/front-end-certification') View My Front End Development Certification + if isFullStackCert + .button-spacer + a.btn.btn-success(href='/' + username + '/full-stack-certification') View My Full Stack Development Certification if (user && user.username !== username) - a.btn.btn-lg.btn-block.btn-twitter.btn-link-social(href='/link/twitter') + a.btn.btn-lg.btn-block.btn-twitter.btn-link-social(href='/leaderboard/add?username=#{username}') i.fa.fa-plus-square | Add them to my personal leaderboard diff --git a/server/views/certificate/font.jade b/server/views/certificate/font.jade new file mode 100644 index 0000000000..54b76b935f --- /dev/null +++ b/server/views/certificate/font.jade @@ -0,0 +1,45 @@ +style. + @font-face { + font-family: "Sax Mono"; + src: url("/fonts/saxmono.ttf") format("truetype"); + } + + body { + display: inline-block; + font-family: "Sax Mono", monospace; + margin: 0; + position: absolute; + text-align: center; + } + + .img-abs { + left 0; + position: relative; + top: 0; + width: 2000px + } + + .cert-name { + font-size: 64px; + left: 1000px; + position: absolute; + top: 704px; + z-index: 1000; + } + + .cert-date { + font-size: 60px; + left: 760px; + position: absolute; + top: 1004.8px; + z-index: 1000; + } + + .cert-link { + font-size: 22px; + left: 120px; + position: absolute; + top: 1488px; + z-index: 1000; + } + diff --git a/server/views/certificate/front-end.jade b/server/views/certificate/front-end.jade new file mode 100644 index 0000000000..379dfb7dd1 --- /dev/null +++ b/server/views/certificate/front-end.jade @@ -0,0 +1,6 @@ +include font +#name.cert-name= name +img#cert.img-abs(src='http://i.imgur.com/ToFZKBd.jpg') +.cert-date= date +.cert-link verify this certification at: http://freecodecamp.com/#{username}/front-end-certification +include script diff --git a/server/views/certificate/full-stack.jade b/server/views/certificate/full-stack.jade new file mode 100644 index 0000000000..95c94a6eb0 --- /dev/null +++ b/server/views/certificate/full-stack.jade @@ -0,0 +1,6 @@ +include font +#name.cert-name= name +img#cert.img-abs(src='http://i.imgur.com/Z4PgjBQ.jpg') +.cert-date= date +.cert-link verify this certification at: http://freecodecamp.com/#{username}/full-stack-certification +include script diff --git a/server/views/certificate/index.jade b/server/views/certificate/index.jade new file mode 100644 index 0000000000..51e0294db2 --- /dev/null +++ b/server/views/certificate/index.jade @@ -0,0 +1,7 @@ +extends ../layout +block content + .panel.panel-info + .panel-heading.text-center + h1 Certificate + .panel-body + p foo diff --git a/server/views/certificate/script.jade b/server/views/certificate/script.jade new file mode 100644 index 0000000000..ccb83323c5 --- /dev/null +++ b/server/views/certificate/script.jade @@ -0,0 +1,8 @@ +script. + (function() { + var containerWidth = document.getElementById('cert').offsetWidth; + var nameDiv = document.getElementById('name'); + var nameWidth = nameDiv.offsetWidth; + console.log(containerWidth, nameWidth); + nameDiv.style.left = ((containerWidth - nameWidth) / 2) + 15; + })(); diff --git a/server/views/coursewares/showStep.jade b/server/views/coursewares/showStep.jade index f89d65eda2..fa8f11431a 100644 --- a/server/views/coursewares/showStep.jade +++ b/server/views/coursewares/showStep.jade @@ -9,7 +9,7 @@ block content .caption p.large-p= step[2] if step[3] - a.btn.btn-block.btn-primary.challenge-step-btn-action(href='#{step[3]}' target='_blank') Go To Link + a.btn.btn-block.btn-primary.challenge-step-btn-action(id='#{index}' href='#{step[3]}' target='_blank') Go To Link if index + 1 === description.length .btn.btn-block.btn-primary.challenge-step-btn-finish(id='last' class=step[3] ? 'disabled' : '') Finish challenge else @@ -32,8 +32,12 @@ block content a.btn.btn-lg.btn-primary.btn-block(href='/challenges/next-challenge?id=' + challengeId) Go to my next challenge script(src=rev('/js', 'commonFramework.js')) script. - var common = common || { init: [] }; + var common = window.common || { init: [] }; common.challengeId = !{JSON.stringify(challengeId)}; common.challengeName = !{JSON.stringify(name)}; common.challengeType = 7; common.dashedName = !{JSON.stringify(dashedName || '')}; + common.isHonest = !{JSON.stringify(isHonest || false)}; + common.isFrontEndCert = !{JSON.stringify(isFrontEndCert || false)}; + common.isFullStackCert = !{JSON.stringify(isFullStackCert || false)}; + common.challengeSeed = !{JSON.stringify(challengeSeed || [])}; From 8f845961427162f6fd4084913bb8e9258e6255ab Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 5 Oct 2015 16:38:58 -0700 Subject: [PATCH 04/14] Fix 500 out of range bug when completing last challenge --- server/boot/challenge.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 15cb507855..adf4c95c21 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -183,9 +183,9 @@ module.exports = function(app) { 'could not find challenge block for ' + challenge.block ); } - const nextBlock$ = blocks$.elementAt(blockIndex + 1); - const firstChallengeOfNextBlock$ = nextBlock$ - .map(block => block.challenges[0]); + const firstChallengeOfNextBlock$ = blocks$ + .elementAtOrDefault(blockIndex + 1, {}) + .map(({ challenges = [] }) => challenges[0]); return blocks$ .elementAt(blockIndex) @@ -214,6 +214,9 @@ module.exports = function(app) { }); }) .map(nextChallenge => { + if (!nextChallenge) { + return null; + } nextChallengeName = nextChallenge.dashedName; return nextChallengeName; }) From 34efc43f6a5cf5c0d1d6d76eb4fa5ca85ce8c110 Mon Sep 17 00:00:00 2001 From: Quincy Larson Date: Mon, 5 Oct 2015 17:18:35 -0700 Subject: [PATCH 05/14] update copy of certificate flows --- .../front-end-development-certificate.json | 26 ++++++++++++++----- .../full-stack-development-certificate.json | 26 ++++++++++++++----- server/boot/certificate.js | 8 ++++++ server/boot/user.js | 4 ++- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/seed/challenges/front-end-development-certificate.json b/seed/challenges/front-end-development-certificate.json index ac8b53ee21..351809e702 100644 --- a/seed/challenges/front-end-development-certificate.json +++ b/seed/challenges/front-end-development-certificate.json @@ -9,21 +9,33 @@ { "properties": ["isHonest", "isFrontEndCert"], "apis": ["/certificate/honest", "/certificate/verify/front-end"], - "stepIndex": [0, 1] + "stepIndex": [1, 2] } ], "description": [ [ - "http://i.imgur.com/RlEk2IF.jpg", - "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", - "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", + "http://i.imgur.com/luMkKst.jpg", + "An image of our Front End Development Certificate", + "This challenge will give you your verified Front End Development Certificate. Before we issue your certificate, we must verify that you have completed all of our basic and intermediate Bonfires, and all our basic and intermediate Ziplines. You must also accept our Academic Honesty Pledge. Click the button below to start this process.", + "" + ], + [ + "http://i.imgur.com/HArFfMN.jpg", + "The definition of plagiarism: Plagiarism (noun) - copying someone else’s work and presenting it as your own without crediting them", + "By clicking below, you pledge that all of your submitted code A) is code you or your pair personally wrote, or B) comes from open source libraries like jQuery, or C) has been clearly attributed to its original authors. You also give us permission to audit your challenge solutions and revoke your certificate if we discover evidence of plagiarism.", "#" ], [ - "http://i.imgur.com/RlEk2IF.jpg", - "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", - "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", + "http://i.imgur.com/14F2Van.jpg", + "An image of the text \"Front End Development Certificate requirements\"", + "Let's confirm that you have completed all of our basic and intermediate Bonfires, and all our basic and intermediate Ziplines. Click the button below to verify this.", "#" + ], + [ + "http://i.imgur.com/16SIhHO.jpg", + "An image of the word \"Congratulations\"", + "Congratulations! We've added your Front End Development Certificate to your certificate to your portfolio page. Unless you choose to hide your solutions, this certificate will remain publicly visible and verifiable.", + "" ] ], "type": "Waypoint", diff --git a/seed/challenges/full-stack-development-certificate.json b/seed/challenges/full-stack-development-certificate.json index bfb2f7df4a..7ae95c94e7 100644 --- a/seed/challenges/full-stack-development-certificate.json +++ b/seed/challenges/full-stack-development-certificate.json @@ -10,21 +10,33 @@ { "properties": ["isHonest", "isFullStackCert"], "apis": ["/certificate/honest", "/certificate/verify/full-stack"], - "stepIndex": [0, 1] + "stepIndex": [1, 2] } ], "description": [ [ - "http://i.imgur.com/RlEk2IF.jpg", - "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", - "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", + "http://i.imgur.com/qXublEe.jpg", + "An image of our Full Stack Development Certificate", + "This challenge will give you your verified Full Stack Development Certificate. Before we issue your certificate, we must verify that you have completed all of Bonfires, Ziplines and Basejumps. You must also accept our Academic Honesty Pledge. Click the button below to start this process.", + "" + ], + [ + "http://i.imgur.com/HArFfMN.jpg", + "The definition of plagiarism: Plagiarism (noun) - copying someone else’s work and presenting it as your own without crediting them", + "By clicking below, you pledge that all of your submitted code A) is code you or your pair personally wrote, or B) comes from open source libraries like jQuery, or C) has been clearly attributed to its original authors. You also give us permission to audit your challenge solutions and revoke your certificate if we discover evidence of plagiarism.", "#" ], [ - "http://i.imgur.com/RlEk2IF.jpg", - "a picture of Free Code Camp's 4 benefits: Get connected, Learn JavaScript, Build your Portfolio, Help nonprofits", - "Welcome to Free Code Camp. We're an open source community of busy people who learn to code and help nonprofits.", + "http://i.imgur.com/2qn7tHp.jpg", + "An image of the text \"Full Stack Development Certificate requirements\"", + "Let's confirm that you have completed all of our Bonfires, Ziplines and Basejumps. Click the button below to verify this.", "#" + ], + [ + "http://i.imgur.com/16SIhHO.jpg", + "An image of the word \"Congratulations\"", + "Congratulations! We've added your Full Stack Development Certificate to your certificate to your portfolio page. Unless you choose to hide your solutions, this certificate will remain publicly visible and verifiable.", + "" ] ], "type": "Waypoint", diff --git a/server/boot/certificate.js b/server/boot/certificate.js index b6a3db9461..ba28cb8a16 100644 --- a/server/boot/certificate.js +++ b/server/boot/certificate.js @@ -91,8 +91,16 @@ export default function certificate(app) { debug('certified'); if (isFront) { user.isFrontEndCert = true; + user.completedChallenges.push({ + completedDate: new Date(), + id: frontEndChallangeId + }) } else { user.isFullStackCert = true; + user.completedChallenges.push({ + completedDate: new Date(), + id: fullStackChallangeId + }) } return saveUser(user); } diff --git a/server/boot/user.js b/server/boot/user.js index 9567a5066a..a7c6fe2978 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -275,7 +275,9 @@ module.exports = function(app) { !showFront && user.isFullStackCert ) { var { completedDate } = _.find(user.completedChallenges, { - id: '561add10cb82ac38a17513be' + id: showFront ? + '561add10cb82ac38a17513be' : + '660add10cb82ac38a17513be' }); return res.render( From 202d0fa0683e1351ceace18fe8bb129ede23026b Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 5 Oct 2015 19:58:44 -0700 Subject: [PATCH 06/14] Fix add to completed challenges on cert verify --- server/boot/certificate.js | 42 ++++++++++++++++++++----------- server/utils/constantStrings.json | 4 ++- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/server/boot/certificate.js b/server/boot/certificate.js index ba28cb8a16..b084d9f4b8 100644 --- a/server/boot/certificate.js +++ b/server/boot/certificate.js @@ -13,8 +13,11 @@ import { observeQuery } from '../utils/rx'; -const frontEndChallangeId = '561add10cb82ac38a17513be'; -const fullStackChallangeId = '660add10cb82ac38a17513be'; +import { + frontEndChallangeId, + fullStackChallangeId +} from '../utils/constantStrings.json'; + const debug = debugFactory('freecc:certification'); const sendMessageToNonUser = ifNoUserSend( 'must be logged in to complete.' @@ -36,10 +39,12 @@ export default function certificate(app) { 'findById', frontEndChallangeId, { - tests: true + id: true, + tests: true, + name: true, + challengeType: true } ) - .map(({ tests = [] }) => tests) .shareReplay(); const fullStackChallangeIds$ = observeQuery( @@ -47,10 +52,12 @@ export default function certificate(app) { 'findById', fullStackChallangeId, { - tests: true + id: true, + tests: true, + name: true, + challengeType: true } ) - .map(({ tests = [] }) => tests) .shareReplay(); router.post( @@ -82,8 +89,14 @@ export default function certificate(app) { } return fullStackChallangeIds$; }) - .flatMap((tests) => { + .flatMap(challenge => { const { user } = req; + const { + id, + tests, + name, + challengeType + } = challenge; if ( isFront && !user.isFrontEndCert && isCertified(tests, user) || !isFront && !user.isFullStackCert && isCertified(tests, user) @@ -91,17 +104,16 @@ export default function certificate(app) { debug('certified'); if (isFront) { user.isFrontEndCert = true; - user.completedChallenges.push({ - completedDate: new Date(), - id: frontEndChallangeId - }) } else { user.isFullStackCert = true; - user.completedChallenges.push({ - completedDate: new Date(), - id: fullStackChallangeId - }) } + + user.completedChallenges.push({ + id, + name, + completedDate: new Date(), + challengeType + }); return saveUser(user); } return Observable.just(user); diff --git a/server/utils/constantStrings.json b/server/utils/constantStrings.json index 70f5d9766c..a2f5b29df8 100644 --- a/server/utils/constantStrings.json +++ b/server/utils/constantStrings.json @@ -1,3 +1,5 @@ { - "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" + "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", + "frontEndChallangeId": "561add10cb82ac38a17513be", + "fullStackChallangeId": "660add10cb82ac38a17513be" } From 188da02ffcbf5a5709810495590888ed8f676325 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 5 Oct 2015 20:00:25 -0700 Subject: [PATCH 07/14] Fix disable cert Disable certificate when user is locked to the public or when they haven't signed academic honesty --- common/models/user.json | 2 +- server/boot/user.js | 27 +++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/common/models/user.json b/common/models/user.json index cb34389b80..776c2c500e 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -103,7 +103,7 @@ "isLocked": { "type": "boolean", "default": false, - "description": "Campers profile does not show challenges to the public" + "description": "Campers profile does not show challenges/certificates to the public" }, "currentChallenge": { "type": {} diff --git a/server/boot/user.js b/server/boot/user.js index a7c6fe2978..9ecbe288dd 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -4,6 +4,10 @@ import moment from 'moment'; import { Observable } from 'rx'; import debugFactory from 'debug'; +import { + frontEndChallangeId, + fullStackChallangeId +} from '../utils/constantStrings.json'; import { ifNoUser401, ifNoUserRedirectTo } from '../utils/middleware'; import { observeQuery } from '../utils/rx'; @@ -270,14 +274,33 @@ module.exports = function(app) { }); return res.redirect('/'); } + if (user.isLocked) { + req.flash('errors', { + msg: dedent` + Looks like user '${username}'s account is locked + down to the public. + ` + }); + return res.redirect('/'); + } + if (!user.isHonest) { + req.flash('errors', { + msg: dedent` + Looks like the user '${username}'s has not signed + the academic honesty pledge. + ` + }); + return res.redirect('/'); + } + if ( showFront && user.isFrontEndCert || !showFront && user.isFullStackCert ) { var { completedDate } = _.find(user.completedChallenges, { id: showFront ? - '561add10cb82ac38a17513be' : - '660add10cb82ac38a17513be' + frontEndChallangeId : + fullStackChallangeId }); return res.render( From 85f2c005cdf5c3cbf74b9ab187011337a8e7306a Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 5 Oct 2015 20:18:52 -0700 Subject: [PATCH 08/14] fix shareable challenges solution undefined During challenges when a user tries to navigate to a challenge and hits the name redirect, the solution is filled with undefined starting the user with an empty box. This PR fixes the issue by ignoring the solution param if it is empty --- server/boot/challenge.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 6a056b705d..dade963b1e 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -267,12 +267,13 @@ module.exports = function(app) { } if (dasherize(challenge.name) !== origChallengeName) { - return Observable.just( - '/challenges/' + - dasherize(challenge.name) + - '?solution=' + - encodeURIComponent(solutionCode) - ); + let redirectUrl = `/challenges/${dasherize(challenge.name)}`; + + if (solutionCode) { + redirectUrl += `?solution=${encodeURIComponent(solutionCode)}`; + } + + return Observable.just(redirectUrl); } // save user does nothing if user does not exist From 68b12caf485404eda634e850f5b6c7a0e0c77557 Mon Sep 17 00:00:00 2001 From: Quincy Larson Date: Fri, 2 Oct 2015 23:58:22 -0700 Subject: [PATCH 09/14] minor improvements to onboarding and create skeleton commit page --- seed/challenges/getting-started.json | 2 +- server/boot/user.js | 10 ++++++ server/views/account/commit.jade | 49 ++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 server/views/account/commit.jade diff --git a/seed/challenges/getting-started.json b/seed/challenges/getting-started.json index c35c3541f8..23b43e6b59 100644 --- a/seed/challenges/getting-started.json +++ b/seed/challenges/getting-started.json @@ -242,7 +242,7 @@ [ "", "", - "Free Code Camp will always be free. If you want to feel more motivated to earn our certificates faster, we encourage you to instead donate each month to a nonprofit.", + "Free Code Camp will always be free. If you want to feel more motivated to earn our certificates faster, we encourage you to instead pledge to donate to a nonprofit each day.", "" ] ], diff --git a/server/boot/user.js b/server/boot/user.js index a7c6fe2978..ae7accda41 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -80,6 +80,10 @@ module.exports = function(app) { router.post('/reset-password', postReset); router.get('/email-signup', getEmailSignup); router.get('/email-signin', getEmailSignin); + router.get( + '/commit', + commitToNonprofit + ); router.get( '/toggle-lockdown-mode', sendNonUserToMap, @@ -122,6 +126,12 @@ module.exports = function(app) { }); } + function commitToNonprofit(req, res) { + res.render('account/commit', { + title: 'Commit to a nonprofit. Commit to your goal.' + }); + } + function signout(req, res) { req.logout(); res.redirect('/'); diff --git a/server/views/account/commit.jade b/server/views/account/commit.jade new file mode 100644 index 0000000000..32629096df --- /dev/null +++ b/server/views/account/commit.jade @@ -0,0 +1,49 @@ +extends ../layout +block content + .panel.panel-info + .panel-body + h3.text-center Commit to your goal. Commit to a nonprofit. + .col-xs-12.col-sm-6.col-sm-offset-3 + h4 Step 1: Choose your goal + .radio + label + input(type='radio' id='front-end-development-certificate' name='goal') + | Front End Development Certificate (takes about 400 hours) + .radio + label + input(type='radio' id='full-stack-development-certificate' name='goal') + | Full Stack Development Certificate (takes about 800 hours) + .spacer + h4 Step 2: Choose your nonprofit + .row + .col-xs-12.col-sm-6 + a(href="http://i.imgur.com/U1CyEuA.jpg" data-lightbox="img-enlarge") + img.img-responsive(src='http://i.imgur.com/U1CyEuA.jpg' alt="Girl Develop It participants coding at tables.") + .radio + label + input(type='radio' id='girl-develop-it' name='nonprofit') + | Girl Develop It is a nonprofit that provides in-person classes for women to learn to code. + .col-xs-12.col-sm-6 + a(href="http://i.imgur.com/NERytFF.jpg" data-lightbox="img-enlarge") + img.img-responsive(src='http://i.imgur.com/NERytFF.jpg' alt="Vets in Tech participants standing together at a conference.") + .radio + label + input(type='radio' id='vets-in-tech' name='nonprofit') + | Vets in Tech is a nonprofit that helps veterans prepare for tech jobs. + + .spacer + h4 Step 3: Choose your monthly pledge + .radio + label + input(type='radio' id='10-dollar-pledge' name='pledge-amount') + | $10 per month + .radio + label + input(type='radio' id='50-dollar-pledge' name='pledge-amount') + | $50 per month + + .spacer + a.button.btn.btn-block.btn-primary(href='https://www.paypal.com/us/cgi-bin/webscr?cmd=_flow&SESSION=T3x0DY-bLMFXuhmjYZXs-BhmDoiXfuNh5BWad5VBcMomkkDSZY0b_-_W3HS&dispatch=5885d80a13c0db1f8e263663d3faee8d0b9dcb01a9b6dc564e45f62871326a5e') Commit + .button-spacer + a.button.btn.btn-block.btn-warning(href='/') Maybe later + .spacer From f02b3ffd4c59a50f7862efabaccb7aee08d1d13f Mon Sep 17 00:00:00 2001 From: Quincy Larson Date: Sat, 3 Oct 2015 08:32:25 -0700 Subject: [PATCH 10/14] update commit copy --- server/views/account/commit.jade | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/views/account/commit.jade b/server/views/account/commit.jade index 32629096df..4786369441 100644 --- a/server/views/account/commit.jade +++ b/server/views/account/commit.jade @@ -2,7 +2,9 @@ extends ../layout block content .panel.panel-info .panel-body - h3.text-center Commit to your goal. Commit to a nonprofit. + h3.text-center Commit to yourself. Commit to a nonprofit. + .col-xs-12.col-sm-6.col-sm-offset-3 + p Are you looking for a burst of motivation? Do you want to help nonprofits before you’re ready to code for them? You can do both by pledging a monthly donation to a nonprofit until you've earned either your Front End or Full Stack Development certificate. Join Commit below or click "maybe later". .col-xs-12.col-sm-6.col-sm-offset-3 h4 Step 1: Choose your goal .radio @@ -14,7 +16,7 @@ block content input(type='radio' id='full-stack-development-certificate' name='goal') | Full Stack Development Certificate (takes about 800 hours) .spacer - h4 Step 2: Choose your nonprofit + h4 Step 2: Choose one of our nonprofits .row .col-xs-12.col-sm-6 a(href="http://i.imgur.com/U1CyEuA.jpg" data-lightbox="img-enlarge") @@ -30,7 +32,6 @@ block content label input(type='radio' id='vets-in-tech' name='nonprofit') | Vets in Tech is a nonprofit that helps veterans prepare for tech jobs. - .spacer h4 Step 3: Choose your monthly pledge .radio From e16efc832cd949c40612bca72c0b4289340897e8 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 5 Oct 2015 21:13:11 -0700 Subject: [PATCH 11/14] Move commit to separate router --- server/boot/commit.js | 15 +++++++++++++++ server/boot/user.js | 10 ---------- .../{account/commit.jade => commit/index.jade} | 0 3 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 server/boot/commit.js rename server/views/{account/commit.jade => commit/index.jade} (100%) diff --git a/server/boot/commit.js b/server/boot/commit.js new file mode 100644 index 0000000000..a54dc19506 --- /dev/null +++ b/server/boot/commit.js @@ -0,0 +1,15 @@ +export default function commit(app) { + const router = app.loopback.Router(); + router.get( + '/commit', + commitToNonprofit + ); + + app.use(router); + + function commitToNonprofit(req, res) { + res.render('commit/', { + title: 'Commit to a nonprofit. Commit to your goal.' + }); + } +} diff --git a/server/boot/user.js b/server/boot/user.js index ae7accda41..a7c6fe2978 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -80,10 +80,6 @@ module.exports = function(app) { router.post('/reset-password', postReset); router.get('/email-signup', getEmailSignup); router.get('/email-signin', getEmailSignin); - router.get( - '/commit', - commitToNonprofit - ); router.get( '/toggle-lockdown-mode', sendNonUserToMap, @@ -126,12 +122,6 @@ module.exports = function(app) { }); } - function commitToNonprofit(req, res) { - res.render('account/commit', { - title: 'Commit to a nonprofit. Commit to your goal.' - }); - } - function signout(req, res) { req.logout(); res.redirect('/'); diff --git a/server/views/account/commit.jade b/server/views/commit/index.jade similarity index 100% rename from server/views/account/commit.jade rename to server/views/commit/index.jade From e6afda62c7354a83b00b293b66c22d482d22e695 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 5 Oct 2015 22:18:50 -0700 Subject: [PATCH 12/14] Add 5 dollar option to commit --- server/views/commit/index.jade | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/views/commit/index.jade b/server/views/commit/index.jade index 4786369441..ba15d37fc1 100644 --- a/server/views/commit/index.jade +++ b/server/views/commit/index.jade @@ -34,6 +34,10 @@ block content | Vets in Tech is a nonprofit that helps veterans prepare for tech jobs. .spacer h4 Step 3: Choose your monthly pledge + .radio + label + input(type='radio' id='5-dollar-pledge' name='pledge-amount') + | $5 per month .radio label input(type='radio' id='10-dollar-pledge' name='pledge-amount') From 9a0505f782dee6675b9f820593c10ca0eb3d63dd Mon Sep 17 00:00:00 2001 From: Quincy Larson Date: Mon, 5 Oct 2015 22:42:42 -0700 Subject: [PATCH 13/14] add github linking enforcement --- server/boot/user.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/server/boot/user.js b/server/boot/user.js index 9ecbe288dd..fa529cdd62 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -274,23 +274,32 @@ module.exports = function(app) { }); return res.redirect('/'); } + if (!user.isGithubCool) { + req.flash('errors', { + msg: dedent` + This user needs to link GitHub with their account + in order to display this certificate to the public. + ` + }); + return res.redirect('back'); + } if (user.isLocked) { req.flash('errors', { msg: dedent` - Looks like user '${username}'s account is locked - down to the public. + ${username} has chosen to hide their work from the public. + They need to unhide their work in order for this certificate to + be verifiable. ` }); - return res.redirect('/'); + return res.redirect('back'); } if (!user.isHonest) { req.flash('errors', { msg: dedent` - Looks like the user '${username}'s has not signed - the academic honesty pledge. + ${username} has not agreed to our Academic Honesty Pledge yet. ` }); - return res.redirect('/'); + return res.redirect('back'); } if ( From 7a31cdd9224fed8b4fe84f1ffdb7594e3f599979 Mon Sep 17 00:00:00 2001 From: ahstro Date: Tue, 6 Oct 2015 10:27:29 +0200 Subject: [PATCH 14/14] 'Regular Expressions'-waypoint improvements * Remove unnecessary `i`-flag from tests and test messages, e.g. `/\s+/gi` => `/\s+/g` * Remove dupliate backslashes from regex test messages, e.g. `/\\s+/g` => `/\s+/g` The two backslashes were probably just an accident caused by how multiple escapes are necessary with the current way tests are handled. The `i`-flag was just unnecessary since its purpose is to ignore the case of alphabetic characters. --- seed/challenges/basic-javascript.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/seed/challenges/basic-javascript.json b/seed/challenges/basic-javascript.json index c10af0def1..82a0430852 100644 --- a/seed/challenges/basic-javascript.json +++ b/seed/challenges/basic-javascript.json @@ -1039,7 +1039,7 @@ ], "tests":[ "assert(test === 2, 'message: Your RegEx should have found two numbers in the testString.');", - "assert(editor.getValue().match(/\\/\\\\d\\+\\//gi), 'message: You should be using the following expression /\\\\d+/gi to find the numbers in the testString.');" + "assert(editor.getValue().match(/\\/\\\\d\\+\\//g), 'message: You should be using the following expression /\\d+/g to find the numbers in the testString.');" ], "challengeSeed":[ "var test = (function() {", @@ -1047,7 +1047,7 @@ "", " // Only change code below this line.", "", - " var expression = /.+/gi;", + " var expression = /.+/g;", "", " // Only change code above this line.", " // We use this function to show you the value of your variable in your output box.", @@ -1069,7 +1069,7 @@ ], "tests":[ "assert(test === 7, 'message: Your RegEx should have found seven spaces in the testString.');", - "assert(editor.getValue().match(/\\/\\\\s\\+\\//gi), 'message: You should be using the following expression /\\\\s+/gi to find the spaces in the testString.');" + "assert(editor.getValue().match(/\\/\\\\s\\+\\//g), 'message: You should be using the following expression /\\s+/g to find the spaces in the testString.');" ], "challengeSeed":[ "var test = (function(){", @@ -1077,7 +1077,7 @@ "", " // Only change code below this line.", "", - " var expression = /.+/gi;", + " var expression = /.+/g;", "", " // Only change code above this line.", " // We use this function to show you the value of your variable in your output box.", @@ -1092,12 +1092,12 @@ "title": "Invert Regular Expression Matches with JavaScript", "difficulty":"9.987", "description":[ - "Use /\\S/gi to match everything that isn't a space in the string.", + "Use /\\S/g to match everything that isn't a space in the string.", "You can invert any match by using the uppercase version of the selector \\s versus \\S for example." ], "tests":[ "assert(test === 49, 'message: Your RegEx should have found forty nine non-space characters in the testString.');", - "assert(editor.getValue().match(/\\/\\\\S\\/gi/gi), 'message: You should be using the following expression /\\\\S/gi to find non-space characters in the testString.');" + "assert(editor.getValue().match(/\\/\\\\S\\/g/g), 'message: You should be using the following expression /\\S/g to find non-space characters in the testString.');" ], "challengeSeed":[ "var test = (function(){", @@ -1105,7 +1105,7 @@ "", " // Only change code below this line.", "", - " var expression = /./gi;", + " var expression = /./g;", "", " // Only change code above this line.", " // We use this function to show you the value of your variable in your output box.",