From d713843bf9363b214152c79c23e1838da776b93d Mon Sep 17 00:00:00 2001 From: BKinahan Date: Wed, 30 Mar 2016 17:05:25 +0000 Subject: [PATCH 01/11] Expand test regexp to verify correct style override --- .../01-front-end-development-certification/html5-and-css.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/01-front-end-development-certification/html5-and-css.json b/seed/challenges/01-front-end-development-certification/html5-and-css.json index 20f548b1a4..472c42e36a 100644 --- a/seed/challenges/01-front-end-development-certification/html5-and-css.json +++ b/seed/challenges/01-front-end-development-certification/html5-and-css.json @@ -3696,7 +3696,7 @@ "assert($(\"h1\").hasClass(\"blue-text\"), 'message: Your h1 element should have the class blue-text.');", "assert($(\"h1\").attr(\"id\") === \"orange-text\", 'message: Your h1 element should have the id of orange-text.');", "assert(code.match(/h1 element should have the inline style of color: white.');", - "assert(code.match(/pink.*\\!important;/gi), 'message: Your pink-text class should have the !important keyword to override all other declarations.');", + "assert(code.match(/\\.pink-text\\s+\\{\\s+color:.*pink.*!important;\\s+\\}/gi), 'message: Your pink-text class declaration should have the !important keyword to override all other declarations.');", "assert($(\"h1\").css(\"color\") === \"rgb(255, 192, 203)\", 'message: Your h1 element should be pink.');" ], "descriptionPtBR": [ From 09549fd34bd4b544971a3f2f17fde423578d81e1 Mon Sep 17 00:00:00 2001 From: Eric Leung Date: Wed, 6 Apr 2016 11:40:19 -0700 Subject: [PATCH 02/11] Add background reading for Express.js challenge --- .../nodejs-and-expressjs.json | 1 + 1 file changed, 1 insertion(+) diff --git a/seed/challenges/03-back-end-development-certification/nodejs-and-expressjs.json b/seed/challenges/03-back-end-development-certification/nodejs-and-expressjs.json index b4b0f4f030..2b47bc4d7f 100644 --- a/seed/challenges/03-back-end-development-certification/nodejs-and-expressjs.json +++ b/seed/challenges/03-back-end-development-certification/nodejs-and-expressjs.json @@ -196,6 +196,7 @@ "id": "bd7153d8c441eddfaeb5bd1f", "title": "Build Web Apps with Express.js", "description": [ + "Before we begin using Express.js, read this article on Understanding Express.js.", "We'll build this Waypoint on Cloud 9, a powerful online code editor with a full Ubuntu Linux workspace, all running in the cloud.", "If you don't already have Cloud 9 account, create one now at http://c9.io.", "Open up http://c9.io and sign in to your account.", From 480318dba05114b88f7722fd1840d0ac6b2f3ed4 Mon Sep 17 00:00:00 2001 From: Eric Leung Date: Wed, 6 Apr 2016 12:44:04 -0700 Subject: [PATCH 03/11] Clarify all characters are unique in No Repeats --- .../advanced-bonfires.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seed/challenges/01-front-end-development-certification/advanced-bonfires.json b/seed/challenges/01-front-end-development-certification/advanced-bonfires.json index 42b752a3d5..15701fee38 100644 --- a/seed/challenges/01-front-end-development-certification/advanced-bonfires.json +++ b/seed/challenges/01-front-end-development-certification/advanced-bonfires.json @@ -223,7 +223,7 @@ "id": "a7bf700cd123b9a54eef01d5", "title": "No repeats please", "description": [ - "Return the number of total permutations of the provided string that don't have repeated consecutive letters. Assume that duplicate characters are each unique.", + "Return the number of total permutations of the provided string that don't have repeated consecutive letters. Assume that all characters in the provided string are each unique.", "For example, aab should return 2 because it has 6 total permutations (aab, aab, aba, aba, baa, baa), but only 2 of them (aba and aba) don't have the same letter (in this case a) repeating.", "Remember to use Read-Search-Ask if you get stuck. Try to pair program. Write your own code." ], From d9a4badbc0a6b51c76407ee025d7c6d33d94f58c Mon Sep 17 00:00:00 2001 From: Jose Tello Date: Wed, 6 Apr 2016 14:39:52 -0700 Subject: [PATCH 04/11] Change wording from "italicizing" to "emphasizing". Closes #7939. --- .../01-front-end-development-certification/jquery.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seed/challenges/01-front-end-development-certification/jquery.json b/seed/challenges/01-front-end-development-certification/jquery.json index ddcec4176b..ed8430f735 100644 --- a/seed/challenges/01-front-end-development-certification/jquery.json +++ b/seed/challenges/01-front-end-development-certification/jquery.json @@ -546,10 +546,10 @@ "description": [ "Using jQuery, you can change the text between the start and end tags of an element. You can even change HTML markup.", "jQuery has a function called .html() that lets you add HTML tags and text within an element. Any content previously within the element will be completely replaced with the content you provide using this function.", - "Here's how you would rewrite and italicize the text of our heading:", + "Here's how you would rewrite and emphasize the text of our heading:", "$(\"h3\").html(\"<em>jQuery Playground</em>\");", "jQuery also has a similar function called .text() that only alters text without adding tags. In other words, this function will not evaluate any HTML tags passed to it, but will instead treat it as text you want to replace with.", - "Change the button with id target4 by italicizing its text." + "Change the button with id target4 by emphasizing its text." ], "releasedOn": "November 18, 2015", "challengeSeed": [ From fa37dc865d7165d90508b1d9b483095e1f568bec Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 11 Feb 2016 22:33:54 -0800 Subject: [PATCH 05/11] Reduce initial user query load Refactor passport to own file --- package.json | 1 + server/component-passport.js | 114 +++++++++++++++++++++++++++++++++++ server/server.js | 73 +--------------------- 3 files changed, 118 insertions(+), 70 deletions(-) create mode 100644 server/component-passport.js diff --git a/package.json b/package.json index 739e202db7..186a18056c 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "normalize-url": "^1.3.1", "normalizr": "^2.0.0", "object.assign": "^4.0.3", + "passport": "^0.2.1", "passport-facebook": "^2.0.0", "passport-github": "^1.0.0", "passport-google-oauth2": "~0.1.6", diff --git a/server/component-passport.js b/server/component-passport.js new file mode 100644 index 0000000000..b9e1635af9 --- /dev/null +++ b/server/component-passport.js @@ -0,0 +1,114 @@ +import passport from 'passport'; +import { PassportConfigurator } from 'loopback-component-passport'; +import passportProviders from './passport-providers'; +import uuid from 'node-uuid'; +import { generateKey } from 'loopback-component-passport/lib/models/utils'; + +import { + setProfileFromGithub, + getSocialProvider, + getUsernameFromProvider +} from './utils/auth'; + +const passportOptions = { + emailOptional: true, + profileToUser(provider, profile) { + var emails = profile.emails; + // NOTE(berks): get email or set to null. + // MongoDB indexs email but can be sparse(blank) + var email = emails && emails[0] && emails[0].value ? + emails[0].value : + null; + + // create random username + // username will be assigned when camper signups for Github + var username = 'fcc' + uuid.v4().slice(0, 8); + var password = generateKey('password'); + var userObj = { + username: username, + password: password + }; + + if (email) { + userObj.email = email; + } + + if (!(/github/).test(provider)) { + userObj[getSocialProvider(provider)] = getUsernameFromProvider( + getSocialProvider(provider), + profile + ); + } + + if (/github/.test(provider)) { + setProfileFromGithub(userObj, profile, profile._json); + } + return userObj; + } +}; + +const fields = { + progressTimestamps: false, + completedChallenges: false, + challengeMap: false +}; + +PassportConfigurator.prototype.init = function passportInit(noSession) { + this.app.middleware('session:after', passport.initialize()); + + if (noSession) { + return; + } + + this.app.middleware('session:after', passport.session()); + + // Serialization and deserialization is only required if passport session is + // enabled + + passport.serializeUser((user, done) => { + done(null, user.id); + }); + + passport.deserializeUser((id, done) => { + + this.userModel.findById(id, { fields }, (err, user) => { + if (err || !user) { + return done(err, user); + } + this.app.dataSources.db.connector + .collection('user') + .aggregate([ + { $match: { _id: user.id } }, + { $project: { points: { $size: '$progressTimestamps' } } } + ], function(err, { points = 1 } = {}) { + if (err) { return done(err); } + user.points = points; + done(null, user); + }); + }); + }); +}; + +export default function setupPassport(app) { + const configurator = new PassportConfigurator(app); + + configurator.setupModels({ + userModel: app.models.user, + userIdentityModel: app.models.userIdentity, + userCredentialModel: app.models.userCredential + }); + + configurator.init(); + + Object.keys(passportProviders).map(function(strategy) { + var config = passportProviders[strategy]; + config.session = config.session !== false; + configurator.configureProvider( + strategy, + { + ...config, + ...passportOptions + } + ); + }); +} diff --git a/server/server.js b/server/server.js index 1f711cd92e..1d5f9aefda 100755 --- a/server/server.js +++ b/server/server.js @@ -3,96 +3,29 @@ var pmx = require('pmx'); pmx.init(); var _ = require('lodash'), - uuid = require('node-uuid'), - assign = require('lodash').assign, loopback = require('loopback'), boot = require('loopback-boot'), expressState = require('express-state'), path = require('path'), - passportProviders = require('./passport-providers'); + setupPassport = require('./component-passport'); -var setProfileFromGithub = require('./utils/auth').setProfileFromGithub; -var getSocialProvider = require('./utils/auth').getSocialProvider; -var getUsernameFromProvider = require('./utils/auth').getUsernameFromProvider; -var generateKey = - require('loopback-component-passport/lib/models/utils').generateKey; - -var isBeta = !!process.env.BETA; var app = loopback(); +var isBeta = !!process.env.BETA; expressState.extend(app); app.set('state namespace', '__fcc__'); - -var PassportConfigurator = - require('loopback-component-passport').PassportConfigurator; -var passportConfigurator = new PassportConfigurator(app); - app.set('port', process.env.PORT || 3000); app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); app.use(loopback.token()); app.disable('x-powered-by'); -// adds passport initialization after session middleware phase is complete -passportConfigurator.init(); - boot(app, { appRootDir: __dirname, dev: process.env.NODE_ENV }); - -passportConfigurator.setupModels({ - userModel: app.models.user, - userIdentityModel: app.models.userIdentity, - userCredentialModel: app.models.userCredential -}); - -var passportOptions = { - emailOptional: true, - profileToUser: function(provider, profile) { - var emails = profile.emails; - // NOTE(berks): get email or set to null. - // MongoDB indexs email but can be sparse(blank) - var email = emails && emails[0] && emails[0].value ? - emails[0].value : - null; - - // create random username - // username will be assigned when camper signups for GitHub - var username = 'fcc' + uuid.v4().slice(0, 8); - var password = generateKey('password'); - var userObj = { - username: username, - password: password - }; - - if (email) { - userObj.email = email; - } - - if (!(/github/).test(provider)) { - userObj[getSocialProvider(provider)] = getUsernameFromProvider( - getSocialProvider(provider), - profile - ); - } - - if (/github/.test(provider)) { - setProfileFromGithub(userObj, profile, profile._json); - } - return userObj; - } -}; - -Object.keys(passportProviders).map(function(strategy) { - var config = passportProviders[strategy]; - config.session = config.session !== false; - passportConfigurator.configureProvider( - strategy, - assign(config, passportOptions) - ); -}); +setupPassport(app); app.start = _.once(function() { app.listen(app.get('port'), function() { From 40eff8d5ccfc10e767363446ca21fcbc18157acf Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Tue, 5 Apr 2016 16:53:13 -0700 Subject: [PATCH 06/11] Fix lint issues --- server/component-passport.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/component-passport.js b/server/component-passport.js index b9e1635af9..022104d541 100644 --- a/server/component-passport.js +++ b/server/component-passport.js @@ -75,7 +75,7 @@ PassportConfigurator.prototype.init = function passportInit(noSession) { if (err || !user) { return done(err, user); } - this.app.dataSources.db.connector + return this.app.dataSources.db.connector .collection('user') .aggregate([ { $match: { _id: user.id } }, @@ -83,7 +83,7 @@ PassportConfigurator.prototype.init = function passportInit(noSession) { ], function(err, { points = 1 } = {}) { if (err) { return done(err); } user.points = points; - done(null, user); + return done(null, user); }); }); }); From 7737bfedc1671b7fce34ecd89386146fd624e3f1 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 6 Apr 2016 16:54:45 -0700 Subject: [PATCH 07/11] Fix points query --- server/component-passport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/component-passport.js b/server/component-passport.js index 022104d541..db96edcb86 100644 --- a/server/component-passport.js +++ b/server/component-passport.js @@ -80,7 +80,7 @@ PassportConfigurator.prototype.init = function passportInit(noSession) { .aggregate([ { $match: { _id: user.id } }, { $project: { points: { $size: '$progressTimestamps' } } } - ], function(err, { points = 1 } = {}) { + ], function(err, [{ points = 1 } = {}]) { if (err) { return done(err); } user.points = points; return done(null, user); From 1308d312a417733db7503219a0679ecdbe75f2bf Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 6 Apr 2016 21:08:19 -0700 Subject: [PATCH 08/11] Add methods to get challengeMap Update boot/challenge.js to use new methods Update boot/user.js to use new methods --- common/models/user.js | 24 ++++++ server/boot/certificate.js | 3 +- server/boot/challenge.js | 134 +++++++++++++++++------------- server/boot/user.js | 66 +++++++-------- server/views/partials/navbar.jade | 2 +- 5 files changed, 132 insertions(+), 97 deletions(-) diff --git a/common/models/user.js b/common/models/user.js index c772884066..4c48f5e3ab 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -441,4 +441,28 @@ module.exports = function(User) { } return this.constructor.update$({ id }, updateData, updateOptions); }; + User.prototype.getPoints$ = function getPoints$() { + const id = this.getId(); + const filter = { + where: { id }, + fields: { progressTimestamps: true } + }; + return this.constructor.findOne$(filter) + .map(user => { + this.progressTimestamps = user.progressTimestamps; + return user.progressTimestamps; + }); + }; + User.prototype.getChallengeMap$ = function getChallengeMap$() { + const id = this.getId(); + const filter = { + where: { id }, + fields: { challengeMap: true } + }; + return this.constructor.findOne$(filter) + .map(user => { + this.challengeMap = user.challengeMap; + return user.challengeMap; + }); + }; }; diff --git a/server/boot/certificate.js b/server/boot/certificate.js index 6775edd3a0..d9bb42d246 100644 --- a/server/boot/certificate.js +++ b/server/boot/certificate.js @@ -84,7 +84,8 @@ export default function certificate(app) { function verifyCert(certType, req, res, next) { const { user } = req; - return certTypeIds[certType] + return user.getChallengeMap() + .flatMap(() => certTypeIds[certType]) .flatMap(challenge => { const { id, diff --git a/server/boot/challenge.js b/server/boot/challenge.js index aa8938e6df..6496579ba3 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -142,7 +142,7 @@ function getRenderData$(user, challenge$, origChallengeName, solution) { return challenge$ .map(challenge => challenge.toJSON()) - .filter((challenge) => { + .filter(challenge => { return shouldNotFilterComingSoon(challenge) && challenge.type !== 'hike' && testChallengeName.test(challenge.name); @@ -500,8 +500,17 @@ module.exports = function(app) { function showChallenge(req, res, next) { const solution = req.query.solution; const challengeName = req.params.challengeName.replace(challengesRegex, ''); + const { user } = req; - getRenderData$(req.user, challenge$, challengeName, solution) + Observable.defer(() => { + if (user && user.getChallengeMap$) { + return user.getChallengeMap$().map(user); + } + return Observable.just(null); + }) + .flatMap(user => { + return getRenderData$(user, challenge$, challengeName, solution); + }) .subscribe( ({ type, redirectUrl, message, data }) => { if (message) { @@ -546,48 +555,46 @@ module.exports = function(app) { return res.sendStatus(403); } - const completedDate = Date.now(); - const { - id, - name, - challengeType, - solution, - timezone - } = req.body; + return req.user.getChallengeMap$() + .flatMap(() => { + const completedDate = Date.now(); + const { + id, + name, + challengeType, + solution, + timezone + } = req.body; - const { alreadyCompleted, updateData } = buildUserUpdate( - req.user, - id, - { - id, - challengeType, - solution, - name, - completedDate - }, - timezone - ); + const { alreadyCompleted, updateData } = buildUserUpdate( + req.user, + id, + { + id, + challengeType, + solution, + name, + completedDate + }, + timezone + ); - const user = req.user; - const points = alreadyCompleted ? - user.progressTimestamps.length : - user.progressTimestamps.length + 1; + const user = req.user; + const points = alreadyCompleted ? user.points : user.points + 1; - return user.update$(updateData) - .doOnNext(({ count }) => log('%s documents updated', count)) - .subscribe( - () => {}, - next, - function() { - if (type === 'json') { - return res.json({ - points, - alreadyCompleted - }); - } - return res.sendStatus(200); - } - ); + return user.update$(updateData) + .doOnNext(({ count }) => log('%s documents updated', count)) + .map(() => { + if (type === 'json') { + return res.json({ + points, + alreadyCompleted + }); + } + return res.sendStatus(200); + }); + }) + .subscribe(() => {}, next); } function completedZiplineOrBasejump(req, res, next) { @@ -635,31 +642,38 @@ module.exports = function(app) { } - const { - alreadyCompleted, - updateData - } = buildUserUpdate(req.user, completedChallenge.id, completedChallenge); + return user.getChallengeMap$() + .flatMap(() => { + const { + alreadyCompleted, + updateData + } = buildUserUpdate(user, completedChallenge.id, completedChallenge); - return user.update$(updateData) - .doOnNext(({ count }) => log('%s documents updated', count)) - .doOnNext(() => { - if (type === 'json') { - return res.send({ - alreadyCompleted, - points: alreadyCompleted ? - user.progressTimestamps.length : - user.progressTimestamps.length + 1 + return user.update$(updateData) + .doOnNext(({ count }) => log('%s documents updated', count)) + .doOnNext(() => { + if (type === 'json') { + return res.send({ + alreadyCompleted, + points: alreadyCompleted ? user.points : user.points + 1 + }); + } + return res.status(200).send(true); }); - } - return res.status(200).send(true); }) .subscribe(() => {}, next); } - function showMap(showAside, { user = {} }, res, next) { - const { challengeMap = {} } = user; - - return getSuperBlocks$(challenge$, challengeMap) + function showMap(showAside, { user }, res, next) { + return Observable.defer(() => { + if (user && typeof user.getChallengeMap$ === 'function') { + return user.getChallengeMap$(); + } + console.log('foo'); + return Observable.just({}); + }) + .doOnNext(challengeMap => console.log('challengeMap', challengeMap)) + .flatMap(challengeMap => getSuperBlocks$(challenge$, challengeMap)) .subscribe( superBlocks => { res.render('map/show', { diff --git a/server/boot/user.js b/server/boot/user.js index 37b56bd214..8d9984e2ab 100644 --- a/server/boot/user.js +++ b/server/boot/user.js @@ -420,63 +420,59 @@ module.exports = function(app) { } function toggleLockdownMode(req, res, next) { - return User.findById(req.accessToken.userId, function(err, user) { - if (err) { return next(err); } - return user.updateAttribute('isLocked', !user.isLocked, function(err) { - if (err) { return next(err); } - req.flash('info', { - msg: 'We\'ve successfully updated your Privacy preferences.' - }); - return res.redirect('/settings'); - }); - }); + const { user } = req; + user.update$({ isLocked: !user.isLocked }) + .subscribe( + () => { + req.flash('info', { + msg: 'We\'ve successfully updated your Privacy preferences.' + }); + return res.redirect('/settings'); + }, + next + ); } function toggleReceivesAnnouncementEmails(req, res, next) { - return User.findById(req.accessToken.userId, function(err, user) { - if (err) { return next(err); } - return user.updateAttribute( - 'sendMonthlyEmail', - !user.sendMonthlyEmail, - (err) => { - if (err) { return next(err); } + const { user } = req; + return user.update$({ sendMonthlyEmail: !user.sendMonthlyEmail }) + .subscribe( + () => { req.flash('info', { msg: 'We\'ve successfully updated your Email preferences.' }); return res.redirect('/settings'); - }); - }); + }, + next + ); } function toggleReceivesQuincyEmails(req, res, next) { - return User.findById(req.accessToken.userId, function(err, user) { - if (err) { return next(err); } - return user.updateAttribute('sendQuincyEmail', !user.sendQuincyEmail, - (err) => { - if (err) { return next(err); } + const { user } = req; + return user.update$({ sendQuincyEmail: !user.sendQuincyEmail }) + .subscribe( + () => { req.flash('info', { msg: 'We\'ve successfully updated your Email preferences.' }); return res.redirect('/settings'); - } + }, + next ); - }); } function toggleReceivesNotificationEmails(req, res, next) { - return User.findById(req.accessToken.userId, function(err, user) { - if (err) { return next(err); } - return user.updateAttribute( - 'sendNotificationEmail', - !user.sendNotificationEmail, - function(err) { - if (err) { return next(err); } + const { user } = req; + return user.update$({ sendNotificationEmail: !user.sendNotificationEmail }) + .subscribe( + () => { req.flash('info', { msg: 'We\'ve successfully updated your Email preferences.' }); return res.redirect('/settings'); - }); - }); + }, + next + ); } function postDeleteAccount(req, res, next) { diff --git a/server/views/partials/navbar.jade b/server/views/partials/navbar.jade index eb03371a1f..5e527ddc8d 100644 --- a/server/views/partials/navbar.jade +++ b/server/views/partials/navbar.jade @@ -28,7 +28,7 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height a(href='/login') Sign in else li.brownie-points-nav - a(href='/' + user.username) [ #{user.progressTimestamps.length} ] + a(href='/' + user.username) [ #{user.points} ] li.hidden-xs.hidden-sm.avatar a(href='/' + user.username) img.profile-picture.float-right(src='#{user.picture}') From 513d55b23da41a232166a4bc4484f1b840cacb87 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 6 Apr 2016 23:06:19 -0700 Subject: [PATCH 09/11] Add caching to news Deprecate story creation --- server/boot/story.js | 399 ++++--------------------------------------- 1 file changed, 32 insertions(+), 367 deletions(-) diff --git a/server/boot/story.js b/server/boot/story.js index 4d1ca77660..6849812874 100755 --- a/server/boot/story.js +++ b/server/boot/story.js @@ -1,27 +1,11 @@ -var Rx = require('rx'), - assign = require('object.assign'), - sanitizeHtml = require('sanitize-html'), - moment = require('moment'), - debug = require('debug')('fcc:cntr:story'), - utils = require('../utils'), - observeMethod = require('../utils/rx').observeMethod, - saveUser = require('../utils/rx').saveUser, - saveInstance = require('../utils/rx').saveInstance, - validator = require('validator'); +import moment from 'moment'; -import { - ifNoUser401, - ifNoUserRedirectTo -} from '../utils/middleware'; +import { unDasherize } from '../utils'; +import { observeMethod } from '../utils/rx'; const foundationDate = 1413298800000; const time48Hours = 172800000; -const unDasherize = utils.unDasherize; -const dasherize = utils.dasherize; -const getURLTitle = utils.getURLTitle; -const sendNonUserToNews = ifNoUserRedirectTo('/news'); - function hotRank(timeValue, rank) { /* * Hotness ranking algorithm: http://amix.dk/blog/post/19588 @@ -39,81 +23,54 @@ function sortByRank(a, b) { hotRank(a.timePosted - foundationDate, a.rank); } -function cleanData(data, opts) { - var options = assign( - {}, - { - allowedTags: [], - allowedAttributes: [] - }, - opts || {} - ); - return sanitizeHtml(data, options).replace(/";/g, '"'); -} - module.exports = function(app) { - var router = app.loopback.Router(); - var User = app.models.User; - var findUserById = observeMethod(User, 'findById'); + const router = app.loopback.Router(); + const Story = app.models.Story; + const findStory = observeMethod(Story, 'find'); + const findOneStory = observeMethod(Story, 'findOne'); + const query = { + order: 'timePosted DESC', + limit: 1000 + }; + const storiesData$ = findStory(query) + .map(stories => { + const sliceVal = stories.length >= 100 ? 100 : stories.length; + return stories.sort(sortByRank).slice(0, sliceVal); + }) + .shareReplay(); - var Story = app.models.Story; - var findStory = observeMethod(Story, 'find'); - var findOneStory = observeMethod(Story, 'findOne'); - var findStoryById = observeMethod(Story, 'findById'); - var countStories = observeMethod(Story, 'count'); - - router.post('/news/userstories', userStories); + const redirectToNews = (req, res) => res.redirect('/news'); + const deprecated = (req, res) => res.sendStatus(410); + router.post('/news/userstories', deprecated); router.get('/news/hot', hotJSON); router.get('/news/feed', RSSFeed); router.get('/stories/hotStories', hotJSON); - router.get( - '/stories/submit', - sendNonUserToNews, - submitNew - ); - router.get( - '/stories/submit/new-story', - sendNonUserToNews, - preSubmit - ); - router.post('/stories/preliminary', ifNoUser401, newStory); - router.post('/stories/', ifNoUser401, storySubmission); - router.post('/stories/search', getStories); + router.get('/stories/submit', redirectToNews); + router.get('/stories/submit/new-story', redirectToNews); + router.post('/stories/preliminary', deprecated); + router.post('/stories/', deprecated); + router.post('/stories/search', deprecated); router.get('/news/:storyName', returnIndividualStory); - router.post('/stories/upvote/', ifNoUser401, upvote); - router.get('/stories/:storyName', redirectToNews); + router.post('/stories/upvote/', deprecated); + router.get('/stories/:storyName', replaceStoryWithNews); app.use(router); - function redirectToNews(req, res) { + function replaceStoryWithNews(req, res) { var url = req.originalUrl.replace(/^\/stories/, '/news'); return res.redirect(url); } function hotJSON(req, res, next) { - var query = { - order: 'timePosted DESC', - limit: 1000 - }; - findStory(query).subscribe( - function(stories) { - var sliceVal = stories.length >= 100 ? 100 : stories.length; - var data = stories.sort(sortByRank).slice(0, sliceVal); - res.json(data); - }, + storiesData$.subscribe( + stories => res.json(stories), next ); } function RSSFeed(req, res, next) { - var query = { - order: 'timePosted DESC', - limit: 1000 - }; - findStory(query).subscribe( - function(stories) { - var sliceVal = stories.length >= 100 ? 100 : stories.length; - var data = stories.sort(sortByRank).slice(0, sliceVal); + storiesData$.subscribe( + data => { res.set('Content-Type', 'text/xml'); res.render('feed', { title: 'FreeCodeCamp Camper News RSS Feed', @@ -126,51 +83,6 @@ module.exports = function(app) { ); } - function submitNew(req, res) { - if (!req.user.isGithubCool) { - req.flash('errors', { - msg: 'You must link GitHub with your account before you can post' + - ' on Camper News.' - }); - return res.redirect('/news'); - } - - return res.render('stories/index', { - title: 'Submit a new story to Camper News', - page: 'submit' - }); - } - - function preSubmit(req, res) { - var data = req.query; - if (typeof data.url !== 'string') { - req.flash('errors', { msg: 'No URL supplied with story' }); - return res.redirect('/news'); - } - var cleanedData = cleanData(data.url); - - if (data.url.replace(/&/g, '&') !== cleanedData) { - req.flash('errors', { - msg: 'The data for this post is malformed' - }); - return res.render('stories/index', { - page: 'stories/submit' - }); - } - - var title = data.title || ''; - var image = data.image || ''; - var description = data.description || ''; - return res.render('stories/index', { - title: 'Confirm your Camper News story submission', - page: 'storySubmission', - storyURL: data.url, - storyTitle: title, - storyImage: image, - storyMetaDescription: description - }); - } - function returnIndividualStory(req, res, next) { var dashedName = req.params.storyName; var storyName = unDasherize(dashedName); @@ -217,251 +129,4 @@ module.exports = function(app) { next ); } - - function userStories({ body: { search = '' } = {} }, res, next) { - if (!search || typeof search !== 'string') { - return res.sendStatus(404); - } - - return app.dataSources.db.connector - .collection('story') - .find({ - 'author.username': search.toLowerCase().replace('$', '') - }) - .toArray(function(err, items) { - if (err) { - return next(err); - } - if (items && items.length !== 0) { - return res.json(items.sort(sortByRank)); - } - return res.sendStatus(404); - }); - } - - function getStories({ body: { search = '' } = {} }, res, next) { - if (!search || typeof search !== 'string') { - return res.sendStatus(404); - } - - const query = { - '$text': { - // protect against NoSQL injection - '$search': search.replace('$', '') - } - }; - - const fields = { - headline: 1, - timePosted: 1, - link: 1, - description: 1, - rank: 1, - upVotes: 1, - author: 1, - image: 1, - storyLink: 1, - metaDescription: 1, - textScore: { - $meta: 'textScore' - } - }; - - const options = { - sort: { - textScore: { - $meta: 'textScore' - } - } - }; - - return app.dataSources.db.connector - .collection('story') - .find(query, fields, options) - .toArray(function(err, items) { - if (err) { - return next(err); - } - if (items && items.length !== 0) { - return res.json(items); - } - return res.sendStatus(404); - }); - } - - function upvote(req, res, next) { - const { id } = req.body; - var story$ = findStoryById(id).shareReplay(); - - story$.flatMap(function(story) { - // find story author - return findUserById(story.author.userId); - }) - .flatMap(function(user) { - // if user deletes account then this will not exist - if (user) { - user.progressTimestamps.push({ - timestamp: Date.now() - }); - } - return saveUser(user); - }) - .flatMap(function() { - return story$; - }) - .flatMap(function(story) { - debug('upvoting'); - story.rank += 1; - story.upVotes.push({ - upVotedBy: req.user.id, - upVotedByUsername: req.user.username - }); - return saveInstance(story); - }) - .subscribe( - function(story) { - return res.send(story); - }, - next - ); - } - - function newStory(req, res, next) { - if (!req.user.isGithubCool) { - req.flash('errors', { - msg: 'You must authenticate with GitHub to post to Camper News' - }); - return res.redirect('/news'); - } - var url = req.body.data.url; - - if (!validator.isURL('' + url)) { - req.flash('errors', { - msg: "The URL you submitted doesn't appear valid" - }); - return res.json({ - alreadyPosted: true, - storyURL: '/stories/submit' - }); - - } - if (url.search(/^https?:\/\//g) === -1) { - url = 'http://' + url; - } - - return findStory({ where: { link: url } }) - .map(function(stories) { - if (stories.length) { - return { - alreadyPosted: true, - storyURL: '/stories/' + stories.pop().storyLink - }; - } - return { - alreadyPosted: false, - storyURL: url - }; - }) - .flatMap(function(data) { - if (data.alreadyPosted) { - return Rx.Observable.just(data); - } - return Rx.Observable.fromNodeCallback(getURLTitle)(data.storyURL) - .map(function(story) { - return { - alreadyPosted: false, - storyURL: data.storyURL, - storyTitle: story.title, - storyImage: story.image, - storyMetaDescription: story.description - }; - }); - }) - .subscribe( - function(story) { - if (story.alreadyPosted) { - req.flash('errors', { - msg: "Someone's already posted that link. Here's the discussion." - }); - } - res.json(story); - }, - next - ); - } - - function storySubmission(req, res, next) { - if (req.user.isBanned) { - return res.json({ - isBanned: true - }); - } - var data = req.body.data; - - var storyLink = data.headline - .replace(/[^a-z0-9\s]/gi, '') - .replace(/\s+/g, ' ') - .toLowerCase() - .trim(); - - var link = data.link; - - if (link.search(/^https?:\/\//g) === -1) { - link = 'http://' + link; - } - - var query = { - storyLink: { - like: ('^' + storyLink + '(?: [0-9]+)?$'), - options: 'i' - } - }; - - var savedStory = countStories(query) - .flatMap(function(storyCount) { - // if duplicate storyLink add unique number - storyLink = (storyCount === 0) ? - storyLink : - storyLink + ' ' + storyCount; - - var link = data.link; - if (link.search(/^https?:\/\//g) === -1) { - link = 'http://' + link; - } - var newStory = new Story({ - headline: cleanData(data.headline), - timePosted: Date.now(), - link: link, - description: cleanData(data.description), - rank: 1, - upVotes: [({ - upVotedBy: req.user.id, - upVotedByUsername: req.user.username - })], - author: { - picture: req.user.picture, - userId: req.user.id, - username: req.user.username - }, - image: data.image, - storyLink: storyLink, - metaDescription: data.storyMetaDescription - }); - return saveInstance(newStory); - }); - - req.user.progressTimestamps.push({ - timestamp: Date.now() - }); - return saveUser(req.user) - .flatMap(savedStory) - .subscribe( - function(story) { - res.json({ - storyLink: dasherize(story.storyLink) - }); - }, - next - ); - } }; From 0d0f4253ec876082c3e2c58fc2a637a5440a7ffa Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 7 Apr 2016 09:39:52 -0700 Subject: [PATCH 10/11] Remove console.logs --- server/boot/challenge.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/boot/challenge.js b/server/boot/challenge.js index 6496579ba3..c194311404 100644 --- a/server/boot/challenge.js +++ b/server/boot/challenge.js @@ -669,10 +669,8 @@ module.exports = function(app) { if (user && typeof user.getChallengeMap$ === 'function') { return user.getChallengeMap$(); } - console.log('foo'); return Observable.just({}); }) - .doOnNext(challengeMap => console.log('challengeMap', challengeMap)) .flatMap(challengeMap => getSuperBlocks$(challenge$, challengeMap)) .subscribe( superBlocks => { From 258597f053ffbc6e140a1dee6e5b10314f824fd0 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 7 Apr 2016 10:12:29 -0700 Subject: [PATCH 11/11] Fix news jade pages Remove unused pages --- server/boot/randomAPIs.js | 7 - server/boot/story.js | 22 ++- .../deprecated.jade} | 0 server/views/{ => news}/feed.jade | 0 server/views/news/index.jade | 48 +++++++ server/views/stories/hot-stories.jade | 90 ------------- server/views/stories/index.jade | 32 ----- server/views/stories/news-nav.jade | 127 ------------------ server/views/stories/preliminary-submit.jade | 51 ------- server/views/stories/show.jade | 41 ------ server/views/stories/submit-story.jade | 41 ------ 11 files changed, 57 insertions(+), 402 deletions(-) rename server/views/{resources/camper-news-deprecated.jade => news/deprecated.jade} (100%) rename server/views/{ => news}/feed.jade (100%) create mode 100644 server/views/news/index.jade delete mode 100644 server/views/stories/hot-stories.jade delete mode 100644 server/views/stories/index.jade delete mode 100644 server/views/stories/news-nav.jade delete mode 100644 server/views/stories/preliminary-submit.jade delete mode 100644 server/views/stories/show.jade delete mode 100644 server/views/stories/submit-story.jade diff --git a/server/boot/randomAPIs.js b/server/boot/randomAPIs.js index f12a270843..2995082fb2 100644 --- a/server/boot/randomAPIs.js +++ b/server/boot/randomAPIs.js @@ -43,7 +43,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('/news', news); router.get( '/the-fastest-web-page-on-the-internet', @@ -285,12 +284,6 @@ module.exports = function(app) { }); } - function news(req, res) { - res.render('resources/camper-news-deprecated', { - title: 'Camper News' - }); - } - function twitch(req, res) { res.redirect('https://twitch.tv/freecodecamp'); } diff --git a/server/boot/story.js b/server/boot/story.js index 6849812874..2a7a11be0b 100755 --- a/server/boot/story.js +++ b/server/boot/story.js @@ -41,6 +41,7 @@ module.exports = function(app) { const redirectToNews = (req, res) => res.redirect('/news'); const deprecated = (req, res) => res.sendStatus(410); + router.get('/news', showNews); router.post('/news/userstories', deprecated); router.get('/news/hot', hotJSON); router.get('/news/feed', RSSFeed); @@ -56,6 +57,10 @@ module.exports = function(app) { app.use(router); + function showNews(req, res) { + res.render('news/deprecated', { title: 'Camper News' }); + } + function replaceStoryWithNews(req, res) { var url = req.originalUrl.replace(/^\/stories/, '/news'); return res.redirect(url); @@ -72,7 +77,7 @@ module.exports = function(app) { storiesData$.subscribe( data => { res.set('Content-Type', 'text/xml'); - res.render('feed', { + res.render('news/feed', { title: 'FreeCodeCamp Camper News RSS Feed', description: 'RSS Feed for FreeCodeCamp Top 100 Hot Camper News', url: 'http://www.freecodecamp.com/news', @@ -105,25 +110,16 @@ module.exports = function(app) { return res.redirect('../stories/' + dashedNameFull); } - var username = req.user ? req.user.username : ''; - // true if any of votes are made by user - var userVoted = story.upVotes.some(function(upvote) { - return upvote.upVotedByUsername === username; - }); - - return res.render('stories/index', { - title: story.headline, + return res.render('news/index', { + title: story.headline || 'news', link: story.link, originalStoryLink: dashedName, author: story.author, rank: story.upVotes.length, - upVotes: story.upVotes, id: story.id, timeAgo: moment(story.timePosted).fromNow(), image: story.image, - page: 'show', - storyMetaDescription: story.metaDescription, - hasUserVoted: userVoted + storyMetaDescription: story.metaDescription }); }, next diff --git a/server/views/resources/camper-news-deprecated.jade b/server/views/news/deprecated.jade similarity index 100% rename from server/views/resources/camper-news-deprecated.jade rename to server/views/news/deprecated.jade diff --git a/server/views/feed.jade b/server/views/news/feed.jade similarity index 100% rename from server/views/feed.jade rename to server/views/news/feed.jade diff --git a/server/views/news/index.jade b/server/views/news/index.jade new file mode 100644 index 0000000000..c645be7c88 --- /dev/null +++ b/server/views/news/index.jade @@ -0,0 +1,48 @@ +extends ../layout +block content + h1.text-center Camper News + hr + .spacer + .row + .col-xs-12 + h2 We have discontinued Camper News in favor of our + a(href='http://reddit.com/r/freecodecamp')  Subreddit + | . + h3 Thank you to all of the campers who have contributed links over the past year. Our + a(href='http://reddit.com/r/freecodecamp')  Subreddit + |   is now the best place to share coding-related links. + .spacer + hr + #search-results + .spacer + #story-list + ul#stories + .spacer + h3.row + .col-xs-2.col-sm-1 + a(href="/" + author.username) + img(src="#{author.picture}", class='img-news') + .col-xs-10.col-sm-10 + .col-xs-12.negative-28 + a(href="#{link}", target="_blank") + h3= title + h6 + .col-xs-12.positive-15.hidden-element#image-display + .media + .media-left + img.url-preview.media-object(src="#{image}", alt="#{storyMetaDescription}") + .media-body + .col-xs-12.col-sm-12.col-md-6 + h4= storyMetaDescription + .col-xs-12 + .spacer + span#storyRank= rank + (rank > 1 ? " points" : " point") + |  ·  + span Posted #{timeAgo} + span  by  + a(href="/" + author.username) @#{author.username} + + script. + if (image) { + $('#image-display').removeClass('hidden-element') + } diff --git a/server/views/stories/hot-stories.jade b/server/views/stories/hot-stories.jade deleted file mode 100644 index 0666058399..0000000000 --- a/server/views/stories/hot-stories.jade +++ /dev/null @@ -1,90 +0,0 @@ -.spacer -.spacer -#story-list.story-list - script. - var getLinkedName = function getLinkedName(name) { - return name.trim().toLowerCase().replace(/\s/g, '-'); - } - $.ajax({ - url: '/news/hot', - type: 'GET' - }) - .success( - function(data) { - for (var i = 0; i < data.length; i++) { - var div = document.createElement('div'); - var linkedName = getLinkedName(data[i].storyLink); - var rank = data[i].rank; - var alreadyUpvoted = false; - if (typeof username !== 'undefined') { - alreadyUpvoted = data[i].upVotes.some(function(vote) { - return vote.upVotedByUsername === username - }); - } - - $(div) - .html( - "
" + - "
" + - "" + - "" + - "" + - "
" + - "
" + - "" + - data[i].headline + - "" + - "
" + - "
" + - rank + (rank > 1 ? " points" : " point") + " · posted " + - moment(data[i].timePosted).fromNow() + - " by @" + data[i].author.username + " " + - "
" + - "
" + - "
" + - (typeof username !== 'undefined' ? - "" : - "upvote") + - "" + - "
" + - "
" + - "" - ); - $(div).addClass('story-list news-box') - $(div).appendTo($('#story-list')); - $(div).find('.btn-upvote').each(function(idx, btn) { - var $btn = $(btn); - if (alreadyUpvoted) { - $btn.addClass('disabled'); - $btn.text('upvoted!'); - } - $btn.data('upVotes', data[i].upVotes); - }); - } - }); diff --git a/server/views/stories/index.jade b/server/views/stories/index.jade deleted file mode 100644 index 56958a7a08..0000000000 --- a/server/views/stories/index.jade +++ /dev/null @@ -1,32 +0,0 @@ -extends ../layout -block content - if (user) - script. - var isLoggedIn = true; - var userId = !{JSON.stringify(user.id)}; - var username = !{JSON.stringify(user.username)}; - else - script. - var isLoggedIn = false; - script. - var challengeName = 'Camper News'; - var page = !{JSON.stringify(page)}; - h1.text-center Camper News - hr - .spacer - include news-nav - .spacer - if (page === 'hot') - include hot-stories - if (page === 'submit') - if (user) - include preliminary-submit - else - .spacer - .text-center - a.btn.btn-cta.signup-btn.btn-primary(href="/login") Sign in to post your story (it's free) - .spacer - if (page === 'storySubmission') - include submit-story - if (page === 'show') - include show diff --git a/server/views/stories/news-nav.jade b/server/views/stories/news-nav.jade deleted file mode 100644 index 345b2eb895..0000000000 --- a/server/views/stories/news-nav.jade +++ /dev/null @@ -1,127 +0,0 @@ -.row - .col-xs-12 - h2 We are retiring Camper News in favor of our - a(href='http://reddit.com/r/freecodecamp') Subreddit - | . - h3 Thank you to all of the campers who have contributed links over the past year. We will keep Camper News accessible until May. Our - a(href='http://reddit.com/r/freecodecamp') Subreddit - |   is now the best place to share coding-related links. - .spacer - hr - .spacer - .col-xs-12.col-sm-3 - span - a.btn.btn-primary.btn-bigger.btn-block.btn-responsive(href='/stories/submit' class="#{ page === 'hot' ? '' : 'hidden' }") Submit a link - span - a.btn.btn-success.btn-bigger.btn-block.btn-responsive(href='/news/' class="#{ (page !== 'hot') ? '' : 'hidden' }") All - .visible-xs - .button-spacer - .col-xs-12.col-sm-9 - .input-group - input#searchArea.big-text-field.field-responsive.form-control(type='text', placeholder='Search term or @username') - span.input-group-btn - button#searchbutton.btn.btn-bigger.btn-primary.btn-responsive(type='button') Search - .spacer - -#search-results - -.spacer -#story-list - ul#stories -script. - $('#searchArea').keypress(function (event) { - if (event.keyCode === 13 || event.which === 13) { - executeSearch(); - $('#searchArea').focus(); - event.preventDefault(); - } - }); - $('#searchbutton').on('click', function () { - executeSearch(); - }); - function executeSearch() { - $('#stories').empty(); - var searchTerm = $('#searchArea').val(), - url = '/stories/search'; - if (searchTerm.match(/^\@\w+$/)) { - url = '/news/userstories'; - searchTerm = searchTerm.match(/^\@\w+$/)[0].split('@')[1]; - } - var getLinkedName = function getLinkedName(name) { - return name.toLowerCase().replace(/\s/g, '-'); - } - $.post(url, { search: searchTerm }) - .fail(function(xhr, textStatus, errorThrown) { - $('#search-results').empty(); - var div = document.createElement("div"); - $(div).html("

No Results Found

"); - $(div).appendTo($('#search-results')); - }) - .done(function(data, textStatus, xhr) { - $('#search-results').empty(); - var spacer = document.createElement('div'); - $(spacer).html("
"); - $(spacer).appendTo($('#search-results')); - for (var i = 0; i < data.length; i++) { - var div = document.createElement('div'); - var linkedName = getLinkedName(data[i].storyLink); - var rank = data[i].rank; - $(div).html( - "
" + - "
" + - "" + - "" + - "" + - "
" + - "
" + - "" + - data[i].headline + - "" + - "
" + - "
" + - "
" + - rank + (rank > 1 ? " points" : " point") + " · posted " + - moment(data[i].timePosted).fromNow() + - " by " + - "@" + data[i].author.username + - " " + - "
" + - "
" + - "
" + - "more info" + - "
" + - "
" + - "
" + - "") - $(div).addClass('story-list news-box-search') - $(div).appendTo($('#search-results')); - } - var hr = document.createElement("div"); - $(hr).html("

End search results

") - $(hr).appendTo($('#search-results')); - }); - } diff --git a/server/views/stories/preliminary-submit.jade b/server/views/stories/preliminary-submit.jade deleted file mode 100644 index 70af2d94ef..0000000000 --- a/server/views/stories/preliminary-submit.jade +++ /dev/null @@ -1,51 +0,0 @@ -form.input-group(id='URLSubmit' name='URLSubmit') - input#story-url.big-text-field.field-responsive.form-control(placeholder='Paste your link here', name='link', type='url', required, autofocus) - span.input-group-btn - button#preliminary-story-submit.btn.btn-bigger.btn-primary.btn-responsive(type='submit') Submit -.spacer - - script. - $('#story-url').on('keypress', function(e) { - if (e.keyCode === 13) { - $('#preliminary-story-submit').click(); - } - }); - function preliminaryStorySubmit(e) { - if (!$('#URLSubmit')[0].checkValidity()) { - return null; - } - e.preventDefault(); - - var storyURL = $('#story-url').val(); - $('#preliminary-story-submit').attr('disabled', 'disabled'); - - $.post('/stories/preliminary', - { - data: { - url: storyURL - } - }) - .fail(function (xhr, textStatus, errorThrown) { - $('#preliminary-story-submit').attr('disabled', false); - }) - .done(function (data, textStatus, xhr) { - if (data.alreadyPosted) { - window.location = data.storyURL; - } else { - window.location = '/stories/submit/new-story?url=' + - encodeURIComponent(data.storyURL) + - '&title=' + encodeURIComponent(data.storyTitle) + - '&image=' + encodeURIComponent(data.storyImage) + - '&description=' + encodeURIComponent(data.storyMetaDescription); - } - }); - } - - $('#preliminary-story-submit').on('click', preliminaryStorySubmit); - - arr = $( "h3 input:checked" ) - .map(function() { - return this.id; - }) - .get() - .join('&'); diff --git a/server/views/stories/show.jade b/server/views/stories/show.jade deleted file mode 100644 index cced0f41f5..0000000000 --- a/server/views/stories/show.jade +++ /dev/null @@ -1,41 +0,0 @@ -script. - var storyId = !{JSON.stringify(id)}; - var originalStoryLink = !{JSON.stringify(originalStoryLink)}; - var upVotes = !{JSON.stringify(upVotes)}; - var image = !{JSON.stringify(image)}; - var hasUserVoted = !{JSON.stringify(hasUserVoted)}; - -h3.row - .col-xs-2.col-sm-1 - a(href="/" + author.username) - img(src="#{author.picture}", class='img-news') - .col-xs-10.col-sm-10 - .col-xs-12.negative-28 - a(href="#{link}", target="_blank") - h3= title - h6 - .col-xs-12.positive-15.hidden-element#image-display - .media - .media-left - img.url-preview.media-object(src="#{image}", alt="#{storyMetaDescription}") - .media-body - .col-xs-12.col-sm-12.col-md-6 - h4= storyMetaDescription - .col-xs-12 - .spacer - if !hasUserVoted - a#upvote.btn.btn-no-shadow.btn-primary.btn-xs.btn-primary-ghost Upvote - |  ·  - else - a#upvote.btn.disabled.btn-no-shadow.btn-primary.btn-xs.btn-primary-ghost Upvoted! - |  ·  - span#storyRank= rank + (rank > 1 ? " points" : " point") - |  ·  - span Posted #{timeAgo} - span  by  - a(href="/" + author.username) @#{author.username} - -script. - if (image) { - $('#image-display').removeClass('hidden-element') - } diff --git a/server/views/stories/submit-story.jade b/server/views/stories/submit-story.jade deleted file mode 100644 index 66b91fc4f1..0000000000 --- a/server/views/stories/submit-story.jade +++ /dev/null @@ -1,41 +0,0 @@ -.spacer -.col-xs-12 - script. - var main = window.main || { init: [] }; - main.storyURL = !{JSON.stringify(storyURL)}; - main.storyTitle = !{JSON.stringify(storyTitle)}; - main.storyImage = !{JSON.stringify(storyImage)}; - main.storyMetaDescription = !{JSON.stringify(storyMetaDescription)}; - - form.form-horizontal.control-label-story-submission#story-submission-form(name="submitStory") - .col-xs-12 - .form-group - .col-xs-12.col-md-1 - label.control-label.control-label-story-submission(for='name') Link - .col-xs-12.col-md-11 - input#story-url.form-control(name='Link', disabled, value='#{storyURL}') - .form-group - .col-xs-12.col-md-1 - label.control-label.control-label-story-submission(for='name') Title - .col-xs-12.col-md-11 - input#story-title.form-control(value='#{storyTitle}', name='Title', maxlength='90', autocomplete="off", autofocus, required) - .form-group - .col-xs-12.col-md-offset-1 - span.pull-left#textarea_feedback - .form-group - .col-xs-11.col-md-offset-1 - .hidden-element#image-display - .media - .media-left - img.url-preview.media-object(src="#{storyImage}", alt="#{storyMetaDescription}") - .media-body - .col-xs-12 - p= storyMetaDescription - .spacer - .row - .form-group - button.btn.btn-bigger.btn-block.btn-primary#story-submit(type='submit', onclick="return false;") Submit - script. - if (main.storyImage) { - $('#image-display').removeClass('hidden-element'); - }