From b2462e1b4c6eb3ed8fd3fd2ce97a83f70dc7cce5 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 7 Apr 2016 13:00:51 -0700 Subject: [PATCH 1/5] Fix sitemap Add 24 hour cache Add jobs to sitemap Possible warning about to many eventemitter listeners --- server/boot/randomAPIs.js | 136 ++-------------------------- server/boot/sitemap.js | 64 +++++++++++++ server/utils/rx.js | 11 ++- server/views/resources/sitemap.jade | 21 ++--- 4 files changed, 89 insertions(+), 143 deletions(-) create mode 100644 server/boot/sitemap.js diff --git a/server/boot/randomAPIs.js b/server/boot/randomAPIs.js index 2995082fb2..3fd3f953bd 100644 --- a/server/boot/randomAPIs.js +++ b/server/boot/randomAPIs.js @@ -1,24 +1,15 @@ -var Rx = require('rx'), - async = require('async'), - moment = require('moment'), - request = require('request'), - debug = require('debug')('fcc:cntr:resources'), - constantStrings = require('../utils/constantStrings.json'), - labs = require('../resources/labs.json'), - testimonials = require('../resources/testimonials.json'), - secrets = require('../../config/secrets'); +import request from 'request'; +import constantStrings from '../utils/constantStrings.json'; +import labs from '../resources/labs.json'; +import testimonials from '../resources/testimonials.json'; +import secrets from '../../config/secrets'; module.exports = function(app) { - var router = app.loopback.Router(); - var User = app.models.User; - var Challenge = app.models.Challenge; - var Story = app.models.Story; - var Nonprofit = app.models.Nonprofit; - + const router = app.loopback.Router(); + const User = app.models.User; router.get('/api/github', githubCalls); router.get('/api/blogger', bloggerCalls); router.get('/api/trello', trelloCalls); - router.get('/sitemap.xml', sitemap); router.get('/chat', chat); router.get('/coding-bootcamp-cost-calculator', bootcampCalculator); router.get('/twitch', twitch); @@ -43,7 +34,6 @@ module.exports = function(app) { router.get('/how-nonprofit-projects-work', howNonprofitProjectsWork); router.get('/code-of-conduct', codeOfConduct); router.get('/academic-honesty', academicHonesty); - router.get( '/the-fastest-web-page-on-the-internet', theFastestWebPageOnTheInternet @@ -51,118 +41,6 @@ module.exports = function(app) { app.use(router); - function sitemap(req, res, next) { - var appUrl = 'http://www.freecodecamp.com'; - var now = moment(new Date()).format('YYYY-MM-DD'); - - // TODO(berks): refactor async to rx - async.parallel({ - users: function(callback) { - User.find( - { - where: { username: { nlike: '' } }, - fields: { username: true } - }, - function(err, users) { - if (err) { - debug('User err: ', err); - callback(err); - } else { - Rx.Observable.from(users, null, null, Rx.Scheduler.default) - .map(function(user) { - return user.username; - }) - .toArray() - .subscribe( - function(usernames) { - callback(null, usernames); - }, - callback - ); - } - }); - }, - - challenges: function(callback) { - Challenge.find( - { fields: { name: true } }, - function(err, challenges) { - if (err) { - debug('Challenge err: ', err); - callback(err); - } else { - Rx.Observable.from(challenges, null, null, Rx.Scheduler.default) - .map(function(challenge) { - return challenge.name; - }) - .toArray() - .subscribe( - callback.bind(callback, null), - callback - ); - } - }); - }, - stories: function(callback) { - Story.find( - { field: { link: true } }, - function(err, stories) { - if (err) { - debug('Story err: ', err); - callback(err); - } else { - Rx.Observable.from(stories, null, null, Rx.Scheduler.default) - .map(function(story) { - return story.link; - }) - .toArray() - .subscribe( - callback.bind(callback, null), - callback - ); - } - } - ); - }, - nonprofits: function(callback) { - Nonprofit.find( - { field: { name: true } }, - function(err, nonprofits) { - if (err) { - debug('User err: ', err); - callback(err); - } else { - Rx.Observable.from(nonprofits, null, null, Rx.Scheduler.default) - .map(function(nonprofit) { - return nonprofit.name; - }) - .toArray() - .subscribe( - callback.bind(callback, null), - callback - ); - } - }); - } - }, function(err, results) { - if (err) { - return next(err); - } - return process.nextTick(function() { - res.header('Content-Type', 'application/xml'); - res.render('resources/sitemap', { - appUrl: appUrl, - now: now, - users: results.users, - challenges: results.challenges, - stories: results.stories, - nonprofits: results.nonprofits - }); - }); - } - ); - } - function chat(req, res) { res.redirect('https://gitter.im/FreeCodeCamp/FreeCodeCamp'); } diff --git a/server/boot/sitemap.js b/server/boot/sitemap.js new file mode 100644 index 0000000000..fc001a8b2d --- /dev/null +++ b/server/boot/sitemap.js @@ -0,0 +1,64 @@ +import moment from 'moment'; +import { Scheduler, Observable } from 'rx'; +import { timeCache, observeQuery } from '../utils/rx'; +import { dasherize } from '../utils'; + +const cacheTimeout = [ 24, 'hours' ]; +const appUrl = 'https://www.freecodecamp.com'; + +// getCachedObservable( +// app: ExpressApp, +// modelName: String, +// nameProp: String, +// map: (nameProp: String) => String +// ) => Observable[models] +function getCachedObservable(app, modelName, nameProp, map) { + return observeQuery( + app.models[modelName], + 'find', + { fields: { [nameProp]: true } } + ) + .flatMap(models => { + return Observable.from(models, null, null, Scheduler.default); + }) + .filter(model => !!model[nameProp]) + .map(model => model[nameProp]) + .map(map ? map : (x) => x) + .toArray() + ::timeCache(cacheTimeout[0], cacheTimeout[1]); +} + +export default function sitemapRouter(app) { + const router = app.loopback.Router(); + const challenges$ = getCachedObservable(app, 'Challenge', 'dashedName'); + const stories$ = getCachedObservable(app, 'Story', 'storyLink', dasherize); + const jobs$ = getCachedObservable(app, 'Job', 'id'); + function sitemap(req, res, next) { + const now = moment(new Date()).format('YYYY-MM-DD'); + return Observable.combineLatest( + challenges$, + stories$, + jobs$, + ( + challenges, + stories, + jobs + ) => ({ challenges, stories, jobs }) + ) + .subscribe( + ({ challenges, stories, jobs }) => { + res.header('Content-Type', 'application/xml'); + res.render('resources/sitemap', { + appUrl, + now, + challenges, + stories, + jobs + }); + }, + next + ); + } + router.get('/sitemap.xml', sitemap); + app.use(router); +} diff --git a/server/utils/rx.js b/server/utils/rx.js index 20976dfeb8..36bd6c7ae1 100644 --- a/server/utils/rx.js +++ b/server/utils/rx.js @@ -25,15 +25,20 @@ export function saveInstance(instance) { // alias saveInstance export const saveUser = saveInstance; -export function observeQuery(Model, method, query) { - return Rx.Observable.fromNodeCallback(Model[method], Model)(query); +// observeQuery(Model: Object, methodName: String, query: Any) => Observable +export function observeQuery(Model, methodName, query) { + return Rx.Observable.fromNodeCallback(Model[methodName], Model)(query); } +// observeMethod( +// context: Object, methodName: String +// ) => (query: Any) => Observable export function observeMethod(context, methodName) { return Rx.Observable.fromNodeCallback(context[methodName], context); } -// timeChache(amount: Number, unit: String) => Observable +// must be bound to an observable instance +// timeCache(amount: Number, unit: String) => Observable export function timeCache(time, unit) { const source = this; let cache; diff --git a/server/views/resources/sitemap.jade b/server/views/resources/sitemap.jade index 7ce295adb1..0f58d13062 100644 --- a/server/views/resources/sitemap.jade +++ b/server/views/resources/sitemap.jade @@ -49,24 +49,23 @@ urlset(xmlns="http://www.sitemaps.org/schemas/sitemap/0.9") lastmod= now priority= 0.9 - //- User - each user in users - url - loc #{appUrl}/#{user} - lastmod= now - changefreq weekly - priority= 0.5 - each challenge in challenges url - loc #{appUrl}/challenges/#{challenge.replace(/\s/g, '-')} + loc #{appUrl}/challenges/#{challenge} lastmod= now changefreq weekly priority= 0.9 each story in stories url - loc #{appUrl}/news/#{story.replace(/\s/g, '-')} + loc #{appUrl}/news/#{story} lastmod= now - changefreq weekly + changefreq monthly + priority= 0.5 + + each job in jobs + url + loc #{appUrl}/jobs/#{job} + lastmod= now + changefreq monthly priority= 0.5 From 11de01637d33bf82ebe95326dcd478125c40fa71 Mon Sep 17 00:00:00 2001 From: DusanSacha Date: Fri, 8 Apr 2016 16:46:16 +0200 Subject: [PATCH 2/5] Fixed broken link --- .../basic-javascript.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/01-front-end-development-certification/basic-javascript.json b/seed/challenges/01-front-end-development-certification/basic-javascript.json index 5193d537dc..a8a47f849b 100644 --- a/seed/challenges/01-front-end-development-certification/basic-javascript.json +++ b/seed/challenges/01-front-end-development-certification/basic-javascript.json @@ -4953,7 +4953,7 @@ "description": [ "Random numbers are useful for creating random behavior.", "JavaScript has a Math.random() function that generates a random decimal number between 0 (inclusive) and not quite up to 1 (exclusive). Thus Math.random() can return a 0 but never quite return a 1", - "Note
Like Storing Values with the Equal Operator, all function calls will be resolved before the return executes, so we can simply return the value of the Math.random() function.", + "Note
Like Storing Values with the Equal Operator, all function calls will be resolved before the return executes, so we can simply return the value of the Math.random() function.", "

Instructions

", "Change randomFraction to return a random number instead of returning 0." ], From 72d0fa35a957848401e508158fb6a240d74bc206 Mon Sep 17 00:00:00 2001 From: Eric Leung Date: Fri, 8 Apr 2016 08:31:07 -0700 Subject: [PATCH 3/5] Add article to setup SSH keys for GitHub and Git --- .../03-back-end-development-certification/git.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/seed/challenges/03-back-end-development-certification/git.json b/seed/challenges/03-back-end-development-certification/git.json index cfe57f764a..36947d7747 100644 --- a/seed/challenges/03-back-end-development-certification/git.json +++ b/seed/challenges/03-back-end-development-certification/git.json @@ -17,10 +17,11 @@ "Click the \"Create workspace\" button.", "Once C9 builds and loads your workspace, you should see a terminal window in the lower right hand corner. In this window use the following commands. You don't need to know what these mean at this point.", "Install git-it with this command: npm install -g git-it", + "Before you start, read Adding a new SSH key to your GitHub account if you have two-factor authentication set up on your GitHub account.", "Now start the tutorial by running git-it.", "Note that you can resize the c9.io's windows by dragging their borders.", "Make sure that you are always in your project's \"workspace\" directory. You can always navigate back to this directory by running this command: cd ~/workspace.", - "You can view this Node School module's source code on GitHub at https://github.com/jlord/git-it.", + "You can view this Node School module's source code on GitHub at https://github.com/jlord/git-it.", "Complete \"Get Git\"", "Complete \"Repository\"", "Complete \"Commit to it\"", @@ -54,7 +55,7 @@ "Ahora inicia el tutorial ejecutando git-it", "Puedes modificar el tamaño de las ventanas en c9.io arrastrándolas por el borde.", "Asegúrate de que siempre te encuentres en el directorio \"workspace\" de tu proyecto. Puedes regresar a este directorio cuando quieras usando el comando: cd ~/workspace.", - "Puedes ver el código fuente de este módulo de Node School en GitHub visitando https://github.com/jlord/git-it.", + "Puedes ver el código fuente de este módulo de Node School en GitHub visitando https://github.com/jlord/git-it.", "Completa \"Get Git\"", "Completa \"Repository\"", "Completa \"Commit to it\"", From 95fd287f8bac36e8636d62e53dd6d85fec0178a3 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Fri, 8 Apr 2016 14:24:21 -0700 Subject: [PATCH 4/5] Fix typo in claim certificate api --- server/boot/certificate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/boot/certificate.js b/server/boot/certificate.js index d9bb42d246..b45ec34ee6 100644 --- a/server/boot/certificate.js +++ b/server/boot/certificate.js @@ -84,7 +84,7 @@ export default function certificate(app) { function verifyCert(certType, req, res, next) { const { user } = req; - return user.getChallengeMap() + return user.getChallengeMap$() .flatMap(() => certTypeIds[certType]) .flatMap(challenge => { const { From 5fdc961587b6bd175e8f24ed4c7983bfd16fe78c Mon Sep 17 00:00:00 2001 From: Justin Richardsson Date: Fri, 8 Apr 2016 18:13:41 -0400 Subject: [PATCH 5/5] Night Mode + CSS Additions (#7929) * Night Mode and More Hotkeys * Nightmode CSS adjustments * Removed Redundant Line * Wrapped JSON.parse statements in a try catch block --- client/less/challenge.less | 27 +++++++++- client/less/main.less | 51 ++++++++++++++++++- client/less/map.less | 31 ++++++++++++ client/main.js | 80 +++++++++++++++++++++++++++++- server/views/account/settings.jade | 3 ++ server/views/layout-wide.jade | 2 +- 6 files changed, 189 insertions(+), 5 deletions(-) diff --git a/client/less/challenge.less b/client/less/challenge.less index 3ed3808178..5ccb4ca610 100644 --- a/client/less/challenge.less +++ b/client/less/challenge.less @@ -29,7 +29,7 @@ white-space: pre; padding: 5px 10px; margin-bottom: 10px; - margin-top: -10px; + margin-top: -5px; overflow: auto; } dfn { @@ -111,3 +111,28 @@ iframe.iphone { right: -207px; } } + +.night { + .challenge-instructions blockquote { + background-color: #242424; + border-color: #515151; + color: #ABABAB + } + div.CodeMirror { + background-color:#242424; + color:#ABABAB; + &-gutters { + background-color:#242424; + color:#ABABAB; + } + .cm-bracket, .cm-tag { + color:#5CAFD6; + } + .cm-property, .cm-string { + color:#B5753A; + } + .cm-keyword, .cm-attribute { + color:#9BBBDC; + } + } +} diff --git a/client/less/main.less b/client/less/main.less index 9170725425..e5ac587311 100644 --- a/client/less/main.less +++ b/client/less/main.less @@ -54,6 +54,10 @@ html { border-color: @brand-primary; } +body { + display: none; +} + body.full-screen-body-background { background-color: @body-bg; } @@ -678,7 +682,7 @@ thead { /** - * Bonfire styling + * Challenge styling */ form.code span { @@ -986,7 +990,7 @@ hr { } code { - padding: 0; + padding: 1px 5px; } @media only screen and (min-width: 993px) { @@ -1133,6 +1137,49 @@ code { } } +.night { + background-color: #333; + color: #999; + .btn-group, + .text-success, + .challenge-list-header, + .fcc-footer { + background-color: #333; + } + .navbar-default .navbar-nav > li > a { + color: #CCC; + &:hover { + background-color: #666; + } + } + a, .input-group-addon { + color: #CCC; + } + .black-text { + color:#111; + } + code { + background-color: #2A2A2A; + color: #006400; + } + .fcc-footer { + a { + &hover { + background-color: #333; + } + } + } + .btn-primary { + background-color: #006400; + color:#CCC; + transition: color 0.2s, background-color 0.2s; + &:hover { + background-color: #ABABAB!important; + color: #333!important; + } + } +} + @import "chat.less"; @import "jobs.less"; @import "challenge.less"; diff --git a/client/less/map.less b/client/less/map.less index 0c151c4ccd..3cee265c40 100644 --- a/client/less/map.less +++ b/client/less/map.less @@ -2,6 +2,7 @@ * based off of https://github.com/gitterHQ/sidecar * license: MIT */ + .map-aside { width:500px; @@ -314,3 +315,33 @@ .map-aside-action-collapse { background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNzEuNDI5IDE3MS40MjkiIGZpbGw9IiMzYTMxMzMiPjxwYXRoIGQ9Ik0xMjIuNDMzLDEwNi4xMzhsLTE2LjI5NSwxNi4yOTVjLTAuNzQ0LDAuNzQ0LTEuNiwxLjExNi0yLjU2NiwxLjExNmMtMC45NjgsMC0xLjgyMy0wLjM3Mi0yLjU2Ny0xLjExNmwtMTUuMjktMTUuMjkgbC0xNS4yOSwxNS4yOWMtMC43NDQsMC43NDQtMS42LDEuMTE2LTIuNTY3LDEuMTE2cy0xLjgyMy0wLjM3Mi0yLjU2Ny0xLjExNmwtMTYuMjk0LTE2LjI5NWMtMC43NDQtMC43NDQtMS4xMTYtMS42LTEuMTE2LTIuNTY2IGMwLTAuOTY4LDAuMzcyLTEuODIzLDEuMTE2LTIuNTY3bDE1LjI5LTE1LjI5bC0xNS4yOS0xNS4yOWMtMC43NDQtMC43NDQtMS4xMTYtMS42LTEuMTE2LTIuNTY3czAuMzcyLTEuODIzLDEuMTE2LTIuNTY3IEw2NS4yOSw0OC45OTZjMC43NDQtMC43NDQsMS42LTEuMTE2LDIuNTY3LTEuMTE2czEuODIzLDAuMzcyLDIuNTY3LDEuMTE2bDE1LjI5LDE1LjI5bDE1LjI5LTE1LjI5IGMwLjc0NC0wLjc0NCwxLjYtMS4xMTYsMi41NjctMS4xMTZjMC45NjcsMCwxLjgyMiwwLjM3MiwyLjU2NiwxLjExNmwxNi4yOTUsMTYuMjk0YzAuNzQ0LDAuNzQ0LDEuMTE2LDEuNiwxLjExNiwyLjU2NyBzLTAuMzcyLDEuODIzLTEuMTE2LDIuNTY3bC0xNS4yOSwxNS4yOWwxNS4yOSwxNS4yOWMwLjc0NCwwLjc0NCwxLjExNiwxLjYsMS4xMTYsMi41NjcgQzEyMy41NDksMTA0LjUzOSwxMjMuMTc3LDEwNS4zOTQsMTIyLjQzMywxMDYuMTM4eiBNMTQ2LjQyOSw4NS43MTRjMC0xMS4wMTItMi43MTctMjEuMTY4LTguMTQ4LTMwLjQ2OSBzLTEyLjc5Ny0xNi42NjctMjIuMDk4LTIyLjA5OFM5Ni43MjYsMjUsODUuNzE0LDI1cy0yMS4xNjgsMi43MTYtMzAuNDY5LDguMTQ3UzM4LjU3OSw0NS45NDUsMzMuMTQ3LDU1LjI0NlMyNSw3NC43MDMsMjUsODUuNzE0IHMyLjcxNiwyMS4xNjgsOC4xNDcsMzAuNDY5czEyLjc5NywxNi42NjYsMjIuMDk4LDIyLjA5OHMxOS40NTcsOC4xNDgsMzAuNDY5LDguMTQ4czIxLjE2OC0yLjcxNywzMC40NjktOC4xNDggczE2LjY2Ni0xMi43OTcsMjIuMDk4LTIyLjA5OFMxNDYuNDI5LDk2LjcyNiwxNDYuNDI5LDg1LjcxNHogTTE3MS40MjksODUuNzE0YzAsMTUuNTUxLTMuODMyLDI5Ljg5My0xMS40OTYsNDMuMDI0IGMtNy42NjQsMTMuMTMzLTE4LjA2MiwyMy41My0zMS4xOTQsMzEuMTk0Yy0xMy4xMzIsNy42NjQtMjcuNDc0LDExLjQ5Ni00My4wMjQsMTEuNDk2cy0yOS44OTItMy44MzItNDMuMDI0LTExLjQ5NiBjLTEzLjEzMy03LjY2NC0yMy41MzEtMTguMDYyLTMxLjE5NC0zMS4xOTRDMy44MzIsMTE1LjYwNywwLDEwMS4yNjUsMCw4NS43MTRTMy44MzIsNTUuODIyLDExLjQ5Niw0Mi42OSBjNy42NjQtMTMuMTMzLDE4LjA2Mi0yMy41MzEsMzEuMTk0LTMxLjE5NEM1NS44MjIsMy44MzIsNzAuMTY0LDAsODUuNzE0LDBzMjkuODkzLDMuODMyLDQzLjAyNCwxMS40OTYgYzEzLjEzMyw3LjY2NCwyMy41MywxOC4wNjIsMzEuMTk0LDMxLjE5NEMxNjcuNTk3LDU1LjgyMiwxNzEuNDI5LDcwLjE2NCwxNzEuNDI5LDg1LjcxNHoiLz48L3N2Zz4=) } + +.night { + .map-fixed-header { + background-color: #333; + } + .map-aside { + border-left-color:#222; + } + #map-filter, .input-group-addon { + border-color: #222; + background-color: #666; + color:#ABABAB; + } + .map-accordion h2 > a { + background:#666; + } + .map-accordion a:focus, #noneFound { + color: #ABABAB; + } + .input-group-addon { + &.filled{ + background: #555; + border-color: #000d00; + color: white; + } + } + .challenge-title { + color: #999; + } +} diff --git a/client/main.js b/client/main.js index b14f8d1fdd..b7ed7c9bd0 100644 --- a/client/main.js +++ b/client/main.js @@ -356,7 +356,7 @@ $(document).ready(function() { function showMap() { if (!main.isMapAsideLoad) { - var mapAside = $('