Merge branch 'staging' of github.com:FreeCodeCamp/freecodecamp into staging
This commit is contained in:
@ -95,7 +95,7 @@ DEBUG=true
|
||||
mongod
|
||||
|
||||
# Seed your database with the challenges
|
||||
node seed_data/seed.js
|
||||
node seed/
|
||||
|
||||
# start the application
|
||||
gulp
|
||||
|
7
common/config.global.js
Normal file
7
common/config.global.js
Normal file
@ -0,0 +1,7 @@
|
||||
// The path where to mount the REST API app
|
||||
exports.restApiRoot = '/api';
|
||||
//
|
||||
// The URL where the browser client can access the REST API is available
|
||||
// Replace with a full url (including hostname) if your client is being
|
||||
// served from a different server than your REST API.
|
||||
exports.restApiUrl = exports.restApiRoot;
|
16
common/models/User-Credential.json
Normal file
16
common/models/User-Credential.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "userCredential",
|
||||
"plural": "userCredentials",
|
||||
"base": "UserCredential",
|
||||
"properties": {},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "user",
|
||||
"foreignKey": "userId"
|
||||
}
|
||||
},
|
||||
"acls": [],
|
||||
"methods": []
|
||||
}
|
27
common/models/User-Identity.js
Normal file
27
common/models/User-Identity.js
Normal file
@ -0,0 +1,27 @@
|
||||
//var debug = require('debug')('freecc:models:userIdent');
|
||||
//
|
||||
//module.exports = function(UserIdent) {
|
||||
//
|
||||
// UserIdent.observe('before save', function(ctx, next) {
|
||||
//
|
||||
// var userIdent = ctx.instance;
|
||||
// userIdent.user(function(err, user) {
|
||||
// if (err) { return next(err); }
|
||||
// debug('got user', user.username);
|
||||
//
|
||||
// // check if user has picture
|
||||
// // set user.picture from twitter
|
||||
// if (!user.picture) {
|
||||
// debug('use has no pic');
|
||||
// user.picture = userIdent.profile.photos[0].value;
|
||||
// user.save(function(err) {
|
||||
// if (err) { return next(err); }
|
||||
// next();
|
||||
// });
|
||||
// } else {
|
||||
// debug('exiting after user ident');
|
||||
// next();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
//};
|
16
common/models/User-Identity.json
Normal file
16
common/models/User-Identity.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "userIdentity",
|
||||
"plural": "userIdentities",
|
||||
"base": "UserIdentity",
|
||||
"properties": {},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "user",
|
||||
"foreignKey": "userId"
|
||||
}
|
||||
},
|
||||
"acls": [],
|
||||
"methods": []
|
||||
}
|
19
common/models/User.js
Normal file
19
common/models/User.js
Normal file
@ -0,0 +1,19 @@
|
||||
var debug = require('debug')('freecc:models:user');
|
||||
|
||||
module.exports = function(User) {
|
||||
debug('setting up user hooks');
|
||||
/*
|
||||
* NOTE(berks): not sure if this is still needed
|
||||
User.observe('before save', function setUsername(ctx, next) {
|
||||
// set username from twitter
|
||||
if (ctx.instance.username && ctx.instance.username.match(/twitter/g)) {
|
||||
ctx.instance.username =
|
||||
ctx.instance.username.match(/twitter/g) ?
|
||||
ctx.instance.username.split('.').pop().toLowerCase() :
|
||||
ctx.instance.username;
|
||||
debug('username set', ctx.instance.username);
|
||||
}
|
||||
next();
|
||||
});
|
||||
*/
|
||||
};
|
@ -18,7 +18,7 @@
|
||||
"projectDescription": "string",
|
||||
"logoUrl": "string",
|
||||
"imageUrl": "string",
|
||||
"estimatedHours": 0,
|
||||
"estimatedHours": "number",
|
||||
"interestedCampers": [],
|
||||
"confirmedCampers": [],
|
||||
"currentStatus": "string"
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "bonfire",
|
||||
"name": "story",
|
||||
"base": "PersistedModel",
|
||||
"trackChanges": false,
|
||||
"idInjection": true,
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bonfire",
|
||||
"base": "PersistedModel",
|
||||
"name": "user",
|
||||
"base": "User",
|
||||
"trackChanges": false,
|
||||
"idInjection": true,
|
||||
"properties": {
|
||||
|
19
common/screens/App.jsx
Normal file
19
common/screens/App.jsx
Normal file
@ -0,0 +1,19 @@
|
||||
var React = require('react'),
|
||||
RouteHandler = require('react-router').RouteHandler,
|
||||
|
||||
// ## components
|
||||
Nav = require('./nav'),
|
||||
Footer = require('./footer');
|
||||
|
||||
var App = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<Nav />
|
||||
<RouteHandler />
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
module.exports = App;
|
34
common/screens/Router.jsx
Normal file
34
common/screens/Router.jsx
Normal file
@ -0,0 +1,34 @@
|
||||
var React = require('react'),
|
||||
|
||||
// react router
|
||||
Router = require('react-router'),
|
||||
Route = Router.Route,
|
||||
// NotFound = Router.NotFoundRoute,
|
||||
DefaultRoute = Router.DefaultRoute,
|
||||
|
||||
// # Components
|
||||
App = require('./App.jsx'),
|
||||
Bonfires = require('./bonfires');
|
||||
|
||||
var routes = (
|
||||
<Route
|
||||
name='app'
|
||||
path='/'
|
||||
handler={ App }>
|
||||
|
||||
<Route
|
||||
name='bonfires'
|
||||
path='/bonfires/?:bonfireName?'
|
||||
handler={ Bonfires } />
|
||||
|
||||
<DefaultRoute
|
||||
handler={ Bonfires } />
|
||||
</Route>
|
||||
);
|
||||
|
||||
module.exports = function(Location) {
|
||||
return Router.create({
|
||||
routes: routes,
|
||||
location: Location
|
||||
});
|
||||
};
|
63
common/screens/bonfires/Actions.js
Normal file
63
common/screens/bonfires/Actions.js
Normal file
@ -0,0 +1,63 @@
|
||||
var Action = require('thundercats').Action,
|
||||
executeBonfire = require('./executeBonfire'),
|
||||
getModel = require('../../utils/getModel'),
|
||||
debug = require('debug')('freecc:common:bonfires');
|
||||
|
||||
var BonfireActions = Action.createActions([
|
||||
'setUserCode',
|
||||
'testUserCode',
|
||||
'setResults',
|
||||
'setDisplay',
|
||||
'setBonfire',
|
||||
'getBonfire',
|
||||
'handleBonfireError',
|
||||
'openCompletionModal'
|
||||
]);
|
||||
|
||||
BonfireActions
|
||||
.getBonfire
|
||||
.subscribe(function(params) {
|
||||
var Bonfire = getModel('bonfire');
|
||||
var bonfireName = params.bonfireName ?
|
||||
params.bonfireName.replace(/\-/g, ' ') :
|
||||
'meet bonfire';
|
||||
debug('getting bonfire for: ', bonfireName);
|
||||
var regQuery = { name: { like: bonfireName, options: 'i' } };
|
||||
Bonfire.find(
|
||||
{ where: regQuery },
|
||||
function(err, bonfire) {
|
||||
if (err) {
|
||||
return debug('bonfire get err', err);
|
||||
}
|
||||
if (!bonfire || bonfire.length < 1) {
|
||||
return debug('404 no bonfire found for ', bonfireName);
|
||||
}
|
||||
bonfire = bonfire.pop();
|
||||
if (bonfire) {
|
||||
debug(
|
||||
'found bonfire %s for route %s',
|
||||
bonfire.name,
|
||||
bonfireName
|
||||
);
|
||||
}
|
||||
BonfireActions.setBonfire(bonfire);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
BonfireActions
|
||||
.testUserCode
|
||||
.subscribe(function({ userCode, tests }) {
|
||||
debug('test bonfire');
|
||||
executeBonfire(userCode, tests, function(err, { output, results }) {
|
||||
if (err) {
|
||||
debug('error running tests', err);
|
||||
return BonfireActions.setDisplay(err);
|
||||
}
|
||||
BonfireActions.setDisplay(output);
|
||||
BonfireActions.setResults(results);
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = BonfireActions;
|
99
common/screens/bonfires/Bonfires.jsx
Normal file
99
common/screens/bonfires/Bonfires.jsx
Normal file
@ -0,0 +1,99 @@
|
||||
var React = require('react'),
|
||||
|
||||
// ## mixins
|
||||
{ ObservableStateMixin } = require('thundercats'),
|
||||
|
||||
// ## components
|
||||
SidePanel = require('./SidePanel.jsx'),
|
||||
Results = require('./Results.jsx'),
|
||||
Display = require('../displayCode'),
|
||||
Editor = require('../editor'),
|
||||
{ Grid, Row, Col } = require('react-bootstrap'),
|
||||
|
||||
// ## flux
|
||||
BonfireActions = require('./Actions'),
|
||||
BonfireStore = require('./Store');
|
||||
|
||||
var Bonfire = React.createClass({
|
||||
|
||||
mixins: [ObservableStateMixin],
|
||||
|
||||
contextTypes: {
|
||||
makePath: React.PropTypes.func.isRequired,
|
||||
replaceWith: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
getObservable: function() {
|
||||
return BonfireStore;
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
// get history object
|
||||
var his = typeof window !== 'undefined' ? window.history : null;
|
||||
// spinal-case bonfireName
|
||||
var bonfireName = this.state.name.toLowerCase().replace(/\s/g, '-');
|
||||
// create proper URI from react-router
|
||||
var path = this.context.makePath('bonfires', { bonfireName: bonfireName });
|
||||
|
||||
// if html5 push state exists, update URI
|
||||
// else we are using hash location and should just cause a re render
|
||||
if (his) {
|
||||
his.replaceState({ path: path }, '', path);
|
||||
} else {
|
||||
this.context.replaceWith('bonfires', { bonfireName: bonfireName});
|
||||
}
|
||||
},
|
||||
|
||||
_onTestBonfire: function() {
|
||||
BonfireActions.testUserCode({
|
||||
userCode: this.state.userCode,
|
||||
tests: this.state.tests
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var {
|
||||
name,
|
||||
userCode,
|
||||
difficulty,
|
||||
description,
|
||||
results,
|
||||
display
|
||||
} = this.state;
|
||||
var brief = description.slice(0, 1).pop();
|
||||
|
||||
// convert bonfire difficulty from floating point string
|
||||
// to integer.
|
||||
var difficultyInt = Math.floor(+difficulty);
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Row>
|
||||
<Col
|
||||
xs={ 12 }
|
||||
md={ 4 }>
|
||||
<SidePanel
|
||||
name={ name }
|
||||
brief={ brief }
|
||||
difficulty={ difficultyInt }
|
||||
onTestBonfire={ this._onTestBonfire }
|
||||
description={ description.length > 1 ? description : [] }/>
|
||||
<Display
|
||||
value={ display }/>
|
||||
<Results
|
||||
results={ results }/>
|
||||
</Col>
|
||||
<Col
|
||||
xs={ 12 }
|
||||
md={ 8 }>
|
||||
<Editor
|
||||
onValueChange={ BonfireActions.setUserCode }
|
||||
value={ userCode }/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Bonfire;
|
62
common/screens/bonfires/Results.jsx
Normal file
62
common/screens/bonfires/Results.jsx
Normal file
@ -0,0 +1,62 @@
|
||||
var React = require('react'),
|
||||
classNames = require('classnames'),
|
||||
{ Grid, Row, Col } = require('react-bootstrap');
|
||||
|
||||
var Results = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
results: React.PropTypes.array
|
||||
},
|
||||
|
||||
_renderText: function(text, textClass) {
|
||||
return (
|
||||
<Col
|
||||
xs={ 11 }
|
||||
className={ classNames(textClass) }>
|
||||
{ text }
|
||||
</Col>
|
||||
);
|
||||
},
|
||||
|
||||
_renderResult: function(results) {
|
||||
return results.map(function(result, idx) {
|
||||
var err = result.err;
|
||||
var iconClass = {
|
||||
'ion-close-circled big-error-icon': err,
|
||||
'ion-checkmark-circled big-success-icon': !err
|
||||
};
|
||||
var textClass = {
|
||||
'test-output wrappable': true,
|
||||
'test-vertical-center': !err
|
||||
};
|
||||
return (
|
||||
<div key={ idx }>
|
||||
<Row>
|
||||
<Col
|
||||
xs={ 1 }
|
||||
className='text-center'>
|
||||
<i className={ classNames(iconClass) }></i>
|
||||
</Col>
|
||||
{ this._renderText(result.text, textClass) }
|
||||
{ err ? this._renderText(err, textClass) : null }
|
||||
</Row>
|
||||
<div className='ten-pixel-break'></div>
|
||||
</div>
|
||||
);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var results = this.props.results;
|
||||
if (!results || results.length && results.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Grid>
|
||||
{ this._renderResult(this.props.results) }
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Results;
|
129
common/screens/bonfires/SidePanel.jsx
Normal file
129
common/screens/bonfires/SidePanel.jsx
Normal file
@ -0,0 +1,129 @@
|
||||
var React = require('react'),
|
||||
|
||||
// ## components
|
||||
{
|
||||
Well,
|
||||
Row,
|
||||
Col,
|
||||
Button,
|
||||
} = require('react-bootstrap');
|
||||
|
||||
var SidePanel = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
name: React.PropTypes.string,
|
||||
brief: React.PropTypes.string,
|
||||
description: React.PropTypes.array,
|
||||
difficulty: React.PropTypes.number,
|
||||
onTestBonfire: React.PropTypes.func
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
name: 'Welcome to Bonfires!',
|
||||
difficulty: 5,
|
||||
brief: 'This is a brief description'
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
isMoreInfoOpen: false
|
||||
};
|
||||
},
|
||||
|
||||
_toggleMoreInfo: function() {
|
||||
this.setState({
|
||||
isMoreInfoOpen: !this.state.isMoreInfoOpen
|
||||
});
|
||||
},
|
||||
|
||||
_renderFlames: function() {
|
||||
var difficulty = this.props.difficulty;
|
||||
|
||||
return [1, 2, 3, 4, 5].map(num => {
|
||||
var className = 'ion-ios-flame';
|
||||
if (num > difficulty) {
|
||||
className += '-outline';
|
||||
}
|
||||
return (
|
||||
<i
|
||||
key={ num }
|
||||
className={ className }/>
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
_renderMoreInfo: function(isDescription) {
|
||||
var description = this.props.description.map((sentance, index) => {
|
||||
return <p key={ index }>{ sentance }</p>;
|
||||
});
|
||||
|
||||
if (isDescription && this.state.isMoreInfoOpen) {
|
||||
return (
|
||||
<Row>
|
||||
<Col xs={ 12 }>
|
||||
{ description }
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_renderMoreInfoButton: function(isDescription) {
|
||||
if (isDescription) {
|
||||
return (
|
||||
<Button
|
||||
onClick={ this._toggleMoreInfo }
|
||||
bsStyle='primary'
|
||||
block={ true }
|
||||
className='btn-primary-ghost'>
|
||||
<span className='ion-arrow-down-b'></span>
|
||||
More information
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var isDescription = this.props.description &&
|
||||
this.props.description.length > 1;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className='text-center'>{ this.props.name }</h1>
|
||||
<h2 className='text-center'>
|
||||
<div className='bonfire-flames'>
|
||||
Difficulty:
|
||||
{ this._renderFlames() }
|
||||
</div>
|
||||
</h2>
|
||||
<Well>
|
||||
<Row>
|
||||
<Col xs={ 12 }>
|
||||
<div className='bonfire-instructions'>
|
||||
<p>{ this.props.brief }</p>
|
||||
<div>
|
||||
{ this._renderMoreInfo(isDescription) }
|
||||
{ this._renderMoreInfoButton(isDescription) }
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Well>
|
||||
<Button
|
||||
bsStyle='primary'
|
||||
block={ true }
|
||||
className='btn-big'
|
||||
onClick={ this.props.onTestBonfire }>
|
||||
Run Code (ctrl + enter)
|
||||
</Button>
|
||||
<br />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SidePanel;
|
67
common/screens/bonfires/Store.js
Normal file
67
common/screens/bonfires/Store.js
Normal file
@ -0,0 +1,67 @@
|
||||
var BonfiresActions = require('./Actions');
|
||||
var { Store, setStateUtil } = require('thundercats');
|
||||
|
||||
var BonfiresStore = Store.create({
|
||||
|
||||
getInitialValue: function() {
|
||||
return {
|
||||
userCode: 'console.log(\'FreeCodeCamp!\')',
|
||||
difficulty: 0,
|
||||
description: [
|
||||
'default state'
|
||||
],
|
||||
tests: [],
|
||||
results: null
|
||||
};
|
||||
},
|
||||
|
||||
getOperations: function() {
|
||||
var {
|
||||
setBonfire,
|
||||
setUserCode,
|
||||
setResults,
|
||||
setDisplay
|
||||
} = BonfiresActions;
|
||||
|
||||
return [
|
||||
setBonfire
|
||||
.map(function(bonfire) {
|
||||
var {
|
||||
name,
|
||||
description,
|
||||
difficulty,
|
||||
tests
|
||||
} = bonfire;
|
||||
var userCode = bonfire.challengeSeed;
|
||||
return {
|
||||
name,
|
||||
userCode,
|
||||
tests,
|
||||
description,
|
||||
difficulty
|
||||
};
|
||||
})
|
||||
.map(setStateUtil),
|
||||
|
||||
setUserCode
|
||||
.map(function(userCode) {
|
||||
return { userCode };
|
||||
})
|
||||
.map(setStateUtil),
|
||||
|
||||
setDisplay
|
||||
.map(function(display) {
|
||||
return { display };
|
||||
})
|
||||
.map(setStateUtil),
|
||||
|
||||
setResults
|
||||
.map(function(results) {
|
||||
return { results };
|
||||
})
|
||||
.map(setStateUtil)
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = BonfiresStore;
|
27
common/screens/bonfires/executeBonfire.js
Normal file
27
common/screens/bonfires/executeBonfire.js
Normal file
@ -0,0 +1,27 @@
|
||||
var debug = require('debug')('freecc:executebonfire');
|
||||
var {
|
||||
addTests,
|
||||
runTests,
|
||||
testCode
|
||||
} = require('../../utils');
|
||||
|
||||
module.exports = executeBonfire;
|
||||
|
||||
function executeBonfire(userCode, tests, cb) {
|
||||
|
||||
// TODO: move this into componentDidMount
|
||||
// ga('send', 'event', 'Bonfire', 'ran-code', bonfireName);
|
||||
var testSalt = Math.random();
|
||||
var { preppedCode, userTests } = addTests(userCode, tests, testSalt);
|
||||
|
||||
debug('sending code to web worker for testing');
|
||||
testCode(preppedCode, function(err, data) {
|
||||
if (err) { return cb(err); }
|
||||
var results = runTests(userTests, data, testSalt);
|
||||
debug('testing complete', results);
|
||||
cb(null, {
|
||||
output: data.output,
|
||||
results
|
||||
});
|
||||
});
|
||||
}
|
1
common/screens/bonfires/index.js
Normal file
1
common/screens/bonfires/index.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = require('./Bonfires.jsx');
|
34
common/screens/context/Actions.js
Normal file
34
common/screens/context/Actions.js
Normal file
@ -0,0 +1,34 @@
|
||||
var debug = require('debug')('freecc:context'),
|
||||
BonfireActions = require('../bonfires/Actions'),
|
||||
BonfireStore = require('../bonfires/Store');
|
||||
|
||||
var {
|
||||
Action,
|
||||
waitFor
|
||||
} = require('thundercats');
|
||||
|
||||
var actions = Action.createActions([
|
||||
'setContext',
|
||||
'renderToUser'
|
||||
]);
|
||||
|
||||
actions
|
||||
.setContext
|
||||
.filter(function(ctx) {
|
||||
return ctx.state.path.indexOf('/bonfire') !== -1;
|
||||
})
|
||||
.subscribe(function(ctx) {
|
||||
debug('set ctx');
|
||||
BonfireActions.getBonfire(ctx.state.params);
|
||||
waitFor(BonfireStore)
|
||||
.firstOrDefault()
|
||||
.catch(function(err) {
|
||||
// handle timeout error
|
||||
debug('err', err);
|
||||
})
|
||||
.subscribe(function() {
|
||||
actions.renderToUser(ctx);
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = actions;
|
18
common/screens/context/Store.js
Normal file
18
common/screens/context/Store.js
Normal file
@ -0,0 +1,18 @@
|
||||
var Store = require('thundercats').Store,
|
||||
ContextActions = require('./Actions');
|
||||
|
||||
var ContextStore = Store.create({
|
||||
getInitialValue: function() {
|
||||
return {};
|
||||
},
|
||||
|
||||
getOperations: function() {
|
||||
return ContextActions
|
||||
.renderToUser
|
||||
.map(function(ctx) {
|
||||
return { value: ctx };
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ContextStore;
|
51
common/screens/displayCode/Display.jsx
Normal file
51
common/screens/displayCode/Display.jsx
Normal file
@ -0,0 +1,51 @@
|
||||
var React = require('react'),
|
||||
Tailspin = require('tailspin');
|
||||
|
||||
var Editor = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
value: React.PropTypes.string
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
value: [
|
||||
'/**',
|
||||
'* Your output will go here.',
|
||||
'* Console.log() -type statements',
|
||||
'* will appear in your browser\'s',
|
||||
'* DevTools JavaScript console.',
|
||||
'**/'
|
||||
].join('\n')
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var value = this.props.value;
|
||||
var options = {
|
||||
lineNumbers: false,
|
||||
lineWrapping: true,
|
||||
mode: 'text',
|
||||
readOnly: 'noCursor',
|
||||
textAreaClassName: 'hide-textarea',
|
||||
theme: 'monokai',
|
||||
value: value
|
||||
};
|
||||
|
||||
var config = {
|
||||
setSize: ['100%', '100%']
|
||||
};
|
||||
|
||||
return (
|
||||
<form className='code'>
|
||||
<div className='form-group codeMirrorView'>
|
||||
<Tailspin
|
||||
{ ...options }
|
||||
config={ config }/>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Editor;
|
1
common/screens/displayCode/index.js
Normal file
1
common/screens/displayCode/index.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = require('./Display.jsx');
|
91
common/screens/editor/Editor.jsx
Normal file
91
common/screens/editor/Editor.jsx
Normal file
@ -0,0 +1,91 @@
|
||||
var React = require('react'),
|
||||
debug = require('debug')('freecc:comp:editor'),
|
||||
jshint = require('jshint').JSHINT,
|
||||
Tailspin = require('tailspin');
|
||||
|
||||
var Editor = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
onValueChange: React.PropTypes.func,
|
||||
value: React.PropTypes.string
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
value: 'console.log(\'freeCodeCamp is awesome\')'
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
value: this.props.value
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var options = {
|
||||
autoCloseBrackets: true,
|
||||
gutters: ['CodeMirror-lint-markers'],
|
||||
lint: true,
|
||||
linter: jshint,
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
mode: 'javascript',
|
||||
matchBrackets: true,
|
||||
runnable: true,
|
||||
scrollbarStyle: 'null',
|
||||
theme: 'monokai',
|
||||
textAreaClassName: 'hide-textarea',
|
||||
value: this.state.value,
|
||||
onChange: e => {
|
||||
this.setState({ value: e.target.value});
|
||||
if (typeof this.props.onValueChange === 'function') {
|
||||
this.props.onValueChange(e.target.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var config = {
|
||||
setSize: ['100%', 'auto'],
|
||||
extraKeys: {
|
||||
Tab: function(cm) {
|
||||
debug('tab pressed');
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection('add');
|
||||
} else {
|
||||
var spaces = new Array(cm.getOption('indentUnit') + 1).join(' ');
|
||||
cm.replaceSelection(spaces);
|
||||
}
|
||||
},
|
||||
'Shift-Tab': function(cm) {
|
||||
debug('shift-tab pressed');
|
||||
if (cm.somethingSelected()) {
|
||||
cm.indentSelection('subtract');
|
||||
} else {
|
||||
var spaces = new Array(cm.getOption('indentUnit') + 1).join(' ');
|
||||
cm.replaceSelection(spaces);
|
||||
}
|
||||
},
|
||||
'Ctrl-Enter': function() {
|
||||
debug('C-enter pressed');
|
||||
// execute bonfire action
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div id='mainEditorPanel'>
|
||||
<form className='code'>
|
||||
<div className='form-group codeMirrorView'>
|
||||
<Tailspin
|
||||
{ ...options }
|
||||
config={ config }/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Editor;
|
1
common/screens/editor/index.js
Normal file
1
common/screens/editor/index.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = require('./Editor.jsx');
|
106
common/screens/footer/Footer.jsx
Normal file
106
common/screens/footer/Footer.jsx
Normal file
@ -0,0 +1,106 @@
|
||||
var React = require('react');
|
||||
|
||||
var Footer = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div className='fcc-footer'>
|
||||
<div className='col-xs-12 hidden-xs hidden-sm'>
|
||||
<a
|
||||
href='http://blog.freecodecamp.com'
|
||||
target='_blank' className='ion-speakerphone'>
|
||||
Blog
|
||||
</a>
|
||||
<a
|
||||
ref='http://www.twitch.tv/freecodecamp'
|
||||
target='_blank' className='ion-social-twitch-outline'>
|
||||
Twitch
|
||||
</a>
|
||||
<a
|
||||
href='http://github.com/freecodecamp'
|
||||
target='_blank'
|
||||
className='ion-social-github'>
|
||||
Github
|
||||
</a>
|
||||
<a
|
||||
href='http://twitter.com/freecodecamp'
|
||||
target='_blank' className='ion-social-twitter'>
|
||||
Twitter
|
||||
</a>
|
||||
<a
|
||||
href='http://facebook.com/freecodecamp'
|
||||
target='_blank'
|
||||
className='ion-social-facebook'>
|
||||
Facebook
|
||||
</a>
|
||||
<a
|
||||
ref='/learn-to-code'
|
||||
className='ion-information-circled'>
|
||||
About
|
||||
</a>
|
||||
<a
|
||||
href='/privacy'
|
||||
className='ion-locked'>
|
||||
Privacy
|
||||
</a>
|
||||
</div>
|
||||
<div className='col-xs-12 visible-xs visible-sm'>
|
||||
<a
|
||||
href='http://blog.freecodecamp.com'
|
||||
target='_blank' className='ion-speakerphone'>
|
||||
<span className='sr-only'>
|
||||
Free Code Camp\'s Blog
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href='http://www.twitch.tv/freecodecamp'
|
||||
target='_blank'
|
||||
className='ion-social-twitch-outline'>
|
||||
<span className='sr-only'>
|
||||
Free Code Camp Live Pair Programming on Twitch.tv
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href='http://github.com/freecodecamp'
|
||||
target='_blank'
|
||||
className='ion-social-github'>
|
||||
<span className='sr-only'>
|
||||
Free Code Camp on GitHub
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href='http://twitter.com/freecodecamp'
|
||||
target='_blank'
|
||||
className='ion-social-twitter'>
|
||||
<span className='sr-only'>
|
||||
Free Code Camp on Twitter
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href='http://facebook.com/freecodecamp'
|
||||
target='_blank'
|
||||
className='ion-social-facebook'>
|
||||
<span className='sr-only'>
|
||||
Free Code Camp on Facebook
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href='/learn-to-code'
|
||||
className='ion-information-circled'>
|
||||
<span className='sr-only'>
|
||||
About Free Code Camp
|
||||
</span>
|
||||
</a>
|
||||
<a
|
||||
href='/privacy'
|
||||
className='ion-locked'>
|
||||
<span className='sr-only'>
|
||||
Free Code Camp's Privacy Policy
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Footer;
|
1
common/screens/footer/index.js
Normal file
1
common/screens/footer/index.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = require('./Footer.jsx');
|
81
common/screens/nav/Nav.jsx
Normal file
81
common/screens/nav/Nav.jsx
Normal file
@ -0,0 +1,81 @@
|
||||
var React = require('react'),
|
||||
bootStrap = require('react-bootstrap'),
|
||||
Navbar = bootStrap.Navbar,
|
||||
Nav = bootStrap.Nav,
|
||||
NavItem = bootStrap.NavItem,
|
||||
NavItemFCC = require('./NavItem.jsx');
|
||||
|
||||
var NavBarComp = React.createClass({
|
||||
|
||||
propTypes: { signedIn: React.PropTypes.bool },
|
||||
|
||||
getDefaultProps: function() {
|
||||
return { signedIn: false };
|
||||
},
|
||||
|
||||
_renderBrand: function() {
|
||||
var fCClogo = 'https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg';
|
||||
return (
|
||||
<a href='/'>
|
||||
<img
|
||||
src={ fCClogo }
|
||||
alt='learn to code javascript at Free Code Camp logo'
|
||||
className='img-responsive nav-logo' />
|
||||
</a>
|
||||
);
|
||||
},
|
||||
|
||||
_renderSignin: function() {
|
||||
if (this.props.signedIn) {
|
||||
return (
|
||||
<NavItem
|
||||
eventKey={ 2 }>
|
||||
Show Picture
|
||||
</NavItem>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<NavItemFCC
|
||||
eventKey={ 2 }
|
||||
href='/login'
|
||||
aClassName='btn signup-btn signup-btn-nav'>
|
||||
Sign In
|
||||
</NavItemFCC>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
||||
return (
|
||||
<Navbar
|
||||
brand={ this._renderBrand() }
|
||||
fixedTop={ true }
|
||||
toggleNavKey={ 0 }
|
||||
className='nav-height'>
|
||||
<Nav
|
||||
right={ true }
|
||||
eventKey={ 0 }
|
||||
className='hamburger-dropdown'>
|
||||
<NavItem
|
||||
eventKey={ 1 }
|
||||
href='/Challenges'>
|
||||
Challenges
|
||||
</NavItem>
|
||||
<NavItem
|
||||
eventKey={ 1 }
|
||||
href='Chat'>
|
||||
Chat
|
||||
</NavItem>
|
||||
<NavItem
|
||||
eventKey={ 2 }
|
||||
href='/bonfires'>
|
||||
Bonfires
|
||||
</NavItem>
|
||||
{ this._renderSignin() }
|
||||
</Nav>
|
||||
</Navbar>
|
||||
);
|
||||
}
|
||||
});
|
||||
module.exports = NavBarComp;
|
66
common/screens/nav/NavItem.jsx
Normal file
66
common/screens/nav/NavItem.jsx
Normal file
@ -0,0 +1,66 @@
|
||||
var React = require('react/addons');
|
||||
var joinClasses = require('react-bootstrap/lib/utils/joinClasses');
|
||||
var classSet = React.addons.classSet;
|
||||
var BootstrapMixin = require('react-bootstrap').BootstrapMixin;
|
||||
|
||||
var NavItem = React.createClass({
|
||||
mixins: [BootstrapMixin],
|
||||
|
||||
propTypes: {
|
||||
onSelect: React.PropTypes.func,
|
||||
active: React.PropTypes.bool,
|
||||
disabled: React.PropTypes.bool,
|
||||
href: React.PropTypes.string,
|
||||
title: React.PropTypes.string,
|
||||
eventKey: React.PropTypes.any,
|
||||
target: React.PropTypes.string
|
||||
},
|
||||
|
||||
getDefaultProps: function () {
|
||||
return {
|
||||
href: '#'
|
||||
};
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var {
|
||||
disabled,
|
||||
active,
|
||||
href,
|
||||
title,
|
||||
target,
|
||||
children,
|
||||
} = this.props,
|
||||
props = this.props,
|
||||
classes = {
|
||||
'active': active,
|
||||
'disabled': disabled
|
||||
};
|
||||
|
||||
return (
|
||||
<li {...props} className={joinClasses(props.className, classSet(classes))}>
|
||||
<a
|
||||
href={href}
|
||||
title={title}
|
||||
target={target}
|
||||
className={ this.props.aClassName }
|
||||
onClick={this.handleClick}
|
||||
ref="anchor">
|
||||
{ children }
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
},
|
||||
|
||||
handleClick: function (e) {
|
||||
if (this.props.onSelect) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!this.props.disabled) {
|
||||
this.props.onSelect(this.props.eventKey, this.props.href, this.props.target);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = NavItem;
|
1
common/screens/nav/index.js
Normal file
1
common/screens/nav/index.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = require('./Nav.jsx');
|
27
package.json
27
package.json
@ -57,27 +57,28 @@
|
||||
"less": "~1.7.5",
|
||||
"less-middleware": "~2.0.1",
|
||||
"lodash": "~2.4.1",
|
||||
"loopback": "^2.18.0",
|
||||
"loopback-boot": "^2.8.0",
|
||||
"loopback-component-passport": "^1.3.1",
|
||||
"loopback-connector-mongodb": "^1.10.0",
|
||||
"lusca": "~1.0.2",
|
||||
"method-override": "~2.3.0",
|
||||
"moment": "~2.10.2",
|
||||
"mongodb": "~1.4.33",
|
||||
"mongoose": "~4.0.1",
|
||||
"mongoose-long": "0.0.2",
|
||||
"mongodb": "^2.0.33",
|
||||
"morgan": "~1.5.0",
|
||||
"node-slack": "0.0.7",
|
||||
"nodemailer": "~1.3.0",
|
||||
"passport": "~0.2.1",
|
||||
"passport-facebook": "~1.0.3",
|
||||
"passport-github": "~0.1.5",
|
||||
"passport-google-oauth": "~0.1.5",
|
||||
"passport-linkedin-oauth2": "~1.2.1",
|
||||
"passport-local": "~1.0.0",
|
||||
"passport-oauth": "~1.0.0",
|
||||
"passport-twitter": "~1.0.2",
|
||||
"passport-facebook": "^2.0.0",
|
||||
"passport-google-oauth": "^0.2.0",
|
||||
"passport-google-oauth2": "^0.1.6",
|
||||
"passport-linkedin-oauth2": "^1.2.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"passport-oauth": "^1.0.0",
|
||||
"passport-twitter": "^1.0.3",
|
||||
"ramda": "~0.10.0",
|
||||
"request": "~2.53.0",
|
||||
"rx": "^2.5.3",
|
||||
"sanitize-html": "~1.6.1",
|
||||
"sitemap": "~0.7.4",
|
||||
"twit": "~1.1.20",
|
||||
"uglify-js": "~2.4.15",
|
||||
"validator": "~3.22.1",
|
||||
@ -94,7 +95,7 @@
|
||||
"gulp": "~3.8.8",
|
||||
"gulp-eslint": "~0.9.0",
|
||||
"gulp-inject": "~1.0.2",
|
||||
"gulp-nodemon": "~1.0.4",
|
||||
"gulp-nodemon": "^2.0.3",
|
||||
"mocha": "~2.0.1",
|
||||
"multiline": "~1.0.1",
|
||||
"supertest": "~0.15.0"
|
||||
|
@ -237,7 +237,7 @@
|
||||
"name": "Waypoint: Fill in the Blank with Placeholder Text",
|
||||
"difficulty": 0.015,
|
||||
"description": [
|
||||
"Replace the text inside your <code>p</code> element with the first few words of the provided \"Ktty Ipsum\" text.",
|
||||
"Replace the text inside your <code>p</code> element with the first few words of the provided \"Kitty Ipsum\" text.",
|
||||
"Web developers traditionally use \"Lorem Ipsum\" text as placeholder text. It's called \"Lorem Ipsum\" text because those are the first two words of a famous passage by Cicero of Ancient Rome.",
|
||||
"\"Lorem Ipsum\" text has been used as placeholder text by typesetters since the 16th century, and this tradition continues on the web.",
|
||||
"Well, 5 centuries is long enough. Since we're building a CatPhotoApp, let's use something called \"Kitty Ipsum\"!",
|
@ -683,7 +683,7 @@
|
||||
" <li>Fork the Free Code Camp repository and <code>open seed_data/bonfires.json</code> to become familiar with the format of our bonfires.</li>",
|
||||
" <li>Regardless of your bonfire's difficulty, put it as the last bonfire in the JSON file. Change one of the numbers in the ID to ensure that your bonfire has a unique ID.</li>",
|
||||
" <li>In the terminal, run <code>node seed_data/seed.js</code>. Run <code>gulp</code>. You should be able to navigate to your new bonfire in the challenge map. Whenever you make a change to bonfire.json, you'll need to reseed in order to see these changes in the browser.</li>",
|
||||
" <li>Solved your own Bonfire. Confirmed that your tests work as expected and that your instructions are sufficiently clear.</li>",
|
||||
" <li>Solve your own Bonfire. Confirm that your tests work as expected and that your instructions are sufficiently clear.</li>",
|
||||
" <li>Submit a pull request to Free Code Camp's Staging branch and in the pull request body, link to a gist that has your algorithmic solution.</li>",
|
||||
" </ol>",
|
||||
" </p>",
|
99
seed/index.js
Normal file
99
seed/index.js
Normal file
@ -0,0 +1,99 @@
|
||||
/* eslint-disable no-process-exit */
|
||||
require('dotenv').load();
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
app = require('../server/server'),
|
||||
fieldGuides = require('./field-guides.json'),
|
||||
nonprofits = require('./nonprofits.json'),
|
||||
jobs = require('./jobs.json');
|
||||
|
||||
var Challenge = app.models.Challenge;
|
||||
var FieldGuide = app.models.FieldGuide;
|
||||
var Nonprofit = app.models.Nonprofit;
|
||||
var Job = app.models.Job;
|
||||
var counter = 0;
|
||||
var challenges = fs.readdirSync(path.join(__dirname, '/challenges'));
|
||||
var offerings = 3 + challenges.length;
|
||||
|
||||
var CompletionMonitor = function() {
|
||||
counter++;
|
||||
console.log('call ' + counter);
|
||||
|
||||
if (counter < offerings) {
|
||||
return;
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
Challenge.destroyAll(function(err, info) {
|
||||
if (err) {
|
||||
console.err(err);
|
||||
} else {
|
||||
console.log('Deleted ', info);
|
||||
}
|
||||
challenges.forEach(function (file) {
|
||||
Challenge.create(
|
||||
require('./challenges/' + file).challenges,
|
||||
function (err) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('Successfully parsed %s', file);
|
||||
CompletionMonitor();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
FieldGuide.destroyAll(function(err, info) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('Deleted ', info);
|
||||
}
|
||||
FieldGuide.create(fieldGuides, function(err, data) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('Saved ', data);
|
||||
}
|
||||
CompletionMonitor();
|
||||
console.log('field guides');
|
||||
});
|
||||
});
|
||||
|
||||
Nonprofit.destroyAll(function(err, info) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('Deleted ', info);
|
||||
}
|
||||
Nonprofit.create(nonprofits, function(err, data) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('Saved ', data);
|
||||
}
|
||||
CompletionMonitor();
|
||||
console.log('nonprofits');
|
||||
});
|
||||
});
|
||||
|
||||
Job.destroyAll(function(err, info) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('Deleted ', info);
|
||||
}
|
||||
Job.create(jobs, function(err, data) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('Saved ', data);
|
||||
}
|
||||
console.log('jobs');
|
||||
CompletionMonitor();
|
||||
});
|
||||
});
|
@ -1,97 +0,0 @@
|
||||
require('dotenv').load();
|
||||
var Challenge = require('../models/Challenge.js'),
|
||||
FieldGuide = require('../models/FieldGuide.js'),
|
||||
Nonprofit = require('../models/Nonprofit.js'),
|
||||
Job = require('../models/Job.js'),
|
||||
mongoose = require('mongoose'),
|
||||
secrets = require('../config/secrets'),
|
||||
fieldGuides = require('./field-guides.json'),
|
||||
nonprofits = require('./nonprofits.json'),
|
||||
jobs = require('./jobs.json'),
|
||||
fs = require('fs');
|
||||
|
||||
mongoose.connect(secrets.db);
|
||||
var challenges = fs.readdirSync(__dirname + '/challenges');
|
||||
|
||||
var counter = 0;
|
||||
var offerings = 3 + challenges.length;
|
||||
|
||||
var CompletionMonitor = function() {
|
||||
counter++;
|
||||
console.log('call ' + counter);
|
||||
|
||||
if (counter < offerings) {
|
||||
return;
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
Challenge.remove({}, function(err, data) {
|
||||
if (err) {
|
||||
console.err(err);
|
||||
} else {
|
||||
console.log('Deleted ', data);
|
||||
}
|
||||
challenges.forEach(function (file) {
|
||||
Challenge.create(require('./challenges/' + file).challenges, function (err, data) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('Successfully parsed %s', file);
|
||||
CompletionMonitor();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
FieldGuide.remove({}, function(err, data) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('Deleted ', data);
|
||||
}
|
||||
FieldGuide.create(fieldGuides, function(err, data) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('Saved ', data);
|
||||
}
|
||||
CompletionMonitor();
|
||||
});
|
||||
console.log('field guides');
|
||||
});
|
||||
|
||||
Nonprofit.remove({}, function(err, data) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('Deleted ', data);
|
||||
}
|
||||
Nonprofit.create(nonprofits, function(err, data) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('Saved ', data);
|
||||
}
|
||||
CompletionMonitor();
|
||||
});
|
||||
console.log('nonprofits');
|
||||
});
|
||||
|
||||
Job.remove({}, function(err, data) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('Deleted ', data);
|
||||
}
|
||||
Job.create(jobs, function(err, data) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('Saved ', data);
|
||||
}
|
||||
CompletionMonitor();
|
||||
});
|
||||
console.log('jobs');
|
||||
});
|
4
server/boot/authentication.js
Normal file
4
server/boot/authentication.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = function enableAuthentication(app) {
|
||||
// enable authentication
|
||||
app.enableAuth();
|
||||
};
|
@ -31,33 +31,13 @@
|
||||
*/
|
||||
|
||||
var R = require('ramda'),
|
||||
express = require('express'),
|
||||
Challenge = require('../../common/models/Challenge'),
|
||||
User = require('../../common/models/User'),
|
||||
resources = require('../resources/resources'),
|
||||
userMigration = require('../resources/middleware').userMigration,
|
||||
MDNlinks = require('../../seed_data/bonfireMDNlinks');
|
||||
utils = require('../utils'),
|
||||
userMigration = require('../utils/middleware').userMigration,
|
||||
MDNlinks = require('../../seed/bonfireMDNlinks');
|
||||
|
||||
var router = express.Router();
|
||||
var challengeMapWithNames = resources.getChallengeMapWithNames();
|
||||
var challengeMapWithIds = resources.getChallengeMapWithIds();
|
||||
var challengeMapWithNames = utils.getChallengeMapWithNames();
|
||||
var challengeMapWithIds = utils.getChallengeMapWithIds();
|
||||
|
||||
router.get(
|
||||
'/challenges/next-challenge',
|
||||
userMigration,
|
||||
returnNextChallenge
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/challenges/:challengeName',
|
||||
userMigration,
|
||||
returnIndividualChallenge
|
||||
);
|
||||
|
||||
router.get('/challenges/', userMigration, returnCurrentChallenge);
|
||||
router.post('/completed-challenge/', completedChallenge);
|
||||
router.post('/completed-zipline-or-basejump', completedZiplineOrBasejump);
|
||||
router.post('/completed-bonfire', completedBonfire);
|
||||
|
||||
function getMDNlinks(links) {
|
||||
// takes in an array of links, which are strings
|
||||
@ -73,278 +53,302 @@ function getMDNlinks(links) {
|
||||
return populatedLinks;
|
||||
}
|
||||
|
||||
function returnNextChallenge(req, res, next) {
|
||||
if (!req.user) {
|
||||
return res.redirect('../challenges/learn-how-free-code-camp-works');
|
||||
}
|
||||
var completed = req.user.completedChallenges.map(function (elem) {
|
||||
return elem._id;
|
||||
});
|
||||
module.exports = function(app) {
|
||||
var router = app.loopback.Router();
|
||||
var Challenge = app.models.Challenge;
|
||||
var User = app.models.User;
|
||||
|
||||
req.user.uncompletedChallenges = resources.allChallengeIds()
|
||||
.filter(function (elem) {
|
||||
if (completed.indexOf(elem) === -1) {
|
||||
return elem;
|
||||
}
|
||||
});
|
||||
router.get(
|
||||
'/challenges/next-challenge',
|
||||
userMigration,
|
||||
returnNextChallenge
|
||||
);
|
||||
|
||||
// find the user's current challenge and block
|
||||
// look in that block and find the index of their current challenge
|
||||
// if index + 1 < block.challenges.length
|
||||
// serve index + 1 challenge
|
||||
// otherwise increment block key and serve the first challenge in that block
|
||||
// unless the next block is undefined, which means no next block
|
||||
var nextChallengeName;
|
||||
router.get(
|
||||
'/challenges/:challengeName',
|
||||
userMigration,
|
||||
returnIndividualChallenge
|
||||
);
|
||||
|
||||
var challengeId = String(req.user.currentChallenge.challengeId);
|
||||
var challengeBlock = req.user.currentChallenge.challengeBlock;
|
||||
var indexOfChallenge = challengeMapWithIds[challengeBlock]
|
||||
.indexOf(challengeId);
|
||||
router.get('/challenges/', userMigration, returnCurrentChallenge);
|
||||
router.post('/completed-challenge/', completedChallenge);
|
||||
router.post('/completed-zipline-or-basejump', completedZiplineOrBasejump);
|
||||
router.post('/completed-bonfire', completedBonfire);
|
||||
|
||||
if (indexOfChallenge + 1
|
||||
< challengeMapWithIds[challengeBlock].length) {
|
||||
nextChallengeName =
|
||||
challengeMapWithNames[challengeBlock][++indexOfChallenge];
|
||||
} else if (typeof challengeMapWithIds[++challengeBlock] !== 'undefined') {
|
||||
nextChallengeName = R.head(challengeMapWithNames[challengeBlock]);
|
||||
} else {
|
||||
req.flash('errors', {
|
||||
msg: 'It looks like you have finished all of our challenges.' +
|
||||
' Great job! Now on to helping nonprofits!'
|
||||
});
|
||||
nextChallengeName = R.head(challengeMapWithNames[0].challenges);
|
||||
}
|
||||
app.use(router);
|
||||
|
||||
var nameString = nextChallengeName.trim()
|
||||
.toLowerCase()
|
||||
.replace(/\s/g, '-');
|
||||
|
||||
req.user.save(function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
function returnNextChallenge(req, res, next) {
|
||||
if (!req.user) {
|
||||
return res.redirect('../challenges/learn-how-free-code-camp-works');
|
||||
}
|
||||
return res.redirect('../challenges/' + nameString);
|
||||
});
|
||||
}
|
||||
|
||||
function returnCurrentChallenge(req, res, next) {
|
||||
if (!req.user) {
|
||||
return res.redirect('../challenges/learn-how-free-code-camp-works');
|
||||
}
|
||||
var completed = req.user.completedChallenges.map(function (elem) {
|
||||
return elem._id;
|
||||
});
|
||||
|
||||
req.user.uncompletedChallenges = resources.allChallengeIds()
|
||||
.filter(function (elem) {
|
||||
if (completed.indexOf(elem) === -1) {
|
||||
return elem;
|
||||
}
|
||||
var completed = req.user.completedChallenges.map(function (elem) {
|
||||
return elem._id;
|
||||
});
|
||||
if (!req.user.currentChallenge) {
|
||||
req.user.currentChallenge = {};
|
||||
req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0];
|
||||
req.user.currentChallenge.challengeName = challengeMapWithNames['0'][0];
|
||||
req.user.currentChallenge.challengeBlock = '0';
|
||||
|
||||
req.user.uncompletedChallenges = utils.allChallengeIds()
|
||||
.filter(function (elem) {
|
||||
if (completed.indexOf(elem) === -1) {
|
||||
return elem;
|
||||
}
|
||||
});
|
||||
|
||||
// find the user's current challenge and block
|
||||
// look in that block and find the index of their current challenge
|
||||
// if index + 1 < block.challenges.length
|
||||
// serve index + 1 challenge
|
||||
// otherwise increment block key and serve the first challenge in that block
|
||||
// unless the next block is undefined, which means no next block
|
||||
var nextChallengeName;
|
||||
|
||||
var challengeId = String(req.user.currentChallenge.challengeId);
|
||||
var challengeBlock = req.user.currentChallenge.challengeBlock;
|
||||
var indexOfChallenge = challengeMapWithIds[challengeBlock]
|
||||
.indexOf(challengeId);
|
||||
|
||||
if (indexOfChallenge + 1
|
||||
< challengeMapWithIds[challengeBlock].length) {
|
||||
nextChallengeName =
|
||||
challengeMapWithNames[challengeBlock][++indexOfChallenge];
|
||||
} else if (typeof challengeMapWithIds[++challengeBlock] !== 'undefined') {
|
||||
nextChallengeName = R.head(challengeMapWithNames[challengeBlock]);
|
||||
} else {
|
||||
req.flash('errors', {
|
||||
msg: 'It looks like you have finished all of our challenges.' +
|
||||
' Great job! Now on to helping nonprofits!'
|
||||
});
|
||||
nextChallengeName = R.head(challengeMapWithNames[0].challenges);
|
||||
}
|
||||
|
||||
var nameString = nextChallengeName.trim()
|
||||
.toLowerCase()
|
||||
.replace(/\s/g, '-');
|
||||
|
||||
req.user.save(function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
return res.redirect('../challenges/' + nameString);
|
||||
});
|
||||
}
|
||||
var nameString = req.user.currentChallenge.challengeName.trim()
|
||||
.toLowerCase()
|
||||
.replace(/\s/g, '-')
|
||||
.replace(/[^a-z0-9\-\/.]/gi, '');
|
||||
req.user.save(function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
|
||||
function returnCurrentChallenge(req, res, next) {
|
||||
if (!req.user) {
|
||||
return res.redirect('../challenges/learn-how-free-code-camp-works');
|
||||
}
|
||||
return res.redirect('../challenges/' + nameString);
|
||||
});
|
||||
}
|
||||
|
||||
function returnIndividualChallenge(req, res, next) {
|
||||
var dashedName = req.params.challengeName;
|
||||
|
||||
var challengeName =
|
||||
(/^(bonfire|waypoint|zipline|basejump)/i).test(dashedName) ?
|
||||
dashedName
|
||||
.replace(/\-/g, ' ')
|
||||
.split(' ')
|
||||
.slice(1)
|
||||
.join(' ') :
|
||||
dashedName.replace(/\-/g, ' ');
|
||||
|
||||
Challenge.find({'name': new RegExp(challengeName, 'i')},
|
||||
function(err, challengeFromMongo) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
// Handle not found
|
||||
if (challengeFromMongo.length < 1) {
|
||||
req.flash('errors', {
|
||||
msg: '404: We couldn\'t find a challenge with that name. ' +
|
||||
'Please double check the name.'
|
||||
});
|
||||
return res.redirect('/challenges');
|
||||
}
|
||||
var challenge = challengeFromMongo.pop();
|
||||
// Redirect to full name if the user only entered a partial
|
||||
var dashedNameFull = challenge.name
|
||||
.toLowerCase()
|
||||
.replace(/\s/g, '-')
|
||||
.replace(/[^a-z0-9\-\.]/gi, '');
|
||||
if (dashedNameFull !== dashedName) {
|
||||
return res.redirect('../challenges/' + dashedNameFull);
|
||||
} else if (req.user) {
|
||||
req.user.currentChallenge = {
|
||||
challengeId: challenge._id,
|
||||
challengeName: challenge.name,
|
||||
challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds).
|
||||
map(function (key) {
|
||||
return challengeMapWithIds[key]
|
||||
.filter(function (elem) {
|
||||
return String(elem) === String(challenge._id);
|
||||
}).map(function () {
|
||||
return key;
|
||||
});
|
||||
})
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
var challengeType = {
|
||||
0: function() {
|
||||
res.render('coursewares/showHTML', {
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
brief: challenge.description[0],
|
||||
details: challenge.description.slice(1),
|
||||
tests: challenge.tests,
|
||||
challengeSeed: challenge.challengeSeed,
|
||||
verb: resources.randomVerb(),
|
||||
phrase: resources.randomPhrase(),
|
||||
compliment: resources.randomCompliment(),
|
||||
challengeId: challenge._id,
|
||||
environment: resources.whichEnvironment(),
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
},
|
||||
|
||||
1: function() {
|
||||
res.render('coursewares/showJS', {
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
brief: challenge.description[0],
|
||||
details: challenge.description.slice(1),
|
||||
tests: challenge.tests,
|
||||
challengeSeed: challenge.challengeSeed,
|
||||
verb: resources.randomVerb(),
|
||||
phrase: resources.randomPhrase(),
|
||||
compliment: resources.randomCompliment(),
|
||||
challengeId: challenge._id,
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
},
|
||||
|
||||
2: function() {
|
||||
res.render('coursewares/showVideo', {
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
details: challenge.description,
|
||||
tests: challenge.tests,
|
||||
video: challenge.challengeSeed[0],
|
||||
verb: resources.randomVerb(),
|
||||
phrase: resources.randomPhrase(),
|
||||
compliment: resources.randomCompliment(),
|
||||
challengeId: challenge._id,
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
},
|
||||
|
||||
3: function() {
|
||||
res.render('coursewares/showZiplineOrBasejump', {
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
details: challenge.description,
|
||||
video: challenge.challengeSeed[0],
|
||||
verb: resources.randomVerb(),
|
||||
phrase: resources.randomPhrase(),
|
||||
compliment: resources.randomCompliment(),
|
||||
challengeId: challenge._id,
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
},
|
||||
|
||||
4: function() {
|
||||
res.render('coursewares/showZiplineOrBasejump', {
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
details: challenge.description,
|
||||
video: challenge.challengeSeed[0],
|
||||
verb: resources.randomVerb(),
|
||||
phrase: resources.randomPhrase(),
|
||||
compliment: resources.randomCompliment(),
|
||||
challengeId: challenge._id,
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
},
|
||||
|
||||
5: function() {
|
||||
res.render('coursewares/showBonfire', {
|
||||
completedWith: null,
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
difficulty: Math.floor(+challenge.difficulty),
|
||||
brief: challenge.description.shift(),
|
||||
details: challenge.description,
|
||||
tests: challenge.tests,
|
||||
challengeSeed: challenge.challengeSeed,
|
||||
verb: resources.randomVerb(),
|
||||
phrase: resources.randomPhrase(),
|
||||
compliment: resources.randomCompliment(),
|
||||
bonfires: challenge,
|
||||
challengeId: challenge._id,
|
||||
MDNkeys: challenge.MDNlinks,
|
||||
MDNlinks: getMDNlinks(challenge.MDNlinks),
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
}
|
||||
};
|
||||
if (req.user) {
|
||||
req.user.save(function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
return challengeType[challenge.challengeType]();
|
||||
});
|
||||
} else {
|
||||
return challengeType[challenge.challengeType]();
|
||||
}
|
||||
var completed = req.user.completedChallenges.map(function (elem) {
|
||||
return elem._id;
|
||||
});
|
||||
}
|
||||
|
||||
function completedBonfire(req, res, next) {
|
||||
var isCompletedWith = req.body.challengeInfo.completedWith || '';
|
||||
var isCompletedDate = Math.round(+new Date());
|
||||
var challengeId = req.body.challengeInfo.challengeId;
|
||||
var isSolution = req.body.challengeInfo.solution;
|
||||
var challengeName = req.body.challengeInfo.challengeName;
|
||||
|
||||
if (isCompletedWith) {
|
||||
var paired = User.find({'profile.username': isCompletedWith.toLowerCase()})
|
||||
.limit(1);
|
||||
paired.exec(function (err, pairedWith) {
|
||||
req.user.uncompletedChallenges = utils.allChallengeIds()
|
||||
.filter(function (elem) {
|
||||
if (completed.indexOf(elem) === -1) {
|
||||
return elem;
|
||||
}
|
||||
});
|
||||
if (!req.user.currentChallenge) {
|
||||
req.user.currentChallenge = {};
|
||||
req.user.currentChallenge.challengeId = challengeMapWithIds['0'][0];
|
||||
req.user.currentChallenge.challengeName = challengeMapWithNames['0'][0];
|
||||
req.user.currentChallenge.challengeBlock = '0';
|
||||
req.user.save(function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
var nameString = req.user.currentChallenge.challengeName.trim()
|
||||
.toLowerCase()
|
||||
.replace(/\s/g, '-')
|
||||
.replace(/[^a-z0-9\-\/.]/gi, '');
|
||||
req.user.save(function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
} else {
|
||||
}
|
||||
return res.redirect('../challenges/' + nameString);
|
||||
});
|
||||
}
|
||||
|
||||
function returnIndividualChallenge(req, res, next) {
|
||||
var dashedName = req.params.challengeName;
|
||||
|
||||
var challengeName =
|
||||
(/^(bonfire|waypoint|zipline|basejump)/i).test(dashedName) ?
|
||||
dashedName
|
||||
.replace(/\-/g, ' ')
|
||||
.split(' ')
|
||||
.slice(1)
|
||||
.join(' ') :
|
||||
dashedName.replace(/\-/g, ' ');
|
||||
|
||||
Challenge.find(
|
||||
{ where: { name: new RegExp(challengeName, 'i') } },
|
||||
function(err, challengeFromMongo) {
|
||||
if (err) { return next(err); }
|
||||
|
||||
// Handle not found
|
||||
if (challengeFromMongo.length < 1) {
|
||||
req.flash('errors', {
|
||||
msg: '404: We couldn\'t find a challenge with that name. ' +
|
||||
'Please double check the name.'
|
||||
});
|
||||
return res.redirect('/challenges');
|
||||
}
|
||||
var challenge = challengeFromMongo.pop();
|
||||
// Redirect to full name if the user only entered a partial
|
||||
var dashedNameFull = challenge.name
|
||||
.toLowerCase()
|
||||
.replace(/\s/g, '-')
|
||||
.replace(/[^a-z0-9\-\.]/gi, '');
|
||||
if (dashedNameFull !== dashedName) {
|
||||
return res.redirect('../challenges/' + dashedNameFull);
|
||||
} else if (req.user) {
|
||||
req.user.currentChallenge = {
|
||||
challengeId: challenge._id,
|
||||
challengeName: challenge.name,
|
||||
challengeBlock: R.head(R.flatten(Object.keys(challengeMapWithIds).
|
||||
map(function (key) {
|
||||
return challengeMapWithIds[key]
|
||||
.filter(function (elem) {
|
||||
return String(elem) === String(challenge._id);
|
||||
}).map(function () {
|
||||
return key;
|
||||
});
|
||||
})
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
var challengeType = {
|
||||
0: function() {
|
||||
res.render('coursewares/showHTML', {
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
brief: challenge.description[0],
|
||||
details: challenge.description.slice(1),
|
||||
tests: challenge.tests,
|
||||
challengeSeed: challenge.challengeSeed,
|
||||
verb: utils.randomVerb(),
|
||||
phrase: utils.randomPhrase(),
|
||||
compliment: utils.randomCompliment(),
|
||||
challengeId: challenge._id,
|
||||
environment: utils.whichEnvironment(),
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
},
|
||||
|
||||
1: function() {
|
||||
res.render('coursewares/showJS', {
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
brief: challenge.description[0],
|
||||
details: challenge.description.slice(1),
|
||||
tests: challenge.tests,
|
||||
challengeSeed: challenge.challengeSeed,
|
||||
verb: utils.randomVerb(),
|
||||
phrase: utils.randomPhrase(),
|
||||
compliment: utils.randomCompliment(),
|
||||
challengeId: challenge._id,
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
},
|
||||
|
||||
2: function() {
|
||||
res.render('coursewares/showVideo', {
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
details: challenge.description,
|
||||
tests: challenge.tests,
|
||||
video: challenge.challengeSeed[0],
|
||||
verb: utils.randomVerb(),
|
||||
phrase: utils.randomPhrase(),
|
||||
compliment: utils.randomCompliment(),
|
||||
challengeId: challenge._id,
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
},
|
||||
|
||||
3: function() {
|
||||
res.render('coursewares/showZiplineOrBasejump', {
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
details: challenge.description,
|
||||
video: challenge.challengeSeed[0],
|
||||
verb: utils.randomVerb(),
|
||||
phrase: utils.randomPhrase(),
|
||||
compliment: utils.randomCompliment(),
|
||||
challengeId: challenge._id,
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
},
|
||||
|
||||
4: function() {
|
||||
res.render('coursewares/showZiplineOrBasejump', {
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
details: challenge.description,
|
||||
video: challenge.challengeSeed[0],
|
||||
verb: utils.randomVerb(),
|
||||
phrase: utils.randomPhrase(),
|
||||
compliment: utils.randomCompliment(),
|
||||
challengeId: challenge._id,
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
},
|
||||
|
||||
5: function() {
|
||||
res.render('coursewares/showBonfire', {
|
||||
completedWith: null,
|
||||
title: challenge.name,
|
||||
dashedName: dashedName,
|
||||
name: challenge.name,
|
||||
difficulty: Math.floor(+challenge.difficulty),
|
||||
brief: challenge.description.shift(),
|
||||
details: challenge.description,
|
||||
tests: challenge.tests,
|
||||
challengeSeed: challenge.challengeSeed,
|
||||
verb: utils.randomVerb(),
|
||||
phrase: utils.randomPhrase(),
|
||||
compliment: utils.randomCompliment(),
|
||||
bonfires: challenge,
|
||||
challengeId: challenge._id,
|
||||
MDNkeys: challenge.MDNlinks,
|
||||
MDNlinks: getMDNlinks(challenge.MDNlinks),
|
||||
challengeType: challenge.challengeType
|
||||
});
|
||||
}
|
||||
};
|
||||
if (req.user) {
|
||||
req.user.save(function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
return challengeType[challenge.challengeType]();
|
||||
});
|
||||
} else {
|
||||
return challengeType[challenge.challengeType]();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function completedBonfire(req, res, next) {
|
||||
var isCompletedWith = req.body.challengeInfo.completedWith || '';
|
||||
var isCompletedDate = Math.round(+new Date());
|
||||
var challengeId = req.body.challengeInfo.challengeId;
|
||||
var isSolution = req.body.challengeInfo.solution;
|
||||
var challengeName = req.body.challengeInfo.challengeName;
|
||||
|
||||
if (isCompletedWith) {
|
||||
User.find({
|
||||
where: { 'profile.username': isCompletedWith.toLowerCase() },
|
||||
limit: 1
|
||||
}, function (err, pairedWith) {
|
||||
if (err) { return next(err); }
|
||||
|
||||
var index = req.user.uncompletedChallenges.indexOf(challengeId);
|
||||
if (index > -1) {
|
||||
req.user.progressTimestamps.push(Date.now() || 0);
|
||||
@ -379,20 +383,18 @@ function completedBonfire(req, res, next) {
|
||||
});
|
||||
}
|
||||
// User said they paired, but pair wasn't found
|
||||
req.user.completedChallenges.push({
|
||||
_id: challengeId,
|
||||
name: challengeName,
|
||||
completedWith: null,
|
||||
completedDate: isCompletedDate,
|
||||
solution: isSolution,
|
||||
challengeType: 5
|
||||
});
|
||||
|
||||
req.user.completedChallenges.push({
|
||||
_id: challengeId,
|
||||
name: challengeName,
|
||||
completedWith: null,
|
||||
completedDate: isCompletedDate,
|
||||
solution: isSolution,
|
||||
challengeType: 5
|
||||
});
|
||||
|
||||
req.user.save(function (err, user) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (err) { return next(err); }
|
||||
|
||||
if (pairedWith) {
|
||||
pairedWith.save(function (err, paired) {
|
||||
if (err) {
|
||||
@ -406,21 +408,47 @@ function completedBonfire(req, res, next) {
|
||||
res.send(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
req.user.completedChallenges.push({
|
||||
_id: challengeId,
|
||||
name: challengeName,
|
||||
completedWith: null,
|
||||
completedDate: isCompletedDate,
|
||||
solution: isSolution,
|
||||
challengeType: 5
|
||||
});
|
||||
|
||||
var index = req.user.uncompletedChallenges.indexOf(challengeId);
|
||||
if (index > -1) {
|
||||
|
||||
req.user.progressTimestamps.push(Date.now() || 0);
|
||||
req.user.uncompletedChallenges.splice(index, 1);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
req.user.save(function (err) {
|
||||
if (err) { return next(err); }
|
||||
res.send(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function completedChallenge(req, res, next) {
|
||||
|
||||
var isCompletedDate = Math.round(+new Date());
|
||||
var challengeId = req.body.challengeInfo.challengeId;
|
||||
|
||||
req.user.completedChallenges.push({
|
||||
_id: challengeId,
|
||||
name: challengeName,
|
||||
completedWith: null,
|
||||
completedDate: isCompletedDate,
|
||||
solution: isSolution,
|
||||
challengeType: 5
|
||||
name: req.body.challengeInfo.challengeName,
|
||||
solution: null,
|
||||
githubLink: null,
|
||||
verified: true
|
||||
});
|
||||
|
||||
var index = req.user.uncompletedChallenges.indexOf(challengeId);
|
||||
if (index > -1) {
|
||||
|
||||
if (index > -1) {
|
||||
req.user.progressTimestamps.push(Date.now() || 0);
|
||||
req.user.uncompletedChallenges.splice(index, 1);
|
||||
}
|
||||
@ -429,70 +457,36 @@ function completedBonfire(req, res, next) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
// NOTE(berks): Under certain conditions the res is never ended
|
||||
if (user) {
|
||||
res.send(true);
|
||||
res.sendStatus(200);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function completedChallenge(req, res, next) {
|
||||
function completedZiplineOrBasejump(req, res, next) {
|
||||
|
||||
var isCompletedDate = Math.round(+new Date());
|
||||
var challengeId = req.body.challengeInfo.challengeId;
|
||||
|
||||
req.user.completedChallenges.push({
|
||||
_id: challengeId,
|
||||
completedDate: isCompletedDate,
|
||||
name: req.body.challengeInfo.challengeName,
|
||||
solution: null,
|
||||
githubLink: null,
|
||||
verified: true
|
||||
});
|
||||
var index = req.user.uncompletedChallenges.indexOf(challengeId);
|
||||
|
||||
if (index > -1) {
|
||||
req.user.progressTimestamps.push(Date.now() || 0);
|
||||
req.user.uncompletedChallenges.splice(index, 1);
|
||||
}
|
||||
|
||||
req.user.save(function (err, user) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
var isCompletedWith = req.body.challengeInfo.completedWith || false;
|
||||
var isCompletedDate = Math.round(+new Date());
|
||||
var challengeId = req.body.challengeInfo.challengeId;
|
||||
var solutionLink = req.body.challengeInfo.publicURL;
|
||||
var githubLink = req.body.challengeInfo.challengeType === '4'
|
||||
? req.body.challengeInfo.githubURL : true;
|
||||
var challengeType = req.body.challengeInfo.challengeType === '4' ?
|
||||
4 : 3;
|
||||
if (!solutionLink || !githubLink) {
|
||||
req.flash('errors', {
|
||||
msg: 'You haven\'t supplied the necessary URLs for us to inspect ' +
|
||||
'your work.'
|
||||
});
|
||||
return res.sendStatus(403);
|
||||
}
|
||||
if (user) {
|
||||
res.sendStatus(200);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function completedZiplineOrBasejump(req, res, next) {
|
||||
|
||||
var isCompletedWith = req.body.challengeInfo.completedWith || false;
|
||||
var isCompletedDate = Math.round(+new Date());
|
||||
var challengeId = req.body.challengeInfo.challengeId;
|
||||
var solutionLink = req.body.challengeInfo.publicURL;
|
||||
var githubLink = req.body.challengeInfo.challengeType === '4'
|
||||
? req.body.challengeInfo.githubURL : true;
|
||||
var challengeType = req.body.challengeInfo.challengeType === '4' ?
|
||||
4 : 3;
|
||||
if (!solutionLink || !githubLink) {
|
||||
req.flash('errors', {
|
||||
msg: 'You haven\'t supplied the necessary URLs for us to inspect ' +
|
||||
'your work.'
|
||||
});
|
||||
return res.sendStatus(403);
|
||||
}
|
||||
|
||||
if (isCompletedWith) {
|
||||
var paired = User.find({'profile.username': isCompletedWith.toLowerCase()})
|
||||
.limit(1);
|
||||
|
||||
paired.exec(function (err, pairedWithFromMongo) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
} else {
|
||||
if (isCompletedWith) {
|
||||
User.find({
|
||||
where: { 'profile.username': isCompletedWith.toLowerCase() },
|
||||
limit: 1
|
||||
}, function (err, pairedWithFromMongo) {
|
||||
if (err) { return next(err); }
|
||||
var index = req.user.uncompletedChallenges.indexOf(challengeId);
|
||||
if (index > -1) {
|
||||
req.user.progressTimestamps.push(Date.now() || 0);
|
||||
@ -512,9 +506,7 @@ function completedZiplineOrBasejump(req, res, next) {
|
||||
});
|
||||
|
||||
req.user.save(function (err, user) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (err) { return next(err); }
|
||||
|
||||
if (req.user._id.toString() === pairedWith._id.toString()) {
|
||||
return res.sendStatus(200);
|
||||
@ -545,37 +537,36 @@ function completedZiplineOrBasejump(req, res, next) {
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
|
||||
req.user.completedChallenges.push({
|
||||
_id: challengeId,
|
||||
name: req.body.challengeInfo.challengeName,
|
||||
completedWith: null,
|
||||
completedDate: isCompletedDate,
|
||||
solution: solutionLink,
|
||||
githubLink: githubLink,
|
||||
challengeType: challengeType,
|
||||
verified: false
|
||||
});
|
||||
|
||||
var index = req.user.uncompletedChallenges.indexOf(challengeId);
|
||||
if (index > -1) {
|
||||
req.user.progressTimestamps.push(Date.now() || 0);
|
||||
req.user.uncompletedChallenges.splice(index, 1);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
req.user.completedChallenges.push({
|
||||
_id: challengeId,
|
||||
name: req.body.challengeInfo.challengeName,
|
||||
completedWith: null,
|
||||
completedDate: isCompletedDate,
|
||||
solution: solutionLink,
|
||||
githubLink: githubLink,
|
||||
challengeType: challengeType,
|
||||
verified: false
|
||||
});
|
||||
|
||||
var index = req.user.uncompletedChallenges.indexOf(challengeId);
|
||||
if (index > -1) {
|
||||
req.user.progressTimestamps.push(Date.now() || 0);
|
||||
req.user.uncompletedChallenges.splice(index, 1);
|
||||
req.user.save(function (err, user) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
// NOTE(berks): under certain conditions this will not close
|
||||
// the response.
|
||||
if (user) {
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
req.user.save(function (err, user) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
// NOTE(berks): under certain conditions this will not close the response.
|
||||
if (user) {
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
};
|
||||
|
@ -1,65 +1,65 @@
|
||||
var R = require('ramda'),
|
||||
express = require('express'),
|
||||
// debug = require('debug')('freecc:cntr:challengeMap'),
|
||||
User = require('../../common/models/User'),
|
||||
resources = require('./../resources/resources'),
|
||||
middleware = require('../resources/middleware'),
|
||||
router = express.Router();
|
||||
utils = require('./../utils'),
|
||||
middleware = require('../utils/middleware');
|
||||
|
||||
router.get('/map', middleware.userMigration, challengeMap);
|
||||
|
||||
router.get('/learn-to-code', function(req, res) {
|
||||
res.redirect(301, '/map');
|
||||
});
|
||||
module.exports = function(app) {
|
||||
var User = app.models.User;
|
||||
var router = app.loopback.Router();
|
||||
|
||||
router.get('/about', function(req, res) {
|
||||
res.redirect(301, '/map');
|
||||
});
|
||||
|
||||
function challengeMap(req, res, next) {
|
||||
var completedList = [];
|
||||
|
||||
if (req.user) {
|
||||
completedList = req.user.completedChallenges;
|
||||
}
|
||||
|
||||
var noDuplicatedChallenges = R.uniq(completedList);
|
||||
|
||||
var completedChallengeList = noDuplicatedChallenges
|
||||
.map(function(challenge) {
|
||||
return challenge._id;
|
||||
});
|
||||
var challengeList = resources.
|
||||
getChallengeMapForDisplay(completedChallengeList);
|
||||
|
||||
Object.keys(challengeList).forEach(function(key) {
|
||||
challengeList[key].completed = challengeList[key]
|
||||
.challenges.filter(function(elem) {
|
||||
return completedChallengeList.indexOf(elem._id) > -1;
|
||||
});
|
||||
router.get('/map', middleware.userMigration, challengeMap);
|
||||
router.get('/learn-to-code', function(req, res) {
|
||||
res.redirect(301, '/map');
|
||||
});
|
||||
router.get('/about', function(req, res) {
|
||||
res.redirect(301, '/map');
|
||||
});
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
}
|
||||
app.use(router);
|
||||
|
||||
var date1 = new Date('10/15/2014');
|
||||
var date2 = new Date();
|
||||
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
|
||||
var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24));
|
||||
function challengeMap(req, res, next) {
|
||||
var completedList = [];
|
||||
|
||||
User.count({}, function (err, camperCount) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
if (req.user) {
|
||||
completedList = req.user.completedChallenges;
|
||||
}
|
||||
res.render('challengeMap/show', {
|
||||
daysRunning: daysRunning,
|
||||
camperCount: numberWithCommas(camperCount),
|
||||
title: "A map of all Free Code Camp's Challenges",
|
||||
challengeList: challengeList,
|
||||
completedChallengeList: completedChallengeList
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
var noDuplicatedChallenges = R.uniq(completedList);
|
||||
|
||||
var completedChallengeList = noDuplicatedChallenges
|
||||
.map(function(challenge) {
|
||||
return challenge._id;
|
||||
});
|
||||
var challengeList = utils.
|
||||
getChallengeMapForDisplay(completedChallengeList);
|
||||
|
||||
Object.keys(challengeList).forEach(function(key) {
|
||||
challengeList[key].completed = challengeList[key]
|
||||
.challenges.filter(function(elem) {
|
||||
return completedChallengeList.indexOf(elem._id) > -1;
|
||||
});
|
||||
});
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
}
|
||||
|
||||
var date1 = new Date('10/15/2014');
|
||||
var date2 = new Date();
|
||||
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
|
||||
var daysRunning = Math.ceil(timeDiff / (1000 * 3600 * 24));
|
||||
|
||||
User.count(function(err, camperCount) {
|
||||
if (err) { return next(err); }
|
||||
|
||||
res.render('challengeMap/show', {
|
||||
daysRunning: daysRunning,
|
||||
camperCount: numberWithCommas(camperCount),
|
||||
title: "A map of all Free Code Camp's Challenges",
|
||||
challengeList: challengeList,
|
||||
completedChallengeList: completedChallengeList
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
27
server/boot/explorer.js
Normal file
27
server/boot/explorer.js
Normal file
@ -0,0 +1,27 @@
|
||||
module.exports = function mountLoopBackExplorer(app) {
|
||||
var explorer;
|
||||
try {
|
||||
explorer = require('loopback-explorer');
|
||||
} catch(err) {
|
||||
// Print the message only when the app was started via `app.listen()`.
|
||||
// Do not print any message when the project is used as a component.
|
||||
app.once('started', function() {
|
||||
console.log(
|
||||
'Run `npm install loopback-explorer` to enable the LoopBack explorer'
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var restApiRoot = app.get('restApiRoot');
|
||||
|
||||
var explorerApp = explorer(app, { basePath: restApiRoot });
|
||||
app.use('/explorer', explorerApp);
|
||||
app.once('started', function() {
|
||||
var baseUrl = app.get('url').replace(/\/$/, '');
|
||||
// express 4.x (loopback 2.x) uses `mountpath`
|
||||
// express 3.x (loopback 1.x) uses `route`
|
||||
var explorerPath = explorerApp.mountpath || explorerApp.route;
|
||||
console.log('Browse your REST API at %s%s', baseUrl, explorerPath);
|
||||
});
|
||||
};
|
@ -1,123 +1,128 @@
|
||||
var R = require('ramda'),
|
||||
express = require('express'),
|
||||
// Rx = require('rx'),
|
||||
// debug = require('debug')('freecc:fieldguides'),
|
||||
FieldGuide = require('../../common/models/FieldGuide'),
|
||||
resources = require('../resources/resources');
|
||||
utils = require('../utils');
|
||||
|
||||
var router = express.Router();
|
||||
module.exports = function(app) {
|
||||
var router = app.loopback.Router();
|
||||
var FieldGuide = app.models.FieldGuide;
|
||||
|
||||
router.get('/field-guide/all-articles', showAllFieldGuides);
|
||||
router.get('/field-guide/:fieldGuideName', returnIndividualFieldGuide);
|
||||
router.get('/field-guide/', returnNextFieldGuide);
|
||||
router.post('/completed-field-guide/', completedFieldGuide);
|
||||
router.get('/field-guide/all-articles', showAllFieldGuides);
|
||||
router.get('/field-guide/:fieldGuideName', returnIndividualFieldGuide);
|
||||
router.get('/field-guide/', returnNextFieldGuide);
|
||||
router.post('/completed-field-guide/', completedFieldGuide);
|
||||
|
||||
function returnIndividualFieldGuide(req, res, next) {
|
||||
var dashedName = req.params.fieldGuideName;
|
||||
if (req.user) {
|
||||
var completed = req.user.completedFieldGuides;
|
||||
app.use(router);
|
||||
|
||||
var uncompletedFieldGuides = resources.allFieldGuideIds()
|
||||
.filter(function (elem) {
|
||||
if (completed.indexOf(elem) === -1) {
|
||||
return elem;
|
||||
}
|
||||
function returnIndividualFieldGuide(req, res, next) {
|
||||
var dashedName = req.params.fieldGuideName;
|
||||
if (req.user) {
|
||||
var completed = req.user.completedFieldGuides;
|
||||
|
||||
var uncompletedFieldGuides = utils.allFieldGuideIds()
|
||||
.filter(function (elem) {
|
||||
if (completed.indexOf(elem) === -1) {
|
||||
return elem;
|
||||
}
|
||||
});
|
||||
req.user.uncompletedFieldGuides = uncompletedFieldGuides;
|
||||
// TODO(berks): handle callback properly
|
||||
req.user.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
});
|
||||
req.user.uncompletedFieldGuides = uncompletedFieldGuides;
|
||||
// TODO(berks): handle callback properly
|
||||
req.user.save();
|
||||
}
|
||||
|
||||
// NOTE(berks): loopback might have issue with regex here.
|
||||
FieldGuide.find(
|
||||
{ dashedName: new RegExp(dashedName, 'i') },
|
||||
function(err, fieldGuideFromMongo) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (fieldGuideFromMongo.length < 1) {
|
||||
req.flash('errors', {
|
||||
msg: "404: We couldn't find a field guide entry with that name. " +
|
||||
'Please double check the name.'
|
||||
});
|
||||
|
||||
return res.redirect('/field-guide');
|
||||
}
|
||||
|
||||
var fieldGuide = R.head(fieldGuideFromMongo);
|
||||
fieldGuide.name.toLowerCase().replace(/\s/g, '-').replace(/\?/g, '');
|
||||
|
||||
if (fieldGuide.dashedName !== dashedName) {
|
||||
return res.redirect('../field-guide/' + fieldGuide.dashedName);
|
||||
}
|
||||
res.render('field-guide/show', {
|
||||
title: fieldGuide.name,
|
||||
fieldGuideId: fieldGuide._id,
|
||||
description: fieldGuide.description.join('')
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
FieldGuide.find(
|
||||
{ dashedName: new RegExp(dashedName, 'i') },
|
||||
function(err, fieldGuideFromMongo) {
|
||||
function showAllFieldGuides(req, res) {
|
||||
var allFieldGuideNamesAndIds = utils.allFieldGuideNamesAndIds();
|
||||
|
||||
var completedFieldGuides = [];
|
||||
if (req.user && req.user.completedFieldGuides) {
|
||||
completedFieldGuides = req.user.completedFieldGuides;
|
||||
}
|
||||
res.render('field-guide/all-articles', {
|
||||
allFieldGuideNamesAndIds: allFieldGuideNamesAndIds,
|
||||
completedFieldGuides: completedFieldGuides
|
||||
});
|
||||
}
|
||||
|
||||
function returnNextFieldGuide(req, res, next) {
|
||||
if (!req.user) {
|
||||
return res.redirect('/field-guide/how-do-i-use-this-guide');
|
||||
}
|
||||
|
||||
var displayedFieldGuides =
|
||||
FieldGuide.find({'_id': req.user.uncompletedFieldGuides[0]});
|
||||
|
||||
displayedFieldGuides.exec(function(err, fieldGuide) {
|
||||
if (err) { return next(err); }
|
||||
fieldGuide = fieldGuide.pop();
|
||||
|
||||
if (typeof fieldGuide === 'undefined') {
|
||||
if (req.user.completedFieldGuides.length > 0) {
|
||||
req.flash('success', {
|
||||
msg: [
|
||||
"You've read all our current Field Guide entries. You can ",
|
||||
'contribute to our Field Guide ',
|
||||
"<a href='https://github.com/FreeCodeCamp/freecodecamp/blob/",
|
||||
"staging/seed/field-guides.json'>here</a>."
|
||||
].join('')
|
||||
});
|
||||
}
|
||||
return res.redirect('../field-guide/how-do-i-use-this-guide');
|
||||
}
|
||||
var nameString = fieldGuide.name.toLowerCase().replace(/\s/g, '-');
|
||||
return res.redirect('../field-guide/' + nameString);
|
||||
});
|
||||
}
|
||||
|
||||
function completedFieldGuide(req, res, next) {
|
||||
var fieldGuideId = req.body.fieldGuideInfo.fieldGuideId;
|
||||
|
||||
req.user.completedFieldGuides.push(fieldGuideId);
|
||||
|
||||
var index = req.user.uncompletedFieldGuides.indexOf(fieldGuideId);
|
||||
if (index > -1) {
|
||||
req.user.progressTimestamps.push(Date.now());
|
||||
req.user.uncompletedFieldGuides.splice(index, 1);
|
||||
}
|
||||
|
||||
req.user.save(function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (fieldGuideFromMongo.length < 1) {
|
||||
req.flash('errors', {
|
||||
msg: "404: We couldn't find a field guide entry with that name. " +
|
||||
'Please double check the name.'
|
||||
});
|
||||
|
||||
return res.redirect('/field-guide');
|
||||
}
|
||||
|
||||
var fieldGuide = R.head(fieldGuideFromMongo);
|
||||
fieldGuide.name.toLowerCase().replace(/\s/g, '-').replace(/\?/g, '');
|
||||
|
||||
if (fieldGuide.dashedName !== dashedName) {
|
||||
return res.redirect('../field-guide/' + fieldGuide.dashedName);
|
||||
}
|
||||
res.render('field-guide/show', {
|
||||
title: fieldGuide.name,
|
||||
fieldGuideId: fieldGuide._id,
|
||||
description: fieldGuide.description.join('')
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function showAllFieldGuides(req, res) {
|
||||
var allFieldGuideNamesAndIds = resources.allFieldGuideNamesAndIds();
|
||||
|
||||
var completedFieldGuides = [];
|
||||
if (req.user && req.user.completedFieldGuides) {
|
||||
completedFieldGuides = req.user.completedFieldGuides;
|
||||
res.send(true);
|
||||
});
|
||||
}
|
||||
res.render('field-guide/all-articles', {
|
||||
allFieldGuideNamesAndIds: allFieldGuideNamesAndIds,
|
||||
completedFieldGuides: completedFieldGuides
|
||||
});
|
||||
}
|
||||
|
||||
function returnNextFieldGuide(req, res, next) {
|
||||
if (!req.user) {
|
||||
return res.redirect('/field-guide/how-do-i-use-this-guide');
|
||||
}
|
||||
|
||||
var displayedFieldGuides =
|
||||
FieldGuide.find({'_id': req.user.uncompletedFieldGuides[0]});
|
||||
|
||||
displayedFieldGuides.exec(function(err, fieldGuide) {
|
||||
if (err) { return next(err); }
|
||||
fieldGuide = fieldGuide.pop();
|
||||
|
||||
if (typeof fieldGuide === 'undefined') {
|
||||
if (req.user.completedFieldGuides.length > 0) {
|
||||
req.flash('success', {
|
||||
msg: [
|
||||
"You've read all our current Field Guide entries. You can ",
|
||||
'contribute to our Field Guide ',
|
||||
"<a href='https://github.com/FreeCodeCamp/freecodecamp/blob/",
|
||||
"staging/seed_data/field-guides.json'>here</a>."
|
||||
].join('')
|
||||
});
|
||||
}
|
||||
return res.redirect('../field-guide/how-do-i-use-this-guide');
|
||||
}
|
||||
var nameString = fieldGuide.name.toLowerCase().replace(/\s/g, '-');
|
||||
return res.redirect('../field-guide/' + nameString);
|
||||
});
|
||||
}
|
||||
|
||||
function completedFieldGuide(req, res, next) {
|
||||
var fieldGuideId = req.body.fieldGuideInfo.fieldGuideId;
|
||||
|
||||
req.user.completedFieldGuides.push(fieldGuideId);
|
||||
|
||||
var index = req.user.uncompletedFieldGuides.indexOf(fieldGuideId);
|
||||
if (index > -1) {
|
||||
req.user.progressTimestamps.push(Date.now());
|
||||
req.user.uncompletedFieldGuides.splice(index, 1);
|
||||
}
|
||||
|
||||
req.user.save(function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.send(true);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
};
|
||||
|
@ -1,22 +1,23 @@
|
||||
var express = require('express');
|
||||
var router = express.Router();
|
||||
var message =
|
||||
'Learn to Code JavaScript and get a Coding Job by Helping Nonprofits';
|
||||
|
||||
router.get('/', index);
|
||||
module.exports = function(app) {
|
||||
var router = app.loopback.Router();
|
||||
router.get('/', index);
|
||||
|
||||
function index(req, res, next) {
|
||||
if (req.user && !req.user.profile.picture) {
|
||||
req.user.profile.picture =
|
||||
'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png';
|
||||
app.use(router);
|
||||
|
||||
req.user.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
function index(req, res, next) {
|
||||
if (req.user && !req.user.profile.picture) {
|
||||
req.user.profile.picture =
|
||||
'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png';
|
||||
|
||||
req.user.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
res.render('home', { title: message });
|
||||
});
|
||||
} else {
|
||||
res.render('home', { title: message });
|
||||
});
|
||||
} else {
|
||||
res.render('home', { title: message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
};
|
||||
|
@ -1,18 +1,18 @@
|
||||
var express = require('express');
|
||||
var Job = require('../../common/models/Job');
|
||||
var router = express.Router();
|
||||
module.exports = function(app) {
|
||||
var Job = app.models.Job;
|
||||
var router = app.loopback.Router();
|
||||
|
||||
router.get('/jobs', jobsDirectory);
|
||||
router.get('/jobs', jobsDirectory);
|
||||
app.use(router);
|
||||
|
||||
function jobsDirectory(req, res, next) {
|
||||
Job.find({}, function(err, jobs) {
|
||||
if (err) { return next(err); }
|
||||
function jobsDirectory(req, res, next) {
|
||||
Job.find({}, function(err, jobs) {
|
||||
if (err) { return next(err); }
|
||||
|
||||
res.render('jobs/directory', {
|
||||
title: 'Junior JavaScript Engineer Jobs',
|
||||
jobs: jobs
|
||||
res.render('jobs/directory', {
|
||||
title: 'Junior JavaScript Engineer Jobs',
|
||||
jobs: jobs
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
}
|
||||
};
|
||||
|
0
server/boot/middlewares.js
Normal file
0
server/boot/middlewares.js
Normal file
@ -1,126 +1,130 @@
|
||||
var express = require('express'),
|
||||
Nonprofit = require('../../common/models/Nonprofit');
|
||||
|
||||
var router = express.Router();
|
||||
module.exports = function(app) {
|
||||
var router = app.loopback.Router();
|
||||
var Nonprofit = app.models.Nonprofit;
|
||||
|
||||
router.get('/nonprofits/directory', nonprofitsDirectory);
|
||||
router.get('/nonprofits/:nonprofitName', returnIndividualNonprofit);
|
||||
router.get('/nonprofits/directory', nonprofitsDirectory);
|
||||
router.get('/nonprofits/:nonprofitName', returnIndividualNonprofit);
|
||||
|
||||
function nonprofitsDirectory(req, res, next) {
|
||||
Nonprofit.find({ estimatedHours: { $gt: 0 } }, function(err, nonprofits) {
|
||||
if (err) { return next(err); }
|
||||
app.use(router);
|
||||
|
||||
res.render('nonprofits/directory', {
|
||||
title: 'Nonprofits we help',
|
||||
nonprofits: nonprofits
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function returnIndividualNonprofit(req, res, next) {
|
||||
var dashedName = req.params.nonprofitName;
|
||||
var nonprofitName = dashedName.replace(/\-/g, ' ');
|
||||
|
||||
Nonprofit.find(
|
||||
{ name: new RegExp(nonprofitName, 'i') },
|
||||
function(err, nonprofit) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (nonprofit.length < 1) {
|
||||
req.flash('errors', {
|
||||
msg: "404: We couldn't find a nonprofit with that name. " +
|
||||
'Please double check the name.'
|
||||
});
|
||||
|
||||
return res.redirect('/nonprofits');
|
||||
}
|
||||
|
||||
nonprofit = nonprofit.pop();
|
||||
var dashedNameFull = nonprofit.name.toLowerCase().replace(/\s/g, '-');
|
||||
if (dashedNameFull !== dashedName) {
|
||||
return res.redirect('../nonprofit/' + dashedNameFull);
|
||||
}
|
||||
var buttonActive = false;
|
||||
if (req.user) {
|
||||
if (req.user.uncompletedBonfires.length === 0) {
|
||||
if (req.user.completedCoursewares.length > 63) {
|
||||
var hasShownInterest =
|
||||
nonprofit.interestedCampers.filter(function ( obj ) {
|
||||
return obj.username === req.user.profile.username;
|
||||
});
|
||||
|
||||
if (hasShownInterest.length === 0) {
|
||||
buttonActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.render('nonprofits/show', {
|
||||
dashedName: dashedNameFull,
|
||||
title: nonprofit.name,
|
||||
logoUrl: nonprofit.logoUrl,
|
||||
estimatedHours: nonprofit.estimatedHours,
|
||||
projectDescription: nonprofit.projectDescription,
|
||||
|
||||
approvedOther:
|
||||
nonprofit.approvedDeliverables.indexOf('other') > -1,
|
||||
approvedWebsite:
|
||||
nonprofit.approvedDeliverables.indexOf('website') > -1,
|
||||
|
||||
approvedDonor:
|
||||
nonprofit.approvedDeliverables.indexOf('donor') > -1,
|
||||
approvedInventory:
|
||||
nonprofit.approvedDeliverables.indexOf('inventory') > -1,
|
||||
|
||||
approvedVolunteer:
|
||||
nonprofit.approvedDeliverables.indexOf('volunteer') > -1,
|
||||
approvedForm:
|
||||
nonprofit.approvedDeliverables.indexOf('form') > -1,
|
||||
|
||||
approvedCommunity:
|
||||
nonprofit.approvedDeliverables.indexOf('community') > -1,
|
||||
approvedELearning:
|
||||
nonprofit.approvedDeliverables.indexOf('eLearning') > -1,
|
||||
|
||||
websiteLink: nonprofit.websiteLink,
|
||||
imageUrl: nonprofit.imageUrl,
|
||||
whatDoesNonprofitDo: nonprofit.whatDoesNonprofitDo,
|
||||
interestedCampers: nonprofit.interestedCampers,
|
||||
assignedCampers: nonprofit.assignedCampers,
|
||||
buttonActive: buttonActive,
|
||||
currentStatus: nonprofit.currentStatus
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
function interestedInNonprofit(req, res, next) {
|
||||
if (req.user) {
|
||||
Nonprofit.findOne(
|
||||
{ name: new RegExp(req.params.nonprofitName.replace(/-/, ' '), 'i') },
|
||||
function(err, nonprofit) {
|
||||
function nonprofitsDirectory(req, res, next) {
|
||||
Nonprofit.find(
|
||||
{ where: { estimatedHours: { $gt: 0 } } },
|
||||
function(err, nonprofits) {
|
||||
if (err) { return next(err); }
|
||||
nonprofit.interestedCampers.push({
|
||||
username: req.user.profile.username,
|
||||
picture: req.user.profile.picture,
|
||||
timeOfInterest: Date.now()
|
||||
});
|
||||
nonprofit.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
req.flash('success', {
|
||||
msg: 'Thanks for expressing interest in this nonprofit project! ' +
|
||||
"We've added you to this project as an interested camper!"
|
||||
});
|
||||
res.redirect('back');
|
||||
|
||||
res.render('nonprofits/directory', {
|
||||
title: 'Nonprofits we help',
|
||||
nonprofits: nonprofits
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
module.exports = router;
|
||||
function returnIndividualNonprofit(req, res, next) {
|
||||
var dashedName = req.params.nonprofitName;
|
||||
var nonprofitName = dashedName.replace(/\-/g, ' ');
|
||||
|
||||
Nonprofit.find(
|
||||
{ where: { name: new RegExp(nonprofitName, 'i') } },
|
||||
function(err, nonprofit) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (nonprofit.length < 1) {
|
||||
req.flash('errors', {
|
||||
msg: "404: We couldn't find a nonprofit with that name. " +
|
||||
'Please double check the name.'
|
||||
});
|
||||
|
||||
return res.redirect('/nonprofits');
|
||||
}
|
||||
|
||||
nonprofit = nonprofit.pop();
|
||||
var dashedNameFull = nonprofit.name.toLowerCase().replace(/\s/g, '-');
|
||||
if (dashedNameFull !== dashedName) {
|
||||
return res.redirect('../nonprofit/' + dashedNameFull);
|
||||
}
|
||||
var buttonActive = false;
|
||||
if (req.user) {
|
||||
if (req.user.uncompletedBonfires.length === 0) {
|
||||
if (req.user.completedCoursewares.length > 63) {
|
||||
var hasShownInterest =
|
||||
nonprofit.interestedCampers.filter(function ( obj ) {
|
||||
return obj.username === req.user.profile.username;
|
||||
});
|
||||
|
||||
if (hasShownInterest.length === 0) {
|
||||
buttonActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.render('nonprofits/show', {
|
||||
dashedName: dashedNameFull,
|
||||
title: nonprofit.name,
|
||||
logoUrl: nonprofit.logoUrl,
|
||||
estimatedHours: nonprofit.estimatedHours,
|
||||
projectDescription: nonprofit.projectDescription,
|
||||
|
||||
approvedOther:
|
||||
nonprofit.approvedDeliverables.indexOf('other') > -1,
|
||||
approvedWebsite:
|
||||
nonprofit.approvedDeliverables.indexOf('website') > -1,
|
||||
|
||||
approvedDonor:
|
||||
nonprofit.approvedDeliverables.indexOf('donor') > -1,
|
||||
approvedInventory:
|
||||
nonprofit.approvedDeliverables.indexOf('inventory') > -1,
|
||||
|
||||
approvedVolunteer:
|
||||
nonprofit.approvedDeliverables.indexOf('volunteer') > -1,
|
||||
approvedForm:
|
||||
nonprofit.approvedDeliverables.indexOf('form') > -1,
|
||||
|
||||
approvedCommunity:
|
||||
nonprofit.approvedDeliverables.indexOf('community') > -1,
|
||||
approvedELearning:
|
||||
nonprofit.approvedDeliverables.indexOf('eLearning') > -1,
|
||||
|
||||
websiteLink: nonprofit.websiteLink,
|
||||
imageUrl: nonprofit.imageUrl,
|
||||
whatDoesNonprofitDo: nonprofit.whatDoesNonprofitDo,
|
||||
interestedCampers: nonprofit.interestedCampers,
|
||||
assignedCampers: nonprofit.assignedCampers,
|
||||
buttonActive: buttonActive,
|
||||
currentStatus: nonprofit.currentStatus
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
function interestedInNonprofit(req, res, next) {
|
||||
if (req.user) {
|
||||
Nonprofit.findOne(
|
||||
{ name: new RegExp(req.params.nonprofitName.replace(/-/, ' '), 'i') },
|
||||
function(err, nonprofit) {
|
||||
if (err) { return next(err); }
|
||||
nonprofit.interestedCampers.push({
|
||||
username: req.user.profile.username,
|
||||
picture: req.user.profile.picture,
|
||||
timeOfInterest: Date.now()
|
||||
});
|
||||
nonprofit.save(function(err) {
|
||||
if (err) { return next(err); }
|
||||
req.flash('success', {
|
||||
msg: 'Thanks for expressing interest in this nonprofit project! ' +
|
||||
"We've added you to this project as an interested camper!"
|
||||
});
|
||||
res.redirect('back');
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
@ -1,68 +1,71 @@
|
||||
var express = require('express'),
|
||||
passport = require('passport'),
|
||||
/*
|
||||
var passport = require('passport'),
|
||||
passportConf = require('../../config/passport');
|
||||
|
||||
var router = express.Router();
|
||||
var passportOptions = {
|
||||
successRedirect: '/',
|
||||
failureRedirect: '/login'
|
||||
};
|
||||
|
||||
router.all('/account', passportConf.isAuthenticated);
|
||||
|
||||
router.get('/auth/twitter', passport.authenticate('twitter'));
|
||||
|
||||
router.get(
|
||||
'/auth/twitter/callback',
|
||||
passport.authenticate('twitter', {
|
||||
module.exports = function(app) {
|
||||
var router = app.loopback.Router();
|
||||
var passportOptions = {
|
||||
successRedirect: '/',
|
||||
failureRedirect: '/login'
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
router.get(
|
||||
'/auth/linkedin',
|
||||
passport.authenticate('linkedin', {
|
||||
state: 'SOME STATE'
|
||||
})
|
||||
);
|
||||
router.all('/account', passportConf.isAuthenticated);
|
||||
|
||||
router.get(
|
||||
'/auth/linkedin/callback',
|
||||
passport.authenticate('linkedin', passportOptions)
|
||||
);
|
||||
router.get('/auth/twitter', passport.authenticate('twitter'));
|
||||
|
||||
router.get(
|
||||
'/auth/facebook',
|
||||
passport.authenticate('facebook', {scope: ['email', 'user_location']})
|
||||
);
|
||||
router.get(
|
||||
'/auth/twitter/callback',
|
||||
passport.authenticate('twitter', {
|
||||
successRedirect: '/',
|
||||
failureRedirect: '/login'
|
||||
})
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/auth/facebook/callback',
|
||||
passport.authenticate('facebook', passportOptions), function (req, res) {
|
||||
res.redirect(req.session.returnTo || '/');
|
||||
}
|
||||
);
|
||||
router.get(
|
||||
'/auth/linkedin',
|
||||
passport.authenticate('linkedin', {
|
||||
state: 'SOME STATE'
|
||||
})
|
||||
);
|
||||
|
||||
router.get('/auth/github', passport.authenticate('github'));
|
||||
router.get(
|
||||
'/auth/linkedin/callback',
|
||||
passport.authenticate('linkedin', passportOptions)
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/auth/github/callback',
|
||||
passport.authenticate('github', passportOptions), function (req, res) {
|
||||
res.redirect(req.session.returnTo || '/');
|
||||
}
|
||||
);
|
||||
router.get(
|
||||
'/auth/facebook',
|
||||
passport.authenticate('facebook', {scope: ['email', 'user_location']})
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/auth/google',
|
||||
passport.authenticate('google', {scope: 'profile email'})
|
||||
);
|
||||
router.get(
|
||||
'/auth/facebook/callback',
|
||||
passport.authenticate('facebook', passportOptions), function (req, res) {
|
||||
res.redirect(req.session.returnTo || '/');
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/auth/google/callback',
|
||||
passport.authenticate('google', passportOptions), function (req, res) {
|
||||
res.redirect(req.session.returnTo || '/');
|
||||
}
|
||||
);
|
||||
router.get('/auth/github', passport.authenticate('github'));
|
||||
|
||||
module.exports = router;
|
||||
router.get(
|
||||
'/auth/github/callback',
|
||||
passport.authenticate('github', passportOptions), function (req, res) {
|
||||
res.redirect(req.session.returnTo || '/');
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/auth/google',
|
||||
passport.authenticate('google', {scope: 'profile email'})
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/auth/google/callback',
|
||||
passport.authenticate('google', passportOptions), function (req, res) {
|
||||
res.redirect(req.session.returnTo || '/');
|
||||
}
|
||||
);
|
||||
|
||||
app.use(router);
|
||||
};
|
||||
*/
|
||||
|
494
server/boot/randomAPIs.js
Normal file
494
server/boot/randomAPIs.js
Normal file
@ -0,0 +1,494 @@
|
||||
var Rx = require('rx'),
|
||||
Twit = require('twit'),
|
||||
async = require('async'),
|
||||
moment = require('moment'),
|
||||
Slack = require('node-slack'),
|
||||
request = require('request'),
|
||||
debug = require('debug')('freecc:cntr:resources'),
|
||||
|
||||
constantStrings = require('../utils/constantStrings.json'),
|
||||
secrets = require('../../config/secrets');
|
||||
|
||||
var slack = new Slack(secrets.slackHook);
|
||||
module.exports = function(app) {
|
||||
var router = app.loopback.Router();
|
||||
var User = app.models.User;
|
||||
var Challenge = app.models.Challenge;
|
||||
var Story = app.models.Store;
|
||||
var FieldGuide = app.models.FieldGuide;
|
||||
var Nonprofit = app.models.Nonprofit;
|
||||
|
||||
router.get('/api/github', githubCalls);
|
||||
router.get('/api/blogger', bloggerCalls);
|
||||
router.get('/api/trello', trelloCalls);
|
||||
router.get('/api/codepen/twitter/:screenName', twitter);
|
||||
router.get('/sitemap.xml', sitemap);
|
||||
router.post('/get-help', getHelp);
|
||||
router.post('/get-pair', getPair);
|
||||
router.get('/chat', chat);
|
||||
router.get('/twitch', twitch);
|
||||
router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
|
||||
router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
|
||||
router.get('/nonprofits', nonprofits);
|
||||
router.get('/nonprofits-form', nonprofitsForm);
|
||||
router.get('/jobs-form', jobsForm);
|
||||
router.get('/submit-cat-photo', catPhotoSubmit);
|
||||
router.get('/unsubscribe/:email', unsubscribe);
|
||||
router.get('/unsubscribed', unsubscribed);
|
||||
router.get('/cats.json', getCats);
|
||||
|
||||
router.get('/api/slack', slackInvite);
|
||||
|
||||
app.use(router);
|
||||
|
||||
function slackInvite(req, res, next) {
|
||||
if (req.user) {
|
||||
if (req.user.email) {
|
||||
var invite = {
|
||||
'email': req.user.email,
|
||||
'token': process.env.SLACK_KEY,
|
||||
'set_active': true
|
||||
};
|
||||
|
||||
var headers = {
|
||||
'User-Agent': 'Node Browser/0.0.1',
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
};
|
||||
|
||||
var options = {
|
||||
url: 'https://freecodecamp.slack.com/api/users.admin.invite',
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
form: invite
|
||||
};
|
||||
|
||||
request(options, function (error, response) {
|
||||
if (!error && response.statusCode === 200) {
|
||||
req.flash('success', {
|
||||
msg: 'We\'ve successfully requested an invite for you.' +
|
||||
' Please check your email and follow the ' +
|
||||
'instructions from Slack.'
|
||||
});
|
||||
req.user.sentSlackInvite = true;
|
||||
req.user.save(function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
return res.redirect('back');
|
||||
});
|
||||
} else {
|
||||
req.flash('errors', {
|
||||
msg: 'The invitation email did not go through for some reason.' +
|
||||
' Please try again or <a href=\'mailto:team@' +
|
||||
'freecodecamp.com?subject=' +
|
||||
'slack%20invite%20failed%20to%20send\'>' +
|
||||
'email us</a>.'
|
||||
});
|
||||
return res.redirect('back');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
req.flash('notice', {
|
||||
msg: 'Before we can send your Slack invite, we need your email ' +
|
||||
'address. Please update your profile information here.'
|
||||
});
|
||||
return res.redirect('/account');
|
||||
}
|
||||
} else {
|
||||
req.flash('notice', {
|
||||
msg: 'You need to sign in to Free Code Camp before ' +
|
||||
'we can send you a Slack invite.'
|
||||
});
|
||||
return res.redirect('/account');
|
||||
}
|
||||
}
|
||||
|
||||
function twitter(req, res, next) {
|
||||
// sends out random tweets about javascript
|
||||
var T = new Twit({
|
||||
'consumer_key': secrets.twitter.consumerKey,
|
||||
'consumer_secret': secrets.twitter.consumerSecret,
|
||||
'access_token': secrets.twitter.token,
|
||||
'access_token_secret': secrets.twitter.tokenSecret
|
||||
});
|
||||
|
||||
var screenName;
|
||||
if (req.params.screenName) {
|
||||
screenName = req.params.screenName;
|
||||
} else {
|
||||
screenName = 'freecodecamp';
|
||||
}
|
||||
|
||||
T.get(
|
||||
'statuses/user_timeline',
|
||||
{
|
||||
'screen_name': screenName,
|
||||
count: 10
|
||||
},
|
||||
function(err, data) {
|
||||
if (err) { return next(err); }
|
||||
return res.json(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function getHelp(req, res) {
|
||||
var userName = req.user.profile.username;
|
||||
var code = req.body.payload.code ? '\n```\n' +
|
||||
req.body.payload.code + '\n```\n'
|
||||
: '';
|
||||
var challenge = req.body.payload.challenge;
|
||||
|
||||
slack.send({
|
||||
text: '*@' + userName + '* wants help with ' + challenge + '. ' +
|
||||
code + 'Hey, *@' + userName + '*, if no one helps you right ' +
|
||||
'away, try typing out your problem in detail to me. Like this: ' +
|
||||
'http://en.wikipedia.org/wiki/Rubber_duck_debugging',
|
||||
channel: '#help',
|
||||
username: 'Debuggy the Rubber Duck',
|
||||
'icon_url': 'https://pbs.twimg.com/profile_images/' +
|
||||
'3609875545/569237541c920fa78d78902069615caf.jpeg'
|
||||
});
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
||||
function getPair(req, res) {
|
||||
var userName = req.user.profile.username;
|
||||
var challenge = req.body.payload.challenge;
|
||||
slack.send({
|
||||
text: [
|
||||
'Anyone want to pair with *@',
|
||||
userName,
|
||||
'* on ',
|
||||
challenge,
|
||||
'?\nMake sure you install Screen Hero here: ',
|
||||
'http://freecodecamp.com/field-guide/how-do-i-install-screenhero\n',
|
||||
'Then start your pair program session with *@',
|
||||
userName,
|
||||
'* by typing \"/hero @',
|
||||
userName,
|
||||
'\" into Slack.\n And *@',
|
||||
userName,
|
||||
'*, be sure to launch Screen Hero, then keep coding. ',
|
||||
'Another camper may pair with you soon.'
|
||||
].join(''),
|
||||
channel: '#letspair',
|
||||
username: 'Companion Cube',
|
||||
'icon_url':
|
||||
'https://lh3.googleusercontent.com/-f6xDPDV2rPE/AAAAAAAAAAI/' +
|
||||
'AAAAAAAAAAA/mdlESXQu11Q/photo.jpg'
|
||||
});
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
||||
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: { 'profile.username': { nlike: '' } },
|
||||
fields: { 'profile.username': true }
|
||||
},
|
||||
function(err, users) {
|
||||
if (err) {
|
||||
debug('User err: ', err);
|
||||
callback(err);
|
||||
} else {
|
||||
Rx.Observable.from(users)
|
||||
.map(function(user) {
|
||||
return user.profile.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)
|
||||
.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)
|
||||
.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)
|
||||
.map(function(nonprofit) {
|
||||
return nonprofit.name;
|
||||
})
|
||||
.toArray()
|
||||
.subscribe(
|
||||
callback.bind(callback, null),
|
||||
callback
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
fieldGuides: function(callback) {
|
||||
FieldGuide.find(
|
||||
{ field: { name: true } },
|
||||
function(err, fieldGuides) {
|
||||
if (err) {
|
||||
debug('User err: ', err);
|
||||
callback(err);
|
||||
} else {
|
||||
Rx.Observable.from(fieldGuides)
|
||||
.map(function(fieldGuide) {
|
||||
return fieldGuide.name;
|
||||
})
|
||||
.toArray()
|
||||
.subscribe(
|
||||
callback.bind(callback, null),
|
||||
callback
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
setTimeout(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,
|
||||
fieldGuides: results.fieldGuides
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function chat(req, res) {
|
||||
if (req.user && req.user.progressTimestamps.length > 5) {
|
||||
res.redirect('http://freecodecamp.slack.com');
|
||||
} else {
|
||||
res.render('resources/chat', {
|
||||
title: 'Watch us code live on Twitch.tv'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function jobsForm(req, res) {
|
||||
res.render('resources/jobs-form', {
|
||||
title: 'Employer Partnership Form for Job Postings,' +
|
||||
' Recruitment and Corporate Sponsorships'
|
||||
});
|
||||
}
|
||||
|
||||
function catPhotoSubmit(req, res) {
|
||||
res.send(
|
||||
'Success! You have submitted your cat photo. Return to your website ' +
|
||||
'by typing any letter into your code editor.'
|
||||
);
|
||||
}
|
||||
|
||||
function nonprofits(req, res) {
|
||||
res.render('resources/nonprofits', {
|
||||
title: 'A guide to our Nonprofit Projects'
|
||||
});
|
||||
}
|
||||
|
||||
function nonprofitsForm(req, res) {
|
||||
res.render('resources/nonprofits-form', {
|
||||
title: 'Nonprofit Projects Proposal Form'
|
||||
});
|
||||
}
|
||||
|
||||
function agileProjectManagers(req, res) {
|
||||
res.render('resources/pmi-acp-agile-project-managers', {
|
||||
title: 'Get Agile Project Management Experience for the PMI-ACP'
|
||||
});
|
||||
}
|
||||
|
||||
function agileProjectManagersForm(req, res) {
|
||||
res.render('resources/pmi-acp-agile-project-managers-form', {
|
||||
title: 'Agile Project Management Program Application Form'
|
||||
});
|
||||
}
|
||||
|
||||
function twitch(req, res) {
|
||||
res.render('resources/twitch', {
|
||||
title: 'Enter Free Code Camp\'s Chat Rooms'
|
||||
});
|
||||
}
|
||||
|
||||
function unsubscribe(req, res, next) {
|
||||
User.findOne({ email: req.params.email }, function(err, user) {
|
||||
if (user) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
user.sendMonthlyEmail = false;
|
||||
user.save(function () {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.redirect('/unsubscribed');
|
||||
});
|
||||
} else {
|
||||
res.redirect('/unsubscribed');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function unsubscribed(req, res) {
|
||||
res.render('resources/unsubscribed', {
|
||||
title: 'You have been unsubscribed'
|
||||
});
|
||||
}
|
||||
|
||||
function githubCalls(req, res, next) {
|
||||
var githubHeaders = {
|
||||
headers: {
|
||||
'User-Agent': constantStrings.gitHubUserAgent
|
||||
},
|
||||
port: 80
|
||||
};
|
||||
request(
|
||||
[
|
||||
'https://api.github.com/repos/freecodecamp/',
|
||||
'freecodecamp/pulls?client_id=',
|
||||
secrets.github.clientID,
|
||||
'&client_secret=',
|
||||
secrets.github.clientSecret
|
||||
].join(''),
|
||||
githubHeaders,
|
||||
function(err, status1, pulls) {
|
||||
if (err) { return next(err); }
|
||||
pulls = pulls ?
|
||||
Object.keys(JSON.parse(pulls)).length :
|
||||
'Can\'t connect to github';
|
||||
|
||||
request(
|
||||
[
|
||||
'https://api.github.com/repos/freecodecamp/',
|
||||
'freecodecamp/issues?client_id=',
|
||||
secrets.github.clientID,
|
||||
'&client_secret=',
|
||||
secrets.github.clientSecret
|
||||
].join(''),
|
||||
githubHeaders,
|
||||
function (err, status2, issues) {
|
||||
if (err) { return next(err); }
|
||||
issues = ((pulls === parseInt(pulls, 10)) && issues) ?
|
||||
Object.keys(JSON.parse(issues)).length - pulls :
|
||||
"Can't connect to GitHub";
|
||||
res.send({
|
||||
issues: issues,
|
||||
pulls: pulls
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function trelloCalls(req, res, next) {
|
||||
request(
|
||||
'https://trello.com/1/boards/BA3xVpz9/cards?key=' +
|
||||
secrets.trello.key,
|
||||
function(err, status, trello) {
|
||||
if (err) { return next(err); }
|
||||
trello = (status && status.statusCode === 200) ?
|
||||
(JSON.parse(trello)) :
|
||||
'Can\'t connect to to Trello';
|
||||
|
||||
res.end(JSON.stringify(trello));
|
||||
});
|
||||
}
|
||||
|
||||
function bloggerCalls(req, res, next) {
|
||||
request(
|
||||
'https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/' +
|
||||
'posts?key=' +
|
||||
secrets.blogger.key,
|
||||
function (err, status, blog) {
|
||||
if (err) { return next(err); }
|
||||
|
||||
blog = (status && status.statusCode === 200) ?
|
||||
JSON.parse(blog) :
|
||||
'Can\'t connect to Blogger';
|
||||
res.end(JSON.stringify(blog));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getCats(req, res) {
|
||||
res.send(
|
||||
[
|
||||
{
|
||||
'name': 'cute',
|
||||
'imageLink': 'https://encrypted-tbn3.gstatic.com/images' +
|
||||
'?q=tbn:ANd9GcRaP1ecF2jerISkdhjr4R9yM9-8ClUy-TA36MnDiFBukd5IvEME0g'
|
||||
},
|
||||
{
|
||||
'name': 'grumpy',
|
||||
'imageLink': 'http://cdn.grumpycats.com/wp-content/uploads/' +
|
||||
'2012/09/GC-Gravatar-copy.png'
|
||||
},
|
||||
{
|
||||
'name': 'mischievous',
|
||||
'imageLink': 'http://www.kittenspet.com/wp-content' +
|
||||
'/uploads/2012/08/cat_with_funny_face_3-200x200.jpg'
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
};
|
@ -1,47 +1,50 @@
|
||||
var express = require('express');
|
||||
var router = express.Router();
|
||||
module.exports = function(app) {
|
||||
var router = app.loopback.Router();
|
||||
|
||||
router.get('/nonprofit-project-instructions', function(req, res) {
|
||||
res.redirect(
|
||||
301,
|
||||
'/field-guide/how-do-free-code-camp\'s-nonprofit-projects-work'
|
||||
);
|
||||
});
|
||||
router.get('/nonprofit-project-instructions', function(req, res) {
|
||||
res.redirect(
|
||||
301,
|
||||
'/field-guide/how-do-free-code-camp\'s-nonprofit-projects-work'
|
||||
);
|
||||
});
|
||||
|
||||
router.get('/agile', function(req, res) {
|
||||
res.redirect(301, '/pmi-acp-agile-project-managers');
|
||||
});
|
||||
router.get('/agile', function(req, res) {
|
||||
res.redirect(301, '/pmi-acp-agile-project-managers');
|
||||
});
|
||||
|
||||
router.get('/live-pair-programming', function(req, res) {
|
||||
res.redirect(301, '/field-guide/live-stream-pair-programming-on-twitch.tv');
|
||||
});
|
||||
router.get('/live-pair-programming', function(req, res) {
|
||||
res.redirect(301, '/field-guide/live-stream-pair-programming-on-twitch.tv');
|
||||
});
|
||||
|
||||
router.get('/install-screenhero', function(req, res) {
|
||||
res.redirect(301, '/field-guide/install-screenhero');
|
||||
});
|
||||
router.get('/install-screenhero', function(req, res) {
|
||||
res.redirect(301, '/field-guide/install-screenhero');
|
||||
});
|
||||
|
||||
router.get('/guide-to-our-nonprofit-projects', function(req, res) {
|
||||
res.redirect(301, '/field-guide/a-guide-to-our-nonprofit-projects');
|
||||
});
|
||||
router.get('/guide-to-our-nonprofit-projects', function(req, res) {
|
||||
res.redirect(301, '/field-guide/a-guide-to-our-nonprofit-projects');
|
||||
});
|
||||
|
||||
router.get('/chromebook', function(req, res) {
|
||||
res.redirect(301, '/field-guide/chromebook');
|
||||
});
|
||||
router.get('/chromebook', function(req, res) {
|
||||
res.redirect(301, '/field-guide/chromebook');
|
||||
});
|
||||
|
||||
router.get('/deploy-a-website', function(req, res) {
|
||||
res.redirect(301, '/field-guide/deploy-a-website');
|
||||
});
|
||||
router.get('/deploy-a-website', function(req, res) {
|
||||
res.redirect(301, '/field-guide/deploy-a-website');
|
||||
});
|
||||
|
||||
router.get('/gmail-shortcuts', function(req, res) {
|
||||
res.redirect(301, '/field-guide/gmail-shortcuts');
|
||||
});
|
||||
router.get('/gmail-shortcuts', function(req, res) {
|
||||
res.redirect(301, '/field-guide/gmail-shortcuts');
|
||||
});
|
||||
|
||||
router.get('/nodeschool-challenges', function(req, res) {
|
||||
res.redirect(301, '/field-guide/nodeschool-challenges');
|
||||
});
|
||||
router.get('/nodeschool-challenges', function(req, res) {
|
||||
res.redirect(301, '/field-guide/nodeschool-challenges');
|
||||
});
|
||||
|
||||
router.get('/privacy', function(req, res) {
|
||||
res.redirect(301, '/field-guide/what-is-the-free-code-camp-privacy-policy?');
|
||||
});
|
||||
router.get('/privacy', function(req, res) {
|
||||
res.redirect(
|
||||
301, '/field-guide/what-is-the-free-code-camp-privacy-policy?'
|
||||
);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
app.use(router);
|
||||
};
|
||||
|
4
server/boot/restApi.js
Normal file
4
server/boot/restApi.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = function mountRestApi(app) {
|
||||
var restApiRoot = app.get('restApiRoot');
|
||||
app.use(restApiRoot, app.loopback.rest());
|
||||
};
|
1060
server/boot/story.js
1060
server/boot/story.js
File diff suppressed because it is too large
Load Diff
1483
server/boot/user.js
1483
server/boot/user.js
File diff suppressed because it is too large
Load Diff
@ -1,446 +0,0 @@
|
||||
var express = require('express'),
|
||||
async = require('async'),
|
||||
moment = require('moment'),
|
||||
Twit = require('twit'),
|
||||
Slack = require('node-slack'),
|
||||
request = require('request'),
|
||||
debug = require('debug')('freecc:cntr:resources'),
|
||||
constantStrings = require('../resources/constantStrings.json'),
|
||||
|
||||
User = require('../../common/models/User'),
|
||||
Challenge = require('../../common/models/Challenge'),
|
||||
Story = require('../../common/models/Story'),
|
||||
FieldGuide = require('../../common/models/FieldGuide'),
|
||||
Nonprofit = require('../../common/models/Nonprofit'),
|
||||
secrets = require('../../config/secrets');
|
||||
|
||||
var slack = new Slack(secrets.slackHook);
|
||||
var router = express.Router();
|
||||
|
||||
router.get('/api/github', githubCalls);
|
||||
router.get('/api/blogger', bloggerCalls);
|
||||
router.get('/api/trello', trelloCalls);
|
||||
router.get('/api/codepen/twitter/:screenName', twitter);
|
||||
router.get('/sitemap.xml', sitemap);
|
||||
router.post('/get-help', getHelp);
|
||||
router.post('/get-pair', getPair);
|
||||
router.get('/chat', chat);
|
||||
router.get('/twitch', twitch);
|
||||
router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
|
||||
router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
|
||||
router.get('/nonprofits', nonprofits);
|
||||
router.get('/nonprofits-form', nonprofitsForm);
|
||||
router.get('/jobs-form', jobsForm);
|
||||
router.get('/submit-cat-photo', catPhotoSubmit);
|
||||
router.get('/unsubscribe/:email', unsubscribe);
|
||||
router.get('/unsubscribed', unsubscribed);
|
||||
router.get('/cats.json', getCats);
|
||||
|
||||
router.get('/api/slack', slackInvite);
|
||||
|
||||
function slackInvite(req, res, next) {
|
||||
if (req.user) {
|
||||
if (req.user.email) {
|
||||
var invite = {
|
||||
'email': req.user.email,
|
||||
'token': process.env.SLACK_KEY,
|
||||
'set_active': true
|
||||
};
|
||||
|
||||
var headers = {
|
||||
'User-Agent': 'Node Browser/0.0.1',
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
};
|
||||
|
||||
var options = {
|
||||
url: 'https://freecodecamp.slack.com/api/users.admin.invite',
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
form: invite
|
||||
};
|
||||
|
||||
request(options, function (error, response) {
|
||||
if (!error && response.statusCode === 200) {
|
||||
req.flash('success', {
|
||||
msg: 'We\'ve successfully requested an invite for you.' +
|
||||
' Please check your email and follow the instructions from Slack.'
|
||||
});
|
||||
req.user.sentSlackInvite = true;
|
||||
req.user.save(function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
return res.redirect('back');
|
||||
});
|
||||
} else {
|
||||
req.flash('errors', {
|
||||
msg: 'The invitation email did not go through for some reason.' +
|
||||
' Please try again or <a href=\'mailto:team@' +
|
||||
'freecodecamp.com?subject=' +
|
||||
'slack%20invite%20failed%20to%20send\'>' +
|
||||
'email us</a>.'
|
||||
});
|
||||
return res.redirect('back');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
req.flash('notice', {
|
||||
msg: 'Before we can send your Slack invite, we need your email ' +
|
||||
'address. Please update your profile information here.'
|
||||
});
|
||||
return res.redirect('/account');
|
||||
}
|
||||
} else {
|
||||
req.flash('notice', {
|
||||
msg: 'You need to sign in to Free Code Camp before ' +
|
||||
'we can send you a Slack invite.'
|
||||
});
|
||||
return res.redirect('/account');
|
||||
}
|
||||
}
|
||||
|
||||
function twitter(req, res, next) {
|
||||
// sends out random tweets about javascript
|
||||
var T = new Twit({
|
||||
'consumer_key': secrets.twitter.consumerKey,
|
||||
'consumer_secret': secrets.twitter.consumerSecret,
|
||||
'access_token': secrets.twitter.token,
|
||||
'access_token_secret': secrets.twitter.tokenSecret
|
||||
});
|
||||
|
||||
var screenName;
|
||||
if (req.params.screenName) {
|
||||
screenName = req.params.screenName;
|
||||
} else {
|
||||
screenName = 'freecodecamp';
|
||||
}
|
||||
|
||||
T.get(
|
||||
'statuses/user_timeline',
|
||||
{
|
||||
'screen_name': screenName,
|
||||
count: 10
|
||||
},
|
||||
function(err, data) {
|
||||
if (err) { return next(err); }
|
||||
return res.json(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function getHelp(req, res) {
|
||||
var userName = req.user.profile.username;
|
||||
var code = req.body.payload.code ? '\n```\n' +
|
||||
req.body.payload.code + '\n```\n'
|
||||
: '';
|
||||
var challenge = req.body.payload.challenge;
|
||||
|
||||
slack.send({
|
||||
text: '*@' + userName + '* wants help with ' + challenge + '. ' +
|
||||
code + 'Hey, *@' + userName + '*, if no one helps you right ' +
|
||||
'away, try typing out your problem in detail to me. Like this: ' +
|
||||
'http://en.wikipedia.org/wiki/Rubber_duck_debugging',
|
||||
channel: '#help',
|
||||
username: 'Debuggy the Rubber Duck',
|
||||
'icon_url': 'https://pbs.twimg.com/profile_images/' +
|
||||
'3609875545/569237541c920fa78d78902069615caf.jpeg'
|
||||
});
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
||||
function getPair(req, res) {
|
||||
var userName = req.user.profile.username;
|
||||
var challenge = req.body.payload.challenge;
|
||||
slack.send({
|
||||
text: [
|
||||
'Anyone want to pair with *@',
|
||||
userName,
|
||||
'* on ',
|
||||
challenge,
|
||||
'?\nMake sure you install Screen Hero here: ',
|
||||
'http://freecodecamp.com/field-guide/how-do-i-install-screenhero\n',
|
||||
'Then start your pair program session with *@',
|
||||
userName,
|
||||
'* by typing \"/hero @',
|
||||
userName,
|
||||
'\" into Slack.\n And *@',
|
||||
userName,
|
||||
'*, be sure to launch Screen Hero, then keep coding. ',
|
||||
'Another camper may pair with you soon.'
|
||||
].join(''),
|
||||
channel: '#letspair',
|
||||
username: 'Companion Cube',
|
||||
'icon_url': 'https://lh3.googleusercontent.com/-f6xDPDV2rPE/AAAAAAAAAAI/' +
|
||||
'AAAAAAAAAAA/mdlESXQu11Q/photo.jpg'
|
||||
});
|
||||
return res.sendStatus(200);
|
||||
}
|
||||
|
||||
function sitemap(req, res, next) {
|
||||
var appUrl = 'http://www.freecodecamp.com';
|
||||
var now = moment(new Date()).format('YYYY-MM-DD');
|
||||
|
||||
|
||||
async.parallel({
|
||||
users: function(callback) {
|
||||
User.aggregate()
|
||||
.group({_id: 1, usernames: { $addToSet: '$profile.username'}})
|
||||
.match({'profile.username': { $ne: ''}})
|
||||
.exec(function(err, users) {
|
||||
if (err) {
|
||||
debug('User err: ', err);
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, users[0].usernames);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
challenges: function (callback) {
|
||||
Challenge.aggregate()
|
||||
.group({_id: 1, names: { $addToSet: '$name'}})
|
||||
.exec(function (err, challenges) {
|
||||
if (err) {
|
||||
debug('Challenge err: ', err);
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, challenges[0].names);
|
||||
}
|
||||
});
|
||||
},
|
||||
stories: function (callback) {
|
||||
Story.aggregate()
|
||||
.group({_id: 1, links: {$addToSet: '$link'}})
|
||||
.exec(function (err, stories) {
|
||||
if (err) {
|
||||
debug('Story err: ', err);
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, stories[0].links);
|
||||
}
|
||||
});
|
||||
},
|
||||
nonprofits: function (callback) {
|
||||
Nonprofit.aggregate()
|
||||
.group({_id: 1, names: { $addToSet: '$name'}})
|
||||
.exec(function (err, nonprofits) {
|
||||
if (err) {
|
||||
debug('User err: ', err);
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, nonprofits[0].names);
|
||||
}
|
||||
});
|
||||
},
|
||||
fieldGuides: function (callback) {
|
||||
FieldGuide.aggregate()
|
||||
.group({_id: 1, names: { $addToSet: '$name'}})
|
||||
.exec(function (err, fieldGuides) {
|
||||
if (err) {
|
||||
debug('User err: ', err);
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, fieldGuides[0].names);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
} else {
|
||||
setTimeout(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,
|
||||
fieldGuides: results.fieldGuides
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function chat(req, res) {
|
||||
if (req.user && req.user.progressTimestamps.length > 5) {
|
||||
res.redirect('http://freecodecamp.slack.com');
|
||||
} else {
|
||||
res.render('resources/chat', {
|
||||
title: 'Watch us code live on Twitch.tv'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function jobsForm(req, res) {
|
||||
res.render('resources/jobs-form', {
|
||||
title: 'Employer Partnership Form for Job Postings,' +
|
||||
' Recruitment and Corporate Sponsorships'
|
||||
});
|
||||
}
|
||||
|
||||
function catPhotoSubmit(req, res) {
|
||||
res.send(
|
||||
'Success! You have submitted your cat photo. Return to your website ' +
|
||||
'by typing any letter into your code editor.'
|
||||
);
|
||||
}
|
||||
|
||||
function nonprofits(req, res) {
|
||||
res.render('resources/nonprofits', {
|
||||
title: 'A guide to our Nonprofit Projects'
|
||||
});
|
||||
}
|
||||
|
||||
function nonprofitsForm(req, res) {
|
||||
res.render('resources/nonprofits-form', {
|
||||
title: 'Nonprofit Projects Proposal Form'
|
||||
});
|
||||
}
|
||||
|
||||
function agileProjectManagers(req, res) {
|
||||
res.render('resources/pmi-acp-agile-project-managers', {
|
||||
title: 'Get Agile Project Management Experience for the PMI-ACP'
|
||||
});
|
||||
}
|
||||
|
||||
function agileProjectManagersForm(req, res) {
|
||||
res.render('resources/pmi-acp-agile-project-managers-form', {
|
||||
title: 'Agile Project Management Program Application Form'
|
||||
});
|
||||
}
|
||||
|
||||
function twitch(req, res) {
|
||||
res.render('resources/twitch', {
|
||||
title: 'Enter Free Code Camp\'s Chat Rooms'
|
||||
});
|
||||
}
|
||||
|
||||
function unsubscribe(req, res, next) {
|
||||
User.findOne({ email: req.params.email }, function(err, user) {
|
||||
if (user) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
user.sendMonthlyEmail = false;
|
||||
user.save(function () {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
res.redirect('/unsubscribed');
|
||||
});
|
||||
} else {
|
||||
res.redirect('/unsubscribed');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function unsubscribed(req, res) {
|
||||
res.render('resources/unsubscribed', {
|
||||
title: 'You have been unsubscribed'
|
||||
});
|
||||
}
|
||||
|
||||
function githubCalls(req, res, next) {
|
||||
var githubHeaders = {
|
||||
headers: {
|
||||
'User-Agent': constantStrings.gitHubUserAgent
|
||||
},
|
||||
port: 80
|
||||
};
|
||||
request(
|
||||
[
|
||||
'https://api.github.com/repos/freecodecamp/',
|
||||
'freecodecamp/pulls?client_id=',
|
||||
secrets.github.clientID,
|
||||
'&client_secret=',
|
||||
secrets.github.clientSecret
|
||||
].join(''),
|
||||
githubHeaders,
|
||||
function(err, status1, pulls) {
|
||||
if (err) { return next(err); }
|
||||
pulls = pulls ?
|
||||
Object.keys(JSON.parse(pulls)).length :
|
||||
'Can\'t connect to github';
|
||||
|
||||
request(
|
||||
[
|
||||
'https://api.github.com/repos/freecodecamp/',
|
||||
'freecodecamp/issues?client_id=',
|
||||
secrets.github.clientID,
|
||||
'&client_secret=',
|
||||
secrets.github.clientSecret
|
||||
].join(''),
|
||||
githubHeaders,
|
||||
function (err, status2, issues) {
|
||||
if (err) { return next(err); }
|
||||
issues = ((pulls === parseInt(pulls, 10)) && issues) ?
|
||||
Object.keys(JSON.parse(issues)).length - pulls :
|
||||
"Can't connect to GitHub";
|
||||
res.send({
|
||||
issues: issues,
|
||||
pulls: pulls
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function trelloCalls(req, res, next) {
|
||||
request(
|
||||
'https://trello.com/1/boards/BA3xVpz9/cards?key=' +
|
||||
secrets.trello.key,
|
||||
function(err, status, trello) {
|
||||
if (err) { return next(err); }
|
||||
trello = (status && status.statusCode === 200) ?
|
||||
(JSON.parse(trello)) :
|
||||
'Can\'t connect to to Trello';
|
||||
|
||||
res.end(JSON.stringify(trello));
|
||||
});
|
||||
}
|
||||
|
||||
function bloggerCalls(req, res, next) {
|
||||
request(
|
||||
'https://www.googleapis.com/blogger/v3/blogs/2421288658305323950/' +
|
||||
'posts?key=' +
|
||||
secrets.blogger.key,
|
||||
function (err, status, blog) {
|
||||
if (err) { return next(err); }
|
||||
|
||||
blog = (status && status.statusCode === 200) ?
|
||||
JSON.parse(blog) :
|
||||
'Can\'t connect to Blogger';
|
||||
res.end(JSON.stringify(blog));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getCats(req, res) {
|
||||
res.send(
|
||||
[
|
||||
{
|
||||
'name': 'cute',
|
||||
'imageLink': 'https://encrypted-tbn3.gstatic.com/images' +
|
||||
'?q=tbn:ANd9GcRaP1ecF2jerISkdhjr4R9yM9-8ClUy-TA36MnDiFBukd5IvEME0g'
|
||||
},
|
||||
{
|
||||
'name': 'grumpy',
|
||||
'imageLink': 'http://cdn.grumpycats.com/wp-content/uploads/' +
|
||||
'2012/09/GC-Gravatar-copy.png'
|
||||
},
|
||||
{
|
||||
'name': 'mischievous',
|
||||
'imageLink': 'http://www.kittenspet.com/wp-content' +
|
||||
'/uploads/2012/08/cat_with_funny_face_3-200x200.jpg'
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = router;
|
18
server/config.development.js
Normal file
18
server/config.development.js
Normal file
@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
host: '127.0.0.1',
|
||||
sessionSecret: process.env.SESSION_SECRET,
|
||||
|
||||
trello: {
|
||||
key: process.env.TRELLO_KEY,
|
||||
secret: process.env.TRELLO_SECRET
|
||||
},
|
||||
|
||||
blogger: {
|
||||
key: process.env.BLOGGER_KEY
|
||||
},
|
||||
|
||||
github: {
|
||||
clientID: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET
|
||||
}
|
||||
};
|
29
server/config.json
Normal file
29
server/config.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"restApiRoot": "/api",
|
||||
"host": "0.0.0.0",
|
||||
"port": 3000,
|
||||
"remoting": {
|
||||
"context": {
|
||||
"enableHttpContext": false
|
||||
},
|
||||
"rest": {
|
||||
"normalizeHttpPath": false,
|
||||
"xml": false
|
||||
},
|
||||
"json": {
|
||||
"strict": false,
|
||||
"limit": "100kb"
|
||||
},
|
||||
"urlencoded": {
|
||||
"extended": true,
|
||||
"limit": "100kb"
|
||||
},
|
||||
"cors": {
|
||||
"origin": true,
|
||||
"credentials": true
|
||||
},
|
||||
"errorHandler": {
|
||||
"disableStackTrace": false
|
||||
}
|
||||
}
|
||||
}
|
5
server/config.local.js
Normal file
5
server/config.local.js
Normal file
@ -0,0 +1,5 @@
|
||||
var globalConfig = require('../common/config.global');
|
||||
|
||||
module.exports = {
|
||||
restApiRoot: globalConfig.restApi
|
||||
};
|
6
server/datasources.development.js
Normal file
6
server/datasources.development.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
db: {
|
||||
connector: 'mongodb',
|
||||
url: process.env.MONGOHQ_URL
|
||||
}
|
||||
};
|
9
server/datasources.json
Normal file
9
server/datasources.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"db": {
|
||||
"name": "db",
|
||||
"connector": "mongodb",
|
||||
"host": "127.0.0.1",
|
||||
"database": "foobar",
|
||||
"port": 27017
|
||||
}
|
||||
}
|
27
server/middleware.json
Normal file
27
server/middleware.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"initial:before": {
|
||||
"loopback#favicon": {}
|
||||
},
|
||||
"initial": {
|
||||
"compression": {}
|
||||
},
|
||||
"session": {
|
||||
},
|
||||
"auth": {
|
||||
},
|
||||
"parse": {
|
||||
},
|
||||
"routes": {
|
||||
},
|
||||
"files": {
|
||||
"loopback#static": {
|
||||
"params": "$!../public"
|
||||
}
|
||||
},
|
||||
"final": {
|
||||
"loopback#urlNotFound": {}
|
||||
},
|
||||
"final:after": {
|
||||
"errorhandler": {}
|
||||
}
|
||||
}
|
69
server/model-config.json
Normal file
69
server/model-config.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"_meta": {
|
||||
"sources": [
|
||||
"loopback/common/models",
|
||||
"loopback/server/models",
|
||||
"../common/models",
|
||||
"./models"
|
||||
]
|
||||
},
|
||||
"User": {
|
||||
"dataSource": "db"
|
||||
},
|
||||
"AccessToken": {
|
||||
"dataSource": "db",
|
||||
"public": false
|
||||
},
|
||||
"ACL": {
|
||||
"dataSource": "db",
|
||||
"public": false
|
||||
},
|
||||
"RoleMapping": {
|
||||
"dataSource": "db",
|
||||
"public": false
|
||||
},
|
||||
"Role": {
|
||||
"dataSource": "db",
|
||||
"public": false
|
||||
},
|
||||
"bonfire": {
|
||||
"dataSource": "db",
|
||||
"public": true
|
||||
},
|
||||
"challenge": {
|
||||
"dataSource": "db",
|
||||
"public": true
|
||||
},
|
||||
"comment": {
|
||||
"dataSource": "db",
|
||||
"public": true
|
||||
},
|
||||
"fieldGuide": {
|
||||
"dataSource": "db",
|
||||
"public": true
|
||||
},
|
||||
"job": {
|
||||
"dataSource": "db",
|
||||
"public": true
|
||||
},
|
||||
"nonprofit": {
|
||||
"dataSource": "db",
|
||||
"public": true
|
||||
},
|
||||
"story": {
|
||||
"dataSource": "db",
|
||||
"public": true
|
||||
},
|
||||
"user": {
|
||||
"dataSource": "db",
|
||||
"public": true
|
||||
},
|
||||
"userCredential": {
|
||||
"dataSource": "db",
|
||||
"public": true
|
||||
},
|
||||
"userIdentity": {
|
||||
"dataSource": "db",
|
||||
"public": true
|
||||
}
|
||||
}
|
122
server/passport-providers.js
Normal file
122
server/passport-providers.js
Normal file
@ -0,0 +1,122 @@
|
||||
var successRedirect = '/';
|
||||
var failureRedirect = '/login';
|
||||
module.exports = {
|
||||
local: {
|
||||
provider: 'local',
|
||||
module: 'passport-local',
|
||||
usernameField: 'email',
|
||||
passwordField: 'password',
|
||||
authPath: '/auth/local',
|
||||
successRedirect: successRedirect,
|
||||
failureRedirect: failureRedirect,
|
||||
failureFlash: true
|
||||
},
|
||||
'facebook-login': {
|
||||
provider: 'facebook',
|
||||
module: 'passport-facebook',
|
||||
clientID: process.env.FACEBOOK_ID,
|
||||
clientSecret: process.env.FACEBOOK_SECRET,
|
||||
authPath: '/auth/facebook',
|
||||
callbackURL: '/auth/facebook/callback',
|
||||
callbackPath: '/auth/facebook/callback',
|
||||
successRedirect: successRedirect,
|
||||
failureRedirect: failureRedirect,
|
||||
scope: ['email'],
|
||||
failureFlash: true
|
||||
},
|
||||
'facebook-link': {
|
||||
provider: 'facebook',
|
||||
module: 'passport-facebook',
|
||||
clientID: process.env.FACEBOOK_ID,
|
||||
clientSecret: process.env.FACEBOOK_SECRET,
|
||||
authPath: '/link/facebook',
|
||||
callbackURL: '/link/facebook/callback',
|
||||
callbackPath: '/link/facebook/callback',
|
||||
successRedirect: successRedirect,
|
||||
failureRedirect: failureRedirect,
|
||||
scope: ['email', 'user_likes'],
|
||||
link: true,
|
||||
failureFlash: true
|
||||
},
|
||||
'google-login': {
|
||||
provider: 'google',
|
||||
module: 'passport-google-oauth2',
|
||||
clientID: process.env.GOOGLE_ID,
|
||||
clientSecret: process.env.GOOGLE_SECRET,
|
||||
authPath: '/auth/google',
|
||||
callbackURL: '/auth/google/callback',
|
||||
callbackPath: '/auth/google/callback',
|
||||
successRedirect: successRedirect,
|
||||
failureRedirect: failureRedirect,
|
||||
scope: ['email', 'profile'],
|
||||
failureFlash: true
|
||||
},
|
||||
'google-link': {
|
||||
provider: 'google',
|
||||
module: 'passport-google-oauth2',
|
||||
clientID: process.env.GOOGLE_ID,
|
||||
clientSecret: process.env.GOOGLE_SECRET,
|
||||
authPath: '/link/google',
|
||||
callbackURL: '/link/google/callback',
|
||||
callbackPath: '/link/google/callback',
|
||||
successRedirect: successRedirect,
|
||||
failureRedirect: failureRedirect,
|
||||
scope: ['email', 'profile'],
|
||||
link: true,
|
||||
failureFlash: true
|
||||
},
|
||||
'twitter-login': {
|
||||
provider: 'twitter',
|
||||
authScheme: 'oauth',
|
||||
module: 'passport-twitter',
|
||||
authPath: '/auth/twitter',
|
||||
callbackURL: '/auth/twitter/callback',
|
||||
callbackPath: '/auth/twitter/callback',
|
||||
successRedirect: successRedirect,
|
||||
failureRedirect: failureRedirect,
|
||||
consumerKey: process.env.TWITTER_KEY,
|
||||
consumerSecret: process.env.TWITTER_SECRET,
|
||||
failureFlash: true
|
||||
},
|
||||
'twitter-link': {
|
||||
provider: 'twitter',
|
||||
authScheme: 'oauth',
|
||||
module: 'passport-twitter',
|
||||
authPath: '/link/twitter',
|
||||
callbackURL: '/link/twitter/callback',
|
||||
callbackPath: '/link/twitter/callback',
|
||||
successRedirect: successRedirect,
|
||||
failureRedirect: failureRedirect,
|
||||
consumerKey: process.env.TWITTER_KEY,
|
||||
consumerSecret: process.env.TWITTER_SECRET,
|
||||
failureFlash: true
|
||||
},
|
||||
'linkedin-login': {
|
||||
provider: 'linkedin',
|
||||
authScheme: 'oauth',
|
||||
module: 'passport-linkedin-oauth2',
|
||||
authPath: '/auth/linkedin',
|
||||
callbackURL: '/auth/linkedin/callback',
|
||||
callbackPath: '/auth/linkedin/callback',
|
||||
successRedirect: successRedirect,
|
||||
failureRedirect: failureRedirect,
|
||||
clientID: process.env.LINKEDIN_ID,
|
||||
clientSecret: process.env.LINKEDIN_SECRET,
|
||||
scope: ['r_fullprofile', 'r_emailaddress'],
|
||||
failureFlash: true
|
||||
},
|
||||
'linkedin-link': {
|
||||
provider: 'linkedin',
|
||||
authScheme: 'oauth',
|
||||
module: 'passport-linkedin-oauth2',
|
||||
authPath: '/link/linkedin',
|
||||
callbackURL: '/link/linkedin/callback',
|
||||
callbackPath: '/link/linkedin/callback',
|
||||
successRedirect: successRedirect,
|
||||
failureRedirect: failureRedirect,
|
||||
clientID: process.env.LINKEDIN_ID,
|
||||
clientSecret: process.env.LINKEDIN_SECRET,
|
||||
scope: ['r_fullprofile', 'r_emailaddress'],
|
||||
failureFlash: true
|
||||
}
|
||||
};
|
135
server/server.js
135
server/server.js
@ -9,64 +9,38 @@ process.on('uncaughtException', function (err) {
|
||||
process.exit(1); // eslint-disable-line
|
||||
});
|
||||
|
||||
var express = require('express'),
|
||||
accepts = require('accepts'),
|
||||
cookieParser = require('cookie-parser'),
|
||||
compress = require('compression'),
|
||||
session = require('express-session'),
|
||||
logger = require('morgan'),
|
||||
errorHandler = require('errorhandler'),
|
||||
methodOverride = require('method-override'),
|
||||
bodyParser = require('body-parser'),
|
||||
helmet = require('helmet'),
|
||||
MongoStore = require('connect-mongo')(session),
|
||||
flash = require('express-flash'),
|
||||
path = require('path'),
|
||||
mongoose = require('mongoose'),
|
||||
passport = require('passport'),
|
||||
expressValidator = require('express-validator'),
|
||||
// request = require('request'),
|
||||
forceDomain = require('forcedomain'),
|
||||
lessMiddleware = require('less-middleware'),
|
||||
var R = require('ramda'),
|
||||
loopback = require('loopback'),
|
||||
boot = require('loopback-boot'),
|
||||
accepts = require('accepts'),
|
||||
cookieParser = require('cookie-parser'),
|
||||
compress = require('compression'),
|
||||
session = require('express-session'),
|
||||
logger = require('morgan'),
|
||||
errorHandler = require('errorhandler'),
|
||||
methodOverride = require('method-override'),
|
||||
bodyParser = require('body-parser'),
|
||||
helmet = require('helmet'),
|
||||
MongoStore = require('connect-mongo')(session),
|
||||
flash = require('express-flash'),
|
||||
path = require('path'),
|
||||
expressValidator = require('express-validator'),
|
||||
forceDomain = require('forcedomain'),
|
||||
lessMiddleware = require('less-middleware'),
|
||||
|
||||
/**
|
||||
* routers.
|
||||
*/
|
||||
homeRouter = require('./boot/home'),
|
||||
userRouter = require('./boot/user'),
|
||||
fieldGuideRouter = require('./boot/fieldGuide'),
|
||||
challengeMapRouter = require('./boot/challengeMap'),
|
||||
challengeRouter = require('./boot/challenge'),
|
||||
jobsRouter = require('./boot/jobs'),
|
||||
redirectsRouter = require('./boot/redirects'),
|
||||
utilityRouter = require('./boot/utility'),
|
||||
storyRouter = require('./boot/story'),
|
||||
passportRouter = require('./boot/passport'),
|
||||
|
||||
/**
|
||||
* API keys and Passport configuration.
|
||||
*/
|
||||
secrets = require('./../config/secrets');
|
||||
passportProviders = require('./passport-providers'),
|
||||
/**
|
||||
* API keys and Passport configuration.
|
||||
*/
|
||||
secrets = require('./../config/secrets');
|
||||
|
||||
/**
|
||||
* Create Express server.
|
||||
*/
|
||||
var app = express();
|
||||
|
||||
/**
|
||||
* Connect to MongoDB.
|
||||
*/
|
||||
mongoose.connect(secrets.db);
|
||||
mongoose.connection.on('error', function () {
|
||||
console.error(
|
||||
'MongoDB Connection Error. Please make sure that MongoDB is running.'
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Express configuration.
|
||||
*/
|
||||
|
||||
var app = loopback();
|
||||
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'));
|
||||
@ -101,8 +75,7 @@ app.use(session({
|
||||
'autoReconnect': true
|
||||
})
|
||||
}));
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
app.use(flash());
|
||||
app.disable('x-powered-by');
|
||||
|
||||
@ -191,6 +164,8 @@ app.use(helmet.csp({
|
||||
safari5: false
|
||||
}));
|
||||
|
||||
passportConfigurator.init();
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
// Make user object available in templates.
|
||||
res.locals.user = req.user;
|
||||
@ -198,9 +173,14 @@ app.use(function (req, res, next) {
|
||||
});
|
||||
|
||||
app.use(
|
||||
express.static(path.join(__dirname, '../public'), { maxAge: 86400000 })
|
||||
loopback.static(path.join(__dirname, '../public'), { maxAge: 86400000 })
|
||||
);
|
||||
|
||||
boot(app, {
|
||||
appRootDir: __dirname,
|
||||
dev: process.env.NODE_ENV
|
||||
});
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
// Remember original destination before login.
|
||||
var path = req.path.split('/')[1];
|
||||
@ -213,17 +193,17 @@ app.use(function (req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
// add sub routers
|
||||
app.use(fieldGuideRouter);
|
||||
app.use(challengeMapRouter);
|
||||
app.use(challengeRouter);
|
||||
app.use(jobsRouter);
|
||||
app.use(redirectsRouter);
|
||||
app.use(utilityRouter);
|
||||
app.use(storyRouter);
|
||||
app.use(passportRouter);
|
||||
app.use(homeRouter);
|
||||
app.use(userRouter);
|
||||
passportConfigurator.setupModels({
|
||||
userModel: app.models.user,
|
||||
userIdentityModel: app.models.userIdentity,
|
||||
userCredentialModel: app.models.userCredential
|
||||
});
|
||||
|
||||
R.keys(passportProviders).map(function(strategy) {
|
||||
var config = passportProviders[strategy];
|
||||
config.session = config.session !== false;
|
||||
passportConfigurator.configureProvider(strategy, config);
|
||||
});
|
||||
|
||||
/**
|
||||
* OAuth sign-in routes.
|
||||
@ -273,12 +253,19 @@ if (process.env.NODE_ENV === 'development') {
|
||||
* Start Express server.
|
||||
*/
|
||||
|
||||
app.listen(app.get('port'), function () {
|
||||
console.log(
|
||||
'FreeCodeCamp server listening on port %d in %s mode',
|
||||
app.get('port'),
|
||||
app.get('env')
|
||||
);
|
||||
});
|
||||
app.start = function() {
|
||||
app.listen(app.get('port'), function () {
|
||||
console.log(
|
||||
'FreeCodeCamp server listening on port %d in %s mode',
|
||||
app.get('port'),
|
||||
app.get('env')
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// start the server if `$ node server.js`
|
||||
if (require.main === module) {
|
||||
app.start();
|
||||
}
|
||||
|
||||
module.exports = app;
|
||||
|
@ -1,18 +1,15 @@
|
||||
var async = require('async'),
|
||||
path = require('path'),
|
||||
// debug = require('debug')('freecc:cntr:resources'),
|
||||
cheerio = require('cheerio'),
|
||||
request = require('request'),
|
||||
R = require('ramda'),
|
||||
_ = require('lodash'),
|
||||
fs = require('fs'),
|
||||
var path = require('path'),
|
||||
// debug = require('debug')('freecc:cntr:resources'),
|
||||
cheerio = require('cheerio'),
|
||||
request = require('request'),
|
||||
R = require('ramda'),
|
||||
_ = require('lodash'),
|
||||
fs = require('fs'),
|
||||
|
||||
|
||||
Story = require('../../common/models/Story'),
|
||||
Comment = require('../../common/models/Comment'),
|
||||
resources = require('./resources.json'),
|
||||
nonprofits = require('../../seed_data/nonprofits.json'),
|
||||
fieldGuides = require('../../seed_data/field-guides.json');
|
||||
resources = require('./resources.json'),
|
||||
nonprofits = require('../../seed/nonprofits.json'),
|
||||
fieldGuides = require('../../seed/field-guides.json');
|
||||
|
||||
/**
|
||||
* Cached values
|
||||
@ -41,12 +38,12 @@ Array.zip = function(left, right, combinerFunction) {
|
||||
if (!challengeMap) {
|
||||
var localChallengeMap = {};
|
||||
var files = fs.readdirSync(
|
||||
path.join(__dirname, '../../seed_data/challenges')
|
||||
path.join(__dirname, '../../seed/challenges')
|
||||
);
|
||||
var keyCounter = 0;
|
||||
files = files.map(function (file) {
|
||||
return require(
|
||||
path.join(__dirname, '../../seed_data/challenges/' + file)
|
||||
path.join(__dirname, '../../seed/challenges/' + file)
|
||||
);
|
||||
});
|
||||
files = files.sort(function (a, b) {
|
||||
@ -215,59 +212,5 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
})();
|
||||
},
|
||||
|
||||
updateUserStoryPictures: function (userId, picture, username, cb) {
|
||||
|
||||
var counter = 0,
|
||||
foundStories,
|
||||
foundComments;
|
||||
|
||||
Story.find({'author.userId': userId}, function (err, stories) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
foundStories = stories;
|
||||
counter++;
|
||||
saveStoriesAndComments();
|
||||
});
|
||||
Comment.find({'author.userId': userId}, function (err, comments) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
foundComments = comments;
|
||||
counter++;
|
||||
saveStoriesAndComments();
|
||||
});
|
||||
|
||||
function saveStoriesAndComments() {
|
||||
if (counter !== 2) {
|
||||
return;
|
||||
}
|
||||
var tasks = [];
|
||||
R.forEach(function (comment) {
|
||||
comment.author.picture = picture;
|
||||
comment.author.username = username;
|
||||
comment.markModified('author');
|
||||
tasks.push(function (cb) {
|
||||
comment.save(cb);
|
||||
});
|
||||
}, foundComments);
|
||||
|
||||
R.forEach(function (story) {
|
||||
story.author.picture = picture;
|
||||
story.author.username = username;
|
||||
story.markModified('author');
|
||||
tasks.push(function (cb) {
|
||||
story.save(cb);
|
||||
});
|
||||
}, foundStories);
|
||||
async.parallel(tasks, function (err) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user