diff --git a/app.js b/app.js index 6243d927b6..d3581170de 100644 --- a/app.js +++ b/app.js @@ -1,19 +1,19 @@ require('newrelic'); +require('dotenv').load(); /** * Module dependencies. */ + var express = require('express'), debug = require('debug')('freecc:server'), cookieParser = require('cookie-parser'), compress = require('compression'), session = require('express-session'), - bodyParser = require('body-parser'), logger = require('morgan'), errorHandler = require('errorhandler'), methodOverride = require('method-override'), bodyParser = require('body-parser'), helmet = require('helmet'), - _ = require('lodash'), MongoStore = require('connect-mongo')(session), flash = require('express-flash'), @@ -99,15 +99,19 @@ app.use(helmet.xframe()); var trusted = [ '"self"', '*.freecodecamp.com', - '*.google-analytics.com', - '*.googleapis.com', '*.gstatic.com', - '*.doubleclick.net', - '*.twitter.com', + "*.google-analytics.com", + "*.googleapis.com", + "*.google.com", + "*.gstatic.com", + "*.doubleclick.net", + "*.twitter.com", '*.twimg.com', - '*.githubusercontent.com', - '"unsafe-eval"', - '"unsafe-inline"' + "*.githubusercontent.com", + "'unsafe-eval'", + "'unsafe-inline'", + "*.rafflecopter.com", + "localhost:3001" ]; //TODO(Berks): conditionally add localhost domains to csp; /*var connectSrc; @@ -123,14 +127,12 @@ debug(trusted); app.use(helmet.contentSecurityPolicy({ defaultSrc: trusted, scriptSrc: ['*.optimizely.com'].concat(trusted), - 'connect-src': ['ws://localhost:3001/', 'http://localhost:3001/'], + 'connect-src': ['ws://*.rafflecopter.com', 'wss://*.rafflecopter.com','https://*.rafflecopter.com', 'ws://www.freecodecamp.com', 'ws://localhost:3001/', 'http://localhost:3001', 'http://www.freecodecamp.com'], styleSrc: trusted, - imgSrc: ['*.evernote.com', '*.amazonaws.com', 'data:'].concat(trusted), - fontSrc: ['"self"', '*.googleapis.com'].concat(trusted), + imgSrc: ['*.evernote.com', '*.amazonaws.com', "data:", '*.licdn.com', '*.gravatar.com', '*.youtube.com'].concat(trusted), + fontSrc: ["'self", '*.googleapis.com'].concat(trusted), mediaSrc: ['*.amazonaws.com', '*.twitter.com'], - frameSrc: ['*.gitter.im', '*.vimeo.com', '*.twitter.com'], -// sandbox: ['allow-forms', 'allow-scripts'], -// reportUri: '/report-violation', + frameSrc: ['*.gitter.im', '*.vimeo.com', '*.twitter.com', '*.rafflecopter.com'], reportOnly: false, // set to true if you only want to report errors setAllHeaders: false, // set to true if you want to set all headers safari5: false // set to true if you want to force buggy CSP in Safari 5 @@ -207,11 +209,9 @@ app.post( ); app.get( - '/challenges/:challengeNumber', - passportConf.isAuthenticated, - challengesController.returnChallenge + '/challenges/:challengeNumber', + challengesController.returnChallenge ); - app.all('/account', passportConf.isAuthenticated); app.get('/account', userController.getAccount); app.post('/account/profile', userController.postUpdateProfile); diff --git a/controllers/api.js b/controllers/api.js new file mode 100644 index 0000000000..41b38606e5 --- /dev/null +++ b/controllers/api.js @@ -0,0 +1,137 @@ +var secrets = require('../config/secrets'); +var User = require('../models/User'); +var querystring = require('querystring'); +var validator = require('validator'); +var async = require('async'); +var cheerio = require('cheerio'); +var request = require('request'); +var graph = require('fbgraph'); +var Github = require('github-api'); +var Twit = require('twit'); +var Linkedin = require('node-linkedin')(secrets.linkedin.clientID, secrets.linkedin.clientSecret, secrets.linkedin.callbackURL); +var Y = require('yui/yql'); +var _ = require('lodash'); + +/** + * GET /api + * List of API examples. + */ + +exports.getApi = function(req, res) { + res.render('api/index', { + title: 'API Examples' + }); +}; + +/** + * GET /api/facebook + * Facebook API example. + */ + +exports.getFacebook = function(req, res, next) { + var token = _.find(req.user.tokens, { kind: 'facebook' }); + graph.setAccessToken(token.accessToken); + async.parallel({ + getMe: function(done) { + graph.get(req.user.facebook, function(err, me) { + done(err, me); + }); + }, + getMyFriends: function(done) { + graph.get(req.user.facebook + '/friends', function(err, friends) { + done(err, friends.data); + }); + } + }, + function(err, results) { + if (err) return next(err); + res.render('api/facebook', { + title: 'Facebook API', + me: results.getMe, + friends: results.getMyFriends + }); + }); +}; + +/** + * GET /api/github + * GitHub API Example. + */ + +exports.getGithub = function(req, res, next) { + var token = _.find(req.user.tokens, { kind: 'github' }); + var github = new Github({ token: token.accessToken }); + var repo = github.getRepo('sahat', 'requirejs-library'); + repo.show(function(err, repo) { + if (err) return next(err); + res.render('api/github', { + title: 'GitHub API', + repo: repo + }); + }); +}; + +/** + * GET /api/twitter + * Twiter API example. + */ + +exports.getTwitter = function(req, res, next) { + var token = _.find(req.user.tokens, { kind: 'twitter' }); + var T = new Twit({ + consumer_key: secrets.twitter.consumerKey, + consumer_secret: secrets.twitter.consumerSecret, + access_token: token.accessToken, + access_token_secret: token.tokenSecret + }); + T.get('search/tweets', { q: 'nodejs since:2013-01-01', geocode: '40.71448,-74.00598,5mi', count: 10 }, function(err, reply) { + if (err) return next(err); + res.render('api/twitter', { + title: 'Twitter API', + tweets: reply.statuses + }); + }); +}; + +/** + * POST /api/twitter + * Post a tweet. + */ + +exports.postTwitter = function(req, res, next) { + req.assert('tweet', 'Tweet cannot be empty.').notEmpty(); + var errors = req.validationErrors(); + if (errors) { + req.flash('errors', errors); + return res.redirect('/api/twitter'); + } + var token = _.find(req.user.tokens, { kind: 'twitter' }); + var T = new Twit({ + consumer_key: secrets.twitter.consumerKey, + consumer_secret: secrets.twitter.consumerSecret, + access_token: token.accessToken, + access_token_secret: token.tokenSecret + }); + T.post('statuses/update', { status: req.body.tweet }, function(err, data, response) { + if (err) return next(err); + req.flash('success', { msg: 'Tweet has been posted.'}); + res.redirect('/api/twitter'); + }); +}; + +/** + * GET /api/linkedin + * LinkedIn API example. + */ + +exports.getLinkedin = function(req, res, next) { + var token = _.find(req.user.tokens, { kind: 'linkedin' }); + var linkedin = Linkedin.init(token.accessToken); + linkedin.people.me(function(err, $in) { + if (err) return next(err); + res.render('api/linkedin', { + title: 'LinkedIn API', + profile: $in + }); + }); +}; \ No newline at end of file diff --git a/controllers/challenges.js b/controllers/challenges.js index 841b4941e9..73c114f535 100644 --- a/controllers/challenges.js +++ b/controllers/challenges.js @@ -7,24 +7,49 @@ var _ = require('lodash'), Challenge = require('./../models/Challenge'); exports.returnChallenge = function(req, res, next) { - var challengeNumber = parseInt(req.params.challengeNumber) || 0; - - if (challengeNumber > 59) { - challengeNumber = 0; - } - - Challenge.findOne({ challengeNumber: challengeNumber }, function(err, c) { - if (err) { - debug('Challenge err: ', err); - return next(err); - } - res.render('challenges/show', { - title: 'Challenge: ' + c.name, - name: c.name, - video: c.video, - time: c.time, - steps: c.steps, - cc: req.user.challengesHash + var challengeNumber = parseInt(req.params.challengeNumber) || 0; + var verbs = [ + 'ACED', + 'NAILED', + 'ROCKED', + 'SCORCHED', + 'DEVASTATED', + 'OWNED', + 'CRUSHED', + 'CONQUERED', + 'KILLED', + 'SHREDDED', + 'ANNIHILATED', + 'NUKED' + ]; + var phrases = [ + "Shout it from on top of a mountain", + "Tell everyone and their dogs", + "Show them. Show them all!", + "Inspire your friends", + "Tell the world of your greatness", + "Look accomplished on social media", + "Share news of your grand endeavor", + "Establish your alibi for the past two hours", + "Prove to mom that computers aren't just for games" + ]; + if (challengeNumber > 59) { challengeNumber = 0; } + Challenge.find({}, function (err, c) { + if (err) { + debug('Challenge err: ', err); + next(err); + } + res.render('challenges/show', { + title: 'Challenge: ' + c[challengeNumber].name, + name: c[challengeNumber].name, + video: c[challengeNumber].video, + time: c[challengeNumber].time, + steps: c[challengeNumber].steps, + number: challengeNumber, + cc: req.user ? req.user.challengesHash : undefined, + verb: verbs[Math.floor(Math.random() * verbs.length)], + phrase: phrases[Math.floor(Math.random() * phrases.length)], + challenges: c + }); }); - }); }; diff --git a/controllers/user.js b/controllers/user.js index 45dc0fc6a7..391de6d4b4 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -6,6 +6,7 @@ var passport = require('passport'); var User = require('../models/User'); var secrets = require('../config/secrets'); var moment = require('moment'); +var Challenge = require('./../models/Challenge'); //TODO(Berks): Refactor to use module.exports = {} pattern. /** @@ -133,10 +134,17 @@ exports.postEmailSignup = function(req, res, next) { */ exports.getAccount = function(req, res) { - res.render('account/profile', { - title: 'Manage your Free Code Camp Account', - cc: req.user.challengesHash, - moment: moment + Challenge.find({}, function (err, c) { + if (err) { + console.error('Challenge err: ', err); + next(err); + } + res.render('account/profile', { + title: 'Manage your Free Code Camp Account', + cc: c, + ch: req.user.challengesHash, + moment: moment + }); }); }; diff --git a/seed_data/challenges.json b/seed_data/challenges.json index 77161bfa46..c85a0eca54 100644 --- a/seed_data/challenges.json +++ b/seed_data/challenges.json @@ -36,7 +36,7 @@ { "name": "Join our Forum", "time": 5, - "video": "114578442", + "video": "115275066", "challengeNumber": 2, "steps": [ "Go to Free Code Camp's forum: http://forum.freecodecamp.com.", @@ -46,7 +46,7 @@ "Click on the \"Introduce yourself here\" discussion.", "Here you can read through other Free Code Camp community members' self introductions.", "Go ahead and type a brief self introduction of your own.", - "Click on the \"Categories\" drop-down menu. You should see a category called \"Local Chapters\". Click that. If your city isn't already on the list, you can create a topic for it. Otherwise, you can also introduce yourself to the other Code Campers from your city.", + "Click on the \"Categories\" drop-down menu. You should see a category called \"Local Chapters\". Click that. If your city isn't already on the list, create a topic for it. Otherwise, introduce yourself to the other Code Campers from your city.", "Come back here daily to ask questions, engage in discussions, and share links to helpful coding tools." ] }, @@ -192,8 +192,8 @@ ] }, { - "name": "Customize Bootstrap with Bootswatch", - "time": 10, + "name": "Customize Bootstrap", + "time": 15, "video": "110752741", "challengeNumber": 16, "steps": [ diff --git a/views/account/profile.jade b/views/account/profile.jade index 4f883901cb..422cb6f5b5 100644 --- a/views/account/profile.jade +++ b/views/account/profile.jade @@ -40,246 +40,12 @@ block content tr th Challenge th Date Finished - if cc[0] > 0 - tr - td Learn how Free Code Camp Works - td=moment(cc[0], 'X').format("MMM DD, YYYY") - if cc[1] > 0 - tr - td Join Our Chat Room - td=moment(cc[1], 'X').format("MMM DD, YYYY") - if cc[2] > 0 - tr - td Subscribe to Our Subreddit - td=moment(cc[2], 'X').format("MMM DD, YYYY") - if cc[3] > 0 - tr - td Build a Personal Website - td=moment(cc[3], 'X').format("MMM DD, YYYY") - if cc[4] > 0 - tr - td Build a Responsive Blog Theme - td=moment(cc[4], 'X').format("MMM DD, YYYY") - if cc[5] > 0 - tr - td Build a Small Business Website - td=moment(cc[5], 'X').format("MMM DD, YYYY") - if cc[6] > 0 - tr - td Tweak HTML and CSS in CodePen - td=moment(cc[6], 'X').format("MMM DD, YYYY") - if cc[7] > 0 - tr - td Build a CSS Robot - td=moment(cc[7], 'X').format("MMM DD, YYYY") - if cc[8] > 0 - tr - td Get Started with jQuery - td=moment(cc[8], 'X').format("MMM DD, YYYY") - if cc[9] > 0 - tr - td Traverse the DOM - td=moment(cc[9], 'X').format("MMM DD, YYYY") - if cc[10] > 0 - tr - td Work with the DOM - td=moment(cc[10], 'X').format("MMM DD, YYYY") - if cc[11] > 0 - tr - td Listen for DOM Events - td=moment(cc[11], 'X').format("MMM DD, YYYY") - if cc[12] > 0 - tr - td Use jQuery for Styling - td=moment(cc[12], 'X').format("MMM DD, YYYY") - if cc[13] > 0 - tr - td Build a MadLibs Game - td=moment(cc[13], 'X').format("MMM DD, YYYY") - if cc[14] > 0 - tr - td Discover Chrome's DevTools - td=moment(cc[14], 'X').format("MMM DD, YYYY") - if cc[15] > 0 - tr - td Tackle jQuery Exercises - td=moment(cc[15], 'X').format("MMM DD, YYYY") - if cc[16] > 0 - tr - td Customize Bootstrap - td=moment(cc[16], 'X').format("MMM DD, YYYY") - if cc[17] > 0 - tr - td Inject Animation into CSS - td=moment(cc[17], 'X').format("MMM DD, YYYY") - if cc[18] > 0 - tr - td Learn Basic Computer Science - td=moment(cc[18], 'X').format("MMM DD, YYYY") - if cc[19] > 0 - tr - td Learn Loops - td=moment(cc[19], 'X').format("MMM DD, YYYY") - if cc[20] > 0 - tr - td Learn Computer Hardware - td=moment(cc[20], 'X').format("MMM DD, YYYY") - if cc[21] > 0 - tr - td Learn Computer Networking - td=moment(cc[21], 'X').format("MMM DD, YYYY") - if cc[22] > 0 - tr - td Learn Boolean Logic - td=moment(cc[22], 'X').format("MMM DD, YYYY") - if cc[23] > 0 - tr - td Learn Computer Security - td=moment(cc[23], 'X').format("MMM DD, YYYY") - if cc[24] > 0 - tr - td Build an Adventure Game - td=moment(cc[24], 'X').format("MMM DD, YYYY") - if cc[25] > 0 - tr - td Build Rock Paper Scissors - td=moment(cc[25], 'X').format("MMM DD, YYYY") - if cc[26] > 0 - tr - td Learn JavaScript For Loops - td=moment(cc[26], 'X').format("MMM DD, YYYY") - if cc[27] > 0 - tr - td Learn JavaScript While Loops - td=moment(cc[27], 'X').format("MMM DD, YYYY") - if cc[28] > 0 - tr - td Learn Control Flow - td=moment(cc[28], 'X').format("MMM DD, YYYY") - if cc[29] > 0 - tr - td Build a Contact List - td=moment(cc[29], 'X').format("MMM DD, YYYY") - if cc[30] > 0 - tr - td Build an Address Book - td=moment(cc[30], 'X').format("MMM DD, YYYY") - if cc[31] > 0 - tr - td Build a Cash Register - td=moment(cc[31], 'X').format("MMM DD, YYYY") - if cc[32] > 0 - tr - td Get Help the Hacker Way - td=moment(cc[32], 'X').format("MMM DD, YYYY") - if cc[33] > 0 - tr - td Learn Regular Expressions - td=moment(cc[33], 'X').format("MMM DD, YYYY") - if cc[34] > 0 - tr - td Pair Program on CoderByte - td=moment(cc[34], 'X').format("MMM DD, YYYY") - if cc[35] > 0 - tr - td Learn Relational Database Theory - td=moment(cc[35], 'X').format("MMM DD, YYYY") - if cc[36] > 0 - tr - td Pair Program to Query SQL pt 1 - td=moment(cc[36], 'X').format("MMM DD, YYYY") - if cc[37] > 0 - tr - td Pair Program to Query SQL pt 2 - td=moment(cc[37], 'X').format("MMM DD, YYYY") - if cc[38] > 0 - tr - td Pair Program to modify SQL pt 1 - td=moment(cc[38], 'X').format("MMM DD, YYYY") - if cc[39] > 0 - tr - td Pair Program to modify SQL pt 2 - td=moment(cc[39], 'X').format("MMM DD, YYYY") - if cc[40] > 0 - tr - td Learn JSON - td=moment(cc[40], 'X').format("MMM DD, YYYY") - if cc[41] > 0 - tr - td Manage Source Code with Git - td=moment(cc[41], 'X').format("MMM DD, YYYY") - if cc[42] > 0 - tr - td Get Started with Node.js - td=moment(cc[42], 'X').format("MMM DD, YYYY") - if cc[43] > 0 - tr - td Try Node.js Events - td=moment(cc[43], 'X').format("MMM DD, YYYY") - if cc[44] > 0 - tr - td Try Node.js Streams - td=moment(cc[44], 'X').format("MMM DD, YYYY") - if cc[45] > 0 - tr - td Learn how Node.js Modules Work - td=moment(cc[45], 'X').format("MMM DD, YYYY") - if cc[46] > 0 - tr - td Start an Express.js Server - td=moment(cc[46], 'X').format("MMM DD, YYYY") - if cc[47] > 0 - tr - td Use Socket.io - td=moment(cc[47], 'X').format("MMM DD, YYYY") - if cc[48] > 0 - tr - td Use Redis to Persist Data - td=moment(cc[48], 'X').format("MMM DD, YYYY") - if cc[49] > 0 - tr - td Dive Deeper into Express.js - td=moment(cc[49], 'X').format("MMM DD, YYYY") - if cc[50] > 0 - tr - td Set up Express.js Middleware - td=moment(cc[50], 'X').format("MMM DD, YYYY") - if cc[51] > 0 - tr - td Take Advantage of Parameters - td=moment(cc[51], 'X').format("MMM DD, YYYY") - if cc[52] > 0 - tr - td Add the Body Parser - td=moment(cc[52], 'X').format("MMM DD, YYYY") - if cc[53] > 0 - tr - td Configure Routes in Express.js - td=moment(cc[53], 'X').format("MMM DD, YYYY") - if cc[54] > 0 - tr - td Try MongoDB - td=moment(cc[54], 'X').format("MMM DD, YYYY") - if cc[55] > 0 - tr - td Get Started with Angular.js - td=moment(cc[55], 'X').format("MMM DD, YYYY") - if cc[56] > 0 - tr - td Apply Angular.js Directives - td=moment(cc[56], 'X').format("MMM DD, YYYY") - if cc[57] > 0 - tr - td Power Forms with Angular.js - td=moment(cc[57], 'X').format("MMM DD, YYYY") - if cc[58] > 0 - tr - td Customize Angular.js Directives - td=moment(cc[58], 'X').format("MMM DD, YYYY") - if cc[59] > 0 - tr - td Create Angular.js Services - td=moment(cc[59], 'X').format("MMM DD, YYYY") + for challenge in cc + if ch[challenge.challengeNumber] > 0 + tr + td= cc[challenge.challengeNumber].name + td= moment(ch[challenge.challengeNumber], 'X').format("MMM DD, YYYY") + h3 Danger Zone button.btn.btn-danger.confirm-deletion span.ion-trash-b diff --git a/views/challenges/show.jade b/views/challenges/show.jade index 902730d664..2af4860aa5 100644 --- a/views/challenges/show.jade +++ b/views/challenges/show.jade @@ -15,7 +15,7 @@ block content .btn.btn-primary.btn-big.btn-block.completed-challenge I've completed this challenge .ten-pixel-break .btn.btn-success.btn-large.btn-block.skip-challenge I want to skip this challenge for now - - if (cc[2] > 0) + - if (cc && cc[2] > 0) .panel-footer.text-center span Need a break? Check out our:  a(href="https://gitter.im/FreeCodeCamp/FreeCodeCamp", target="_blank") Chat Room @@ -34,6 +34,11 @@ block content .animated.zoomInUp.delay-1 span.landing-icon.ion-checkmark-circled.text-primary a.animated.fadeIn.delay-2.btn.btn-lg.btn-primary.btn-block.next-button(name='_csrf', value=_csrf, aria-hidden='true') Take me to my next challenge + if (cc && cc[2] > 0) + a.animated.fadeIn.delay-2.btn-twitter.btn.btn-lg.btn-info.btn-block(href="https://twitter.com/intent/tweet?text=I%20just%20#{verb}%20Free%20Code%20Camp%20Challenge%20%23#{number}:%20#{name}&url=http%3A%2F%2Ffreecodecamp.com/challenges/#{number}&hashtags=learntocode, javascript") + i.fa.fa-twitter   + = phrase + #skip-dialog.modal .modal-dialog.animated.zoomIn.fast-animation .modal-content diff --git a/views/partials/challenges.jade b/views/partials/challenges.jade index a4d259ae79..de9fd2c3c6 100644 --- a/views/partials/challenges.jade +++ b/views/partials/challenges.jade @@ -2,185 +2,9 @@ .panel-heading Challenges .panel-body ol(start='0') + for challenge in challenges li - a(href="/challenges/0", class="#{ cc[0] > 0 ? 'strikethrough' : '' }") Learn how Free Code Camp Works - |   (2 minutes) - li - a(href="/challenges/1", class="#{ cc[1] > 0 ? 'strikethrough' : '' }") Join Our Chat Room - |   (5 minutes) - li - a(href="/challenges/2", class="#{ cc[2] > 0 ? 'strikethrough' : '' }") Subscribe to Our Subreddit - |   (5 minutes) - li - a(href="/challenges/3", class="#{ cc[3] > 0 ? 'strikethrough' : '' }") Build a Personal Website - |   (60 minutes) - li - a(href="/challenges/4", class="#{ cc[4] > 0 ? 'strikethrough' : '' }") Build a Responsive Blog Theme - |   (60 minutes) - li - a(href="/challenges/5", class="#{ cc[5] > 0 ? 'strikethrough' : '' }") Build a Small Business Website - |   (60 minutes) - li - a(href="/challenges/6", class="#{ cc[6] > 0 ? 'strikethrough' : '' }") Tweak HTML and CSS in CodePen - |   (10 minutes) - li - a(href="/challenges/7", class="#{ cc[7] > 0 ? 'strikethrough' : '' }") Build a CSS Robot - |   (60 minutes) - li - a(href="/challenges/8", class="#{ cc[8] > 0 ? 'strikethrough' : '' }") Get Started with jQuery - |   (30 minutes) - li - a(href="/challenges/9", class="#{ cc[9] > 0 ? 'strikethrough' : '' }") Traverse the DOM - |   (30 minutes) - li - a(href="/challenges/10", class="#{ cc[10] > 0 ? 'strikethrough' : '' }") Work with the DOM - |   (30 minutes) - li - a(href="/challenges/11", class="#{ cc[11] > 0 ? 'strikethrough' : '' }") Listen for DOM Events - |   (30 minutes) - li - a(href="/challenges/12", class="#{ cc[12] > 0 ? 'strikethrough' : '' }") Use jQuery for Styling - |   (30 minutes) - li - a(href="/challenges/13", class="#{ cc[13] > 0 ? 'strikethrough' : '' }") Build a MadLibs Game - |   (60 minutes) - li - a(href="/challenges/14", class="#{ cc[14] > 0 ? 'strikethrough' : '' }") Discover Chrome's DevTools - |   (90 minutes) - li - a(href="/challenges/15", class="#{ cc[15] > 0 ? 'strikethrough' : '' }") Tackle jQuery Exercises - |   (60 minutes) - li - a(href="/challenges/16", class="#{ cc[16] > 0 ? 'strikethrough' : '' }") Customize Bootstrap - |   (10 minutes) - li - a(href="/challenges/17", class="#{ cc[17] > 0 ? 'strikethrough' : '' }") Inject Animation into CSS - |   (15 minutes) - li - a(href="/challenges/18", class="#{ cc[18] > 0 ? 'strikethrough' : '' }") Learn Basic Computer Science - |   (120 minutes) - li - a(href="/challenges/19", class="#{ cc[19] > 0 ? 'strikethrough' : '' }") Learn Loops - |   (120 minutes) - li - a(href="/challenges/20", class="#{ cc[20] > 0 ? 'strikethrough' : '' }") Learn Computer Hardware - |   (120 minutes) - li - a(href="/challenges/21", class="#{ cc[21] > 0 ? 'strikethrough' : '' }") Learn Computer Networking - |   (120 minutes) - li - a(href="/challenges/22", class="#{ cc[22] > 0 ? 'strikethrough' : '' }") Learn Boolean Logic - |   (120 minutes) - li - a(href="/challenges/23", class="#{ cc[23] > 0 ? 'strikethrough' : '' }") Learn Computer Security - |   (120 minutes) - li - a(href="/challenges/24", class="#{ cc[24] > 0 ? 'strikethrough' : '' }") Build an Adventure Game - |   (60 minutes) - li - a(href="/challenges/25", class="#{ cc[25] > 0 ? 'strikethrough' : '' }") Build Rock Paper Scissors - |   (60 minutes) - li - a(href="/challenges/26", class="#{ cc[26] > 0 ? 'strikethrough' : '' }") Learn JavaScript For Loops - |   (60 minutes) - li - a(href="/challenges/27", class="#{ cc[27] > 0 ? 'strikethrough' : '' }") Learn JavaScript While Loops - |   (60 minutes) - li - a(href="/challenges/28", class="#{ cc[28] > 0 ? 'strikethrough' : '' }") Learn Control Flow - |   (60 minutes) - li - a(href="/challenges/29", class="#{ cc[29] > 0 ? 'strikethrough' : '' }") Build a Contact List - |   (60 minutes) - li - a(href="/challenges/30", class="#{ cc[30] > 0 ? 'strikethrough' : '' }") Build an Address Book - |   (60 minutes) - li - a(href="/challenges/31", class="#{ cc[31] > 0 ? 'strikethrough' : '' }") Build a Cash Register - |   (60 minutes) - li - a(href="/challenges/32", class="#{ cc[32] > 0 ? 'strikethrough' : '' }") Get Help the Hacker Way - |   (30 minutes) - li - a(href="/challenges/33", class="#{ cc[33] > 0 ? 'strikethrough' : '' }") Learn Regular Expressions - |   (60 minutes) - li - a(href="/challenges/34", class="#{ cc[34] > 0 ? 'strikethrough' : '' }") Pair Program on CoderByte - |   (60 minutes) - li - a(href="/challenges/35", class="#{ cc[35] > 0 ? 'strikethrough' : '' }") Learn Relational Database Theory - |   (30 minutes) - li - a(href="/challenges/36", class="#{ cc[36] > 0 ? 'strikethrough' : '' }") Pair Program to Query SQL pt 1 - |   (90 minutes) - li - a(href="/challenges/37", class="#{ cc[37] > 0 ? 'strikethrough' : '' }") Pair Program to Query SQL pt 2 - |   (60 minutes) - li - a(href="/challenges/38", class="#{ cc[38] > 0 ? 'strikethrough' : '' }") Pair Program to modify SQL pt 1 - |   (90 minutes) - li - a(href="/challenges/39", class="#{ cc[39] > 0 ? 'strikethrough' : '' }") Pair Program to modify SQL pt 2 - |   (60 minutes) - li - a(href="/challenges/40", class="#{ cc[40] > 0 ? 'strikethrough' : '' }") Learn JSON - |   (30 minutes) - li - a(href="/challenges/41", class="#{ cc[41] > 0 ? 'strikethrough' : '' }") Manage Source Code with Git - |   (30 minutes) - li - a(href="/challenges/42", class="#{ cc[42] > 0 ? 'strikethrough' : '' }") Get Started with Node.js - |   (45 minutes) - li - a(href="/challenges/43", class="#{ cc[43] > 0 ? 'strikethrough' : '' }") Try Node.js Events - |   (45 minutes) - li - a(href="/challenges/44", class="#{ cc[44] > 0 ? 'strikethrough' : '' }") Try Node.js Streams - |   (45 minutes) - li - a(href="/challenges/45", class="#{ cc[45] > 0 ? 'strikethrough' : '' }") Learn how Node.js Modules Work - |   (45 minutes) - li - a(href="/challenges/46", class="#{ cc[46] > 0 ? 'strikethrough' : '' }") Start an Express.js Server - |   (45 minutes) - li - a(href="/challenges/47", class="#{ cc[47] > 0 ? 'strikethrough' : '' }") Use Socket.io - |   (45 minutes) - li - a(href="/challenges/48", class="#{ cc[48] > 0 ? 'strikethrough' : '' }") Use Redis to Persist Data - |   (45 minutes) - li - a(href="/challenges/49", class="#{ cc[49] > 0 ? 'strikethrough' : '' }") Dive Deeper into Express.js - |   (45 minutes) - li - a(href="/challenges/50", class="#{ cc[50] > 0 ? 'strikethrough' : '' }") Set up Express.js Middleware - |   (45 minutes) - li - a(href="/challenges/51", class="#{ cc[51] > 0 ? 'strikethrough' : '' }") Take Advantage of Parameters - |   (45 minutes) - li - a(href="/challenges/52", class="#{ cc[52] > 0 ? 'strikethrough' : '' }") Add the Body Parser - |   (45 minutes) - li - a(href="/challenges/53", class="#{ cc[53] > 0 ? 'strikethrough' : '' }") Configure Routes in Express.js - |   (45 minutes) - li - a(href="/challenges/54", class="#{ cc[54] > 0 ? 'strikethrough' : '' }") Try MongoDB - |   (30 minutes) - li - a(href="/challenges/55", class="#{ cc[55] > 0 ? 'strikethrough' : '' }") Get Started with Angular.js - |   (45 minutes) - li - a(href="/challenges/56", class="#{ cc[56] > 0 ? 'strikethrough' : '' }") Apply Angular.js Directives - |   (45 minutes) - li - a(href="/challenges/57", class="#{ cc[57] > 0 ? 'strikethrough' : '' }") Power Forms with Angular.js - |   (45 minutes) - li - a(href="/challenges/58", class="#{ cc[58] > 0 ? 'strikethrough' : '' }") Customize Angular.js Directives - |   (45 minutes) - li - a(href="/challenges/59", class="#{ cc[59] > 0 ? 'strikethrough' : '' }") Create Angular.js Services - |   (45 minutes) + a(href="/challenges/#{challenge.challengeNumber}", class="#{ (cc && cc[challenge.challengeNumber] > 0) ? 'strikethrough' : '' }") #{challenge.name} + |   (#{challenge.time} mins) - a.btn.btn-primary(href="/done-with-first-100-hours", class="#{ cc[59] > 0 ? '' : 'disabled' }") I've finished all Free Code Camp challenges and all the Easy and Medium CoderByte Challenges \ No newline at end of file + a.btn.btn-primary(href="/done-with-first-100-hours", class="#{ ((cc && cc[59] === 0) || (!cc)) ? 'disabled' : '' }") I've finished all Free Code Camp challenges and all the Easy and Medium CoderByte Challenges \ No newline at end of file diff --git a/views/partials/navbar.jade b/views/partials/navbar.jade index ea6aeeaf43..6c406569f4 100644 --- a/views/partials/navbar.jade +++ b/views/partials/navbar.jade @@ -27,26 +27,26 @@ a(href='/') span.ion-map | My Challenges - - if (cc[1] > 0) - li.hamburger - a(href='https://gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') - span.ion-coffee - | Chat Room - - else + - if (cc && cc[1] < 1) li.hamburger.disabled a(href='https://gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') span.ion-coffee | Chat Room (do Challenge 1 first) - - if (cc[2] > 0) - li.hamburger - a(href='http://forum.freecodecamp.com/', target='_blank') - span.ion-planet - | Forum - else + li.hamburger + a(href='https://gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') + span.ion-coffee + | Chat Room + - if (cc && cc[2] < 1) li.hamburger.disabled a(href='http://forum.freecodecamp.com/', target='_blank') span.ion-planet | Forum (do Challenge 2 first) + - else + li.hamburger + a(href='http://forum.freecodecamp.com/', target='_blank') + span.ion-planet + | Forum li.hamburger a(href='/account') span.ion-person