Merge branch 'staging' of https://github.com/FreeCodeCamp/freecodecamp into greasan-translateDE
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -10,7 +10,7 @@ lib-cov
|
|||||||
.floo
|
.floo
|
||||||
.flooignore
|
.flooignore
|
||||||
builtAssets/
|
builtAssets/
|
||||||
|
pm2.js
|
||||||
*.env
|
*.env
|
||||||
pids
|
pids
|
||||||
logs
|
logs
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
<img src="https://s3.amazonaws.com/freecodecamp/wide-social-banner.png">
|
||||||
|
|
||||||
[](https://waffle.io/freecodecamp/freecodecamp/metrics)
|
[](https://waffle.io/freecodecamp/freecodecamp/metrics)
|
||||||
|
|
||||||
[](https://waffle.io/FreeCodeCamp/freecodecamp)
|
[](https://gitter.im/freecodecamp/freecodecamp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
<img src="https://s3.amazonaws.com/freecodecamp/logo4.0LG.png">
|
|
||||||
|
|
||||||
Welcome to Free Code Camp's open source codebase!
|
Welcome to Free Code Camp's open source codebase!
|
||||||
=======================
|
=======================
|
||||||
@ -17,7 +18,7 @@ Our campers (students) start by working through our free, self-paced, browser-ba
|
|||||||
|
|
||||||
80% of our campers are over 25, and nearly a fifth of our campers are women.
|
80% of our campers are over 25, and nearly a fifth of our campers are women.
|
||||||
|
|
||||||
This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We also have [Slack](http://freecodecamp.slack.com), a [blog](http://blog.freecodecamp.com), and even a [Twitch.tv channel](http://twitch.tv/freecodecamp).
|
This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We also have [Gitter](https://gitter.im/FreeCodeCamp/FreeCodeCamp), a [blog](http://blog.freecodecamp.com), and even a [Twitch.tv channel](http://twitch.tv/freecodecamp).
|
||||||
|
|
||||||
[Join our community](http://www.freecodecamp.com/signin)!
|
[Join our community](http://www.freecodecamp.com/signin)!
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ Contributing
|
|||||||
We welcome pull requests from Free Code Camp campers (our students) and seasoned JavaScript developers alike! Follow these steps to contribute:
|
We welcome pull requests from Free Code Camp campers (our students) and seasoned JavaScript developers alike! Follow these steps to contribute:
|
||||||
|
|
||||||
1. Check our [public Waffle Board](https://waffle.io/freecodecamp/freecodecamp).
|
1. Check our [public Waffle Board](https://waffle.io/freecodecamp/freecodecamp).
|
||||||
2. Pick an issue that nobody has claimed and start working on it. If your issue isn't on the board, open an issue. If you think you can fix it yourself, start working on it. Feel free to ask for help in our [Slack](http://freecodecamp.slack.com).
|
2. Pick an issue that nobody has claimed and start working on it. If your issue isn't on the board, open an issue. If you think you can fix it yourself, start working on it. Feel free to ask for help in our [Gitter](https://gitter.im/FreeCodeCamp/FreeCodeCamp)
|
||||||
3. Fork the project ([Need help with forking a project?](https://help.github.com/articles/fork-a-repo/)). You'll do all of your work on your forked copy.
|
3. Fork the project ([Need help with forking a project?](https://help.github.com/articles/fork-a-repo/)). You'll do all of your work on your forked copy.
|
||||||
4. Create a branch specific to the issue or feature you are working on. Push your work to that branch. ([Need help with branching?](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches))
|
4. Create a branch specific to the issue or feature you are working on. Push your work to that branch. ([Need help with branching?](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches))
|
||||||
5. Name the branch something like `user-xxx` where user is your username and xxx is the issue number you are addressing.
|
5. Name the branch something like `user-xxx` where user is your username and xxx is the issue number you are addressing.
|
||||||
|
@ -11,6 +11,13 @@
|
|||||||
"foreignKey": "userId"
|
"foreignKey": "userId"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"acls": [],
|
"acls": [
|
||||||
|
{
|
||||||
|
"accessType": "*",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "$everyone",
|
||||||
|
"permission": "DENY"
|
||||||
|
}
|
||||||
|
],
|
||||||
"methods": []
|
"methods": []
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
{
|
{
|
||||||
"accessType": "READ",
|
"accessType": "READ",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$everyone",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
{
|
{
|
||||||
"accessType": "READ",
|
"accessType": "READ",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$everyone",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -22,14 +22,15 @@
|
|||||||
},
|
},
|
||||||
"rank": {
|
"rank": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"default": "-Infinity"
|
"default": 0
|
||||||
},
|
},
|
||||||
"upvotes": {
|
"upvotes": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"default": []
|
"default": []
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"type": {}
|
"type": {},
|
||||||
|
"default": {}
|
||||||
},
|
},
|
||||||
"comments": {
|
"comments": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@ -49,6 +50,12 @@
|
|||||||
"principalId": "$everyone",
|
"principalId": "$everyone",
|
||||||
"permission": "DENY"
|
"permission": "DENY"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"accessType": "READ",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "$everyone",
|
||||||
|
"permission": "ALLOW"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"accessType": "EXECUTE",
|
"accessType": "EXECUTE",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
{
|
{
|
||||||
"accessType": "READ",
|
"accessType": "READ",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$everyone",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
{
|
{
|
||||||
"accessType": "READ",
|
"accessType": "READ",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$everyone",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -8,21 +8,12 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"unique": true
|
"unique": true
|
||||||
},
|
},
|
||||||
"requestedDeliverables": {
|
|
||||||
"type": "array"
|
|
||||||
},
|
|
||||||
"whatDoesNonprofitDo": {
|
"whatDoesNonprofitDo": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"websiteLink": {
|
"websiteLink": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"stakeholderName": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"stakeholderEmail": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"endUser": {
|
"endUser": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -41,11 +32,8 @@
|
|||||||
"estimatedHours": {
|
"estimatedHours": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"interestedCampers": {
|
"moneySaved": {
|
||||||
"type": []
|
"type": "number"
|
||||||
},
|
|
||||||
"confirmedCampers": {
|
|
||||||
"type": []
|
|
||||||
},
|
},
|
||||||
"currentStatus": {
|
"currentStatus": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -63,7 +51,7 @@
|
|||||||
{
|
{
|
||||||
"accessType": "READ",
|
"accessType": "READ",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$everyone",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -69,7 +69,7 @@
|
|||||||
{
|
{
|
||||||
"accessType": "READ",
|
"accessType": "READ",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$everyone",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -134,7 +134,8 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"uncompletedBonfires": {
|
"uncompletedBonfires": {
|
||||||
"type": "array"
|
"type": "array",
|
||||||
|
"default": []
|
||||||
},
|
},
|
||||||
"completedBonfires": {
|
"completedBonfires": {
|
||||||
"type": [
|
"type": [
|
||||||
|
@ -13,10 +13,6 @@ module.exports = {
|
|||||||
key: process.env.BLOGGER_KEY
|
key: process.env.BLOGGER_KEY
|
||||||
},
|
},
|
||||||
|
|
||||||
slack: {
|
|
||||||
key: process.env.SLACK_KEY
|
|
||||||
},
|
|
||||||
|
|
||||||
mandrill: {
|
mandrill: {
|
||||||
user: process.env.MANDRILL_USER,
|
user: process.env.MANDRILL_USER,
|
||||||
password: process.env.MANDRILL_PASSWORD
|
password: process.env.MANDRILL_PASSWORD
|
||||||
|
@ -30,7 +30,7 @@ $(document).ready(function() {
|
|||||||
},
|
},
|
||||||
function(res) {
|
function(res) {
|
||||||
if (res) {
|
if (res) {
|
||||||
window.open('https://freecodecamp.slack.com/messages/help/', '_blank')
|
window.open('https://gitter.im/FreeCodeCamp/Help', '_blank')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -48,7 +48,7 @@ $(document).ready(function() {
|
|||||||
},
|
},
|
||||||
function(res) {
|
function(res) {
|
||||||
if (res) {
|
if (res) {
|
||||||
window.open('https://freecodecamp.slack.com/messages/help/', '_blank')
|
window.open('https://gitter.im/FreeCodeCamp/Help', '_blank')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -71,7 +71,7 @@ $(document).ready(function() {
|
|||||||
},
|
},
|
||||||
function(res) {
|
function(res) {
|
||||||
if (res) {
|
if (res) {
|
||||||
window.open('https://freecodecamp.slack.com/messages/letspair/', '_blank')
|
window.open('https://gitter.im/FreeCodeCamp/LetsPair', '_blank')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -150,10 +150,6 @@ $(document).ready(function() {
|
|||||||
$('#help-modal').modal('show');
|
$('#help-modal').modal('show');
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#trigger-help-editorless-modal').on('click', function() {
|
|
||||||
$('#help-editorless-modal').modal('show');
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#trigger-issue-modal').on('click', function() {
|
$('#trigger-issue-modal').on('click', function() {
|
||||||
$('#issue-modal').modal('show');
|
$('#issue-modal').modal('show');
|
||||||
});
|
});
|
||||||
@ -323,15 +319,13 @@ $(document).ready(function() {
|
|||||||
.fail(function (xhr, textStatus, errorThrown) {
|
.fail(function (xhr, textStatus, errorThrown) {
|
||||||
$('#story-submit').bind('click', storySubmitButtonHandler);
|
$('#story-submit').bind('click', storySubmitButtonHandler);
|
||||||
})
|
})
|
||||||
.done(function (data, textStatus, xhr) {
|
.done(function(data, textStatus, xhr) {
|
||||||
window.location = '/stories/' + JSON.parse(data).storyLink;
|
window.location = '/stories/' + data.storyLink;
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$('#story-submit').on('click', storySubmitButtonHandler);
|
$('#story-submit').on('click', storySubmitButtonHandler);
|
||||||
|
|
||||||
|
|
||||||
var commentSubmitButtonHandler = function commentSubmitButtonHandler() {
|
var commentSubmitButtonHandler = function commentSubmitButtonHandler() {
|
||||||
$('#comment-button').unbind('click');
|
$('#comment-button').unbind('click');
|
||||||
var data = $('#comment-box').val();
|
var data = $('#comment-box').val();
|
@ -81,7 +81,7 @@
|
|||||||
"sym([1, 2, 3], [5, 2, 1, 4]);"
|
"sym([1, 2, 3], [5, 2, 1, 4]);"
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"expect(sym([1, 2, 3], [5, 2, 1, 4])).to.eqls([3, 5, 4])",
|
"expect(sym([1, 2, 3], [5, 2, 1, 4])).to.equal([3, 5, 4]);",
|
||||||
"assert.deepEqual(sym([1, 2, 5], [2, 3, 5], [3, 4, 5]), [1, 4, 5], 'should return the symmetric difference of the given arrays');",
|
"assert.deepEqual(sym([1, 2, 5], [2, 3, 5], [3, 4, 5]), [1, 4, 5], 'should return the symmetric difference of the given arrays');",
|
||||||
"assert.deepEqual(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]), [1, 4, 5], 'should return an array of unique values');",
|
"assert.deepEqual(sym([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]), [1, 4, 5], 'should return an array of unique values');",
|
||||||
"assert.deepEqual(sym([1, 1]), [1], 'should return an array of unique values');"
|
"assert.deepEqual(sym([1, 1]), [1], 'should return an array of unique values');"
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
"Pair Programming is where two people code together on the same computer. It is an efficient way to collaborate, and widely practiced at software companies. Pair Programming is one of the core concepts of \"Agile\" Software Development, which you will hear more about later.",
|
"Pair Programming is where two people code together on the same computer. It is an efficient way to collaborate, and widely practiced at software companies. Pair Programming is one of the core concepts of \"Agile\" Software Development, which you will hear more about later.",
|
||||||
"Many people use Skype or Google Hangouts to pair program, but if you talk with professional software engineers, they will tell you that it's not really pair programming unless both people have the ability to use the keyboard and mouse.",
|
"Many people use Skype or Google Hangouts to pair program, but if you talk with professional software engineers, they will tell you that it's not really pair programming unless both people have the ability to use the keyboard and mouse.",
|
||||||
"The most popular tool for pair programming is Screen Hero. You can download Screen Hero for <a href='http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjowLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLnppcD9zb3VyY2U9d2ViIn0=' target='_blank'>Mac</a> or <a href='http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjoxLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLXNldHVwLmV4ZSJ9' target='_blank'>Windows</a>. Create your new user account from within the app.",
|
"The most popular tool for pair programming is Screen Hero. You can download Screen Hero for <a href='http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjowLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLnppcD9zb3VyY2U9d2ViIn0=' target='_blank'>Mac</a> or <a href='http://links.screenhero.com/e/c/eyJlbWFpbF9pZCI6Ik1qQTNNem9XQkNJQ1pBQUNjd0FYQVZrVEdnRkxNamtfX0JWZEdGVEpSZkVCWlRwbFpXRTBNamM0WVMxaE56SmlMVEV4WlRRdE9HUXpZUzFpWXpVNE1HRTJNalkxTldNNk1UUTJNVEEyQUE9PSIsInBvc2l0aW9uIjoxLCJocmVmIjoiaHR0cDovL2RsLnNjcmVlbmhlcm8uY29tL3NtYXJ0ZG93bmxvYWQvZklYQU1UUUJBTEtQQkhQTC9TY3JlZW5oZXJvLXNldHVwLmV4ZSJ9' target='_blank'>Windows</a>. Create your new user account from within the app.",
|
||||||
"We have a special chat room for people ready to pair program. Go to our <a href='http://freecodecamp.slack.com/messages/letspair'>http://freecodecamp.slack.com/messages/letspair</a> and type \"Hello Pair Programmers!\"",
|
"We have a special chat room for people ready to pair program. Go to our <a href='//gitter.im/FreeCodeCamp/LetsPair' target='_blank'>LetsPair chatroom on gitter</a> and type \"Hello Pair Programmers!\"",
|
||||||
"If someone is available, they will be your \"pair\" - the person you pair programming with.",
|
"If someone is available, they will be your \"pair\" - the person you pair programming with.",
|
||||||
"If no one gets back to you in the first few minutes, don't worry. There will be lots of opportunities to pair program in the future.",
|
"If no one gets back to you in the first few minutes, don't worry. There will be lots of opportunities to pair program in the future.",
|
||||||
"If someone does get back to you, private message them and ask for the email address they used to register Screen Hero.",
|
"If someone does get back to you, private message them and ask for the email address they used to register Screen Hero.",
|
||||||
|
@ -1678,7 +1678,7 @@
|
|||||||
"You can create one like this: <code><input type='text'></code>. Note that <code>input</code> elements are self-closing."
|
"You can create one like this: <code><input type='text'></code>. Note that <code>input</code> elements are self-closing."
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"assert($('input').length > 0, 'Your app should have an text field input element.')"
|
"assert($('input').length > 0, 'Your app should have a text field input element.')"
|
||||||
],
|
],
|
||||||
"challengeSeed": [
|
"challengeSeed": [
|
||||||
"<link href='http://fonts.googleapis.com/css?family=Lobster' rel='stylesheet' type='text/css'>",
|
"<link href='http://fonts.googleapis.com/css?family=Lobster' rel='stylesheet' type='text/css'>",
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"dashedName": "waypoint-mobile-responsive-images",
|
"dashedName": "waypoint-mobile-responsive-images",
|
||||||
"difficulty": 0.047,
|
"difficulty": 0.047,
|
||||||
"description": [
|
"description": [
|
||||||
"Now let's go back to our Cat Photo App. This time, we'll style it using the popular Twitter Bootstrap responsive CSS framework. First, add a new image with the <code>src</code> attribute of \"http://bit.ly/fcc-kittens2\", and add the \"img-responsive\" Bootstrap class to that image.",
|
"Now let's go back to our Cat Photo App. This time, we'll style it using the popular Bootstrap responsive CSS framework. First, add a new image with the <code>src</code> attribute of \"http://bit.ly/fcc-kittens2\", and add the \"img-responsive\" Bootstrap class to that image.",
|
||||||
"It would be great if the image could be exactly the width of our phone's screen.",
|
"It would be great if the image could be exactly the width of our phone's screen.",
|
||||||
"Fortunately, we have access to a Responsive CSS Framework called Bootstrap. You can add Bootstrap to any app just by including it with <code><link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css'/></code> at the top of your HTML. But we've gone ahead and automatically added it to your Cat Photo App for you.",
|
"Fortunately, we have access to a Responsive CSS Framework called Bootstrap. You can add Bootstrap to any app just by including it with <code><link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css'/></code> at the top of your HTML. But we've gone ahead and automatically added it to your Cat Photo App for you.",
|
||||||
"Bootstrap will figure out how wide your screen is and respond by resizing your HTML elements - hence the name <code>Responsive Design</code>.",
|
"Bootstrap will figure out how wide your screen is and respond by resizing your HTML elements - hence the name <code>Responsive Design</code>.",
|
||||||
|
@ -56,15 +56,18 @@
|
|||||||
"name": "Waypoint: Join Our Chat Room",
|
"name": "Waypoint: Join Our Chat Room",
|
||||||
"dashedName": "waypoint-join-our-chat-room",
|
"dashedName": "waypoint-join-our-chat-room",
|
||||||
"difficulty": 0.002,
|
"difficulty": 0.002,
|
||||||
"challengeSeed": ["131321596"],
|
"challengeSeed": ["131574135"],
|
||||||
"description": [
|
"description": [
|
||||||
"Now we're going to join the Free Code Camp chat room. You can come here any time of day to hang out, ask questions, or find another camper to pair program with.",
|
"Now we're going to join the Free Code Camp chat room. You can come here any time of day to hang out, ask questions, or find another camper to pair program with.",
|
||||||
"Create an account with GitHub here: <a href='https://github.com/join' target='_blank'>https://github.com/join</a>.",
|
"Create an account with GitHub here: <a href='https://github.com/join' target='_blank'>https://github.com/join</a>.",
|
||||||
"Click the pixel art in the upper right hand corner of GitHub, then choose settings. Upload a picture of yourself. A picture of your face works best. This is how people will see you in our chat rooms, so put your best foot forward. You can add your city and your personal website if you have one.",
|
"Click the pixel art in the upper right hand corner of GitHub, then choose settings. Upload a picture of yourself. A picture of your face works best. This is how people will see you in our chat rooms, so put your best foot forward. You can add your city and your personal website if you have one.",
|
||||||
"Now follow this link to enter our Welcome chat room: <a href='https://gitter.im/FreeCodeCamp/welcome' target='_blank'>https://gitter.im/FreeCodeCamp/welcome</a>.",
|
"Go to Free Code Camp's open-source repository: <a href='//github.com/freecodecamp/freecodecamp' target='_blank'>https://github.com/freecodecamp/freecodecamp</a>.",
|
||||||
"Once you're in our Welcome chat room, introduce yourself by saying : \"Hello world!\".",
|
"You can \"star\" this repository by clicking the star button in the upper right hand corner.",
|
||||||
|
"Later, you'll be able to fork this repository if you'd like to contribute to our open source codebase.",
|
||||||
|
"Join our main chat room: <a href='https://gitter.im/FreeCodeCamp/FreeCodeCamp' target='_blank'>https://gitter.im/FreeCodeCamp/FreeCodeCamp</a>.",
|
||||||
|
"Once you're in our chat room, introduce yourself by saying : \"Hello world!\".",
|
||||||
"Tell your fellow campers how you found Free Code Camp. Also tell us why you want to learn to code.",
|
"Tell your fellow campers how you found Free Code Camp. Also tell us why you want to learn to code.",
|
||||||
"This is the best room for new campers, but feel free to join other chat rooms as well. Our main chat room: <a href='https://gitter.im/FreeCodeCamp/FreeCodeCamp' target='_blank'>https://gitter.im/FreeCodeCamp/FreeCodeCamp</a>.",
|
"We have a busy chat room, so be sure to configure your notification settings in the top right corner.",
|
||||||
"Keep the chat room open while you work through the other challenges. That way you ask for help if you get stuck on a challenge. You can also socialize when you feel like taking a break.",
|
"Keep the chat room open while you work through the other challenges. That way you ask for help if you get stuck on a challenge. You can also socialize when you feel like taking a break.",
|
||||||
"You can also download a desktop or mobile chat application here: <a href='https://gitter.im/apps' target='_blank'>https://gitter.im/apps</a>",
|
"You can also download a desktop or mobile chat application here: <a href='https://gitter.im/apps' target='_blank'>https://gitter.im/apps</a>",
|
||||||
"You can also access this chat room by clicking the \"Chat\" button in the upper right hand corner.",
|
"You can also access this chat room by clicking the \"Chat\" button in the upper right hand corner.",
|
||||||
@ -297,7 +300,7 @@
|
|||||||
"challengeSeed": ["127358841"],
|
"challengeSeed": ["127358841"],
|
||||||
"description": [
|
"description": [
|
||||||
"One of the best ways to stay motivated when learning to code is to hang out with other campers.",
|
"One of the best ways to stay motivated when learning to code is to hang out with other campers.",
|
||||||
"Slack and Camper News are great ways to communicate with other campers, but there's no substitute for meeting people in-person.",
|
"Gitter and Camper News are great ways to communicate with other campers, but there's no substitute for meeting people in-person.",
|
||||||
"The easiest way to meet other campers in your city is to join your city's Facebook Group. Click <a href='/field-guide/how-can-i-find-other-free-code-camp-campers-in-my-city' target='_blank'>here</a> to view our growing list of local groups.",
|
"The easiest way to meet other campers in your city is to join your city's Facebook Group. Click <a href='/field-guide/how-can-i-find-other-free-code-camp-campers-in-my-city' target='_blank'>here</a> to view our growing list of local groups.",
|
||||||
"Click the link to your city, then, once Facebook loads, click \"Join group\".",
|
"Click the link to your city, then, once Facebook loads, click \"Join group\".",
|
||||||
"Our local groups are new, so if you don't see your city on this list, you should follow the directions to create a Facebook group for your city.",
|
"Our local groups are new, so if you don't see your city on this list, you should follow the directions to create a Facebook group for your city.",
|
||||||
@ -316,7 +319,7 @@
|
|||||||
"nameEs": "Waypoint: Encuentrate con otros Campers en tu Ciudad",
|
"nameEs": "Waypoint: Encuentrate con otros Campers en tu Ciudad",
|
||||||
"descriptionEs": [
|
"descriptionEs": [
|
||||||
"Una de las mejores maneras de mantenerte motivado cuando estás aprendiendo a programar es pasar el rato con otros campers.",
|
"Una de las mejores maneras de mantenerte motivado cuando estás aprendiendo a programar es pasar el rato con otros campers.",
|
||||||
"Slack y Noticias de Campers son una muy buena forma de comunicarte con otros campers, pero no hay ningún substituto para conocerlos en persona.",
|
"Gitter y Noticias de Campers son una muy buena forma de comunicarte con otros campers, pero no hay ningún substituto para conocerlos en persona.",
|
||||||
"La forma más fácil de encontrarte con otros campers en tu ciudad es unirte al grupo de Facebook de tu ciudad o país. Dale click a <a href='/field-guide/how-can-i-find-other-free-code-camp-campers-in-my-city' target='_blank'>here</a> para ver la lista de grupos locales.",
|
"La forma más fácil de encontrarte con otros campers en tu ciudad es unirte al grupo de Facebook de tu ciudad o país. Dale click a <a href='/field-guide/how-can-i-find-other-free-code-camp-campers-in-my-city' target='_blank'>here</a> para ver la lista de grupos locales.",
|
||||||
"Dale click al link de tu ciudad o país y una vez que Facebook cargue, dale click a \"Join group\".",
|
"Dale click al link de tu ciudad o país y una vez que Facebook cargue, dale click a \"Join group\".",
|
||||||
"Nuestros grupos locales son pocos, asi que en caso no veas tu ciudad o país en la lista, solamente sigue las instrucciones para crear un grupo de Facebook para ello.",
|
"Nuestros grupos locales son pocos, asi que en caso no veas tu ciudad o país en la lista, solamente sigue las instrucciones para crear un grupo de Facebook para ello.",
|
||||||
@ -339,17 +342,48 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"_id": "bd7126d8c431eddfaeb5bd3e",
|
||||||
|
"name": "Waypoint: Add Free Code Camp to your LinkedIn Profile",
|
||||||
|
"difficulty": 0.008,
|
||||||
|
"challengeSeed": ["131574134"],
|
||||||
|
"description": [
|
||||||
|
"LinkedIn is a critical tool for your job search later on.",
|
||||||
|
"Add Free Code Camp to your LinkedIn profile by going to <a href='https://www.linkedin.com/profile/edit-education?school=Free+Code+Camp' target='_blank'>https://www.linkedin.com/profile/edit-education?school=Free+Code+Camp</a>.",
|
||||||
|
"Estimate your dates. Keep in mind that Free Code Camp is a rigorous 1,600 hour program, and will probably take at least a year to complete.",
|
||||||
|
"In the \"Degree\" section, type \"Full Stack Web Development\".",
|
||||||
|
"In the \"Field of study\" section, type \"Computer Software Engineering\".",
|
||||||
|
"Click the \"Save Changes\" button.",
|
||||||
|
"Be sure to add your key word skills to LinkedIn's skills section as you learn them, such as HTML, jQuery, Linux and Node.js.",
|
||||||
|
"You can expand your LinkedIn network by inviting friends you meet through Free Code Camp to connect with you on LinkedIn.",
|
||||||
|
"Make your LinkedIn profile as complete as possible. Unlike other social networks, with LinkedIn, it's perfectly fine if you don't want to add a photo.",
|
||||||
|
"Let's keep moving. We're almost ready to start coding!"
|
||||||
|
],
|
||||||
|
"challengeType": 2,
|
||||||
|
"tests": [],
|
||||||
|
"nameCn": "",
|
||||||
|
"descriptionCn": [],
|
||||||
|
"nameFr": "",
|
||||||
|
"descriptionFr": [],
|
||||||
|
"nameRu": "",
|
||||||
|
"descriptionRu": [],
|
||||||
|
"nameEs": "",
|
||||||
|
"descriptionEs": [],
|
||||||
|
"namePt": "",
|
||||||
|
"descriptionPt": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "bd7137d8c441eddfaeb5bdef",
|
||||||
"id": "bd7137d8c441eddfaeb5bdef",
|
"id": "bd7137d8c441eddfaeb5bdef",
|
||||||
"name": "Waypoint: Get Help the Hacker Way with RSAP",
|
"name": "Waypoint: Get Help the Hacker Way with RSAP",
|
||||||
"dashedName": "waypoint-get-help-the-hacker-way-with-rsap",
|
"dashedName": "waypoint-get-help-the-hacker-way-with-rsap",
|
||||||
"difficulty": 0.008,
|
"difficulty": 0.009,
|
||||||
"challengeSeed": ["125407432"],
|
"challengeSeed": ["125407432"],
|
||||||
"description": [
|
"description": [
|
||||||
"Let's cover one last thing before you start working through our lessons: how to get help.",
|
"Let's cover one last thing before you start working through our lessons: how to get help.",
|
||||||
"Any time you get stuck or don't know what to do next, follow this simple algorithm (procedure): RSAP (Read, Search, Ask, Post).",
|
"Any time you get stuck or don't know what to do next, follow this simple algorithm (procedure): RSAP (Read, Search, Ask, Post).",
|
||||||
"First, R - Read the documentation or error message. A key skill that good coders have is the ability to interpret and then follow instructions.",
|
"First, R - Read the documentation or error message. A key skill that good coders have is the ability to interpret and then follow instructions.",
|
||||||
"Next, S - Search Google. Good Google queries take a lot of practice. When you search Google, you usually want to include the language or framework you're using. You also want to limit the results to a recent period.",
|
"Next, S - Search Google. Good Google queries take a lot of practice. When you search Google, you usually want to include the language or framework you're using. You also want to limit the results to a recent period.",
|
||||||
"Then, if you still haven't found an answer to your question, A - Ask your friends. If you have trouble, you can ask your fellow campers. We have a special chat room specifically for getting help with tools you learn through these Free Code Camp Challenges. Go to <a href='https://freecodecamp.slack.com/messages/help/' target='_blank'>https://freecodecamp.slack.com/messages/help/</a>. Keep this chat open while you work on the remaining challenges.",
|
"Then, if you still haven't found an answer to your question, A - Ask your friends. If you have trouble, you can ask your fellow campers. We have a special chat room specifically for getting help with tools you learn through these Free Code Camp Challenges. Go to <a href='https://gitter.im/FreeCodeCamp/Help' target='_blank'>https://gitter.im/FreeCodeCamp/Help</a>. Keep this chat open while you work on the remaining challenges.",
|
||||||
"Finally, P - Post on Stack Overflow. Before you attempt to do this, read Stack Overflow's guide to asking good questions: <a href='http://stackoverflow.com/help/how-to-ask'>http://stackoverflow.com/help/how-to-ask</a>.",
|
"Finally, P - Post on Stack Overflow. Before you attempt to do this, read Stack Overflow's guide to asking good questions: <a href='http://stackoverflow.com/help/how-to-ask'>http://stackoverflow.com/help/how-to-ask</a>.",
|
||||||
"Here's our detailed field guide on getting help: <a href='/field-guide/how-do-i-get-help-when-i-get-stuck' target='_blank'>http://freecodecamp.com/field-guide/how-do-i-get-help-when-i-get-stuck</a>.",
|
"Here's our detailed field guide on getting help: <a href='/field-guide/how-do-i-get-help-when-i-get-stuck' target='_blank'>http://freecodecamp.com/field-guide/how-do-i-get-help-when-i-get-stuck</a>.",
|
||||||
"Now you have a clear algorithm to follow when you need help! Let's start coding! Move on to your next challenge."
|
"Now you have a clear algorithm to follow when you need help! Let's start coding! Move on to your next challenge."
|
||||||
@ -368,7 +402,7 @@
|
|||||||
"Cualquier momento en el que te atasques o no sepas que hacer, sigue este simple algoritmo (procedimiento): RSAP (Read, Search, Ask, Post). Que en español vendría a ser Lee, Busca, Pregunta, Publica.",
|
"Cualquier momento en el que te atasques o no sepas que hacer, sigue este simple algoritmo (procedimiento): RSAP (Read, Search, Ask, Post). Que en español vendría a ser Lee, Busca, Pregunta, Publica.",
|
||||||
"Primero, Lee - Lee la documentación o el mensaje de error. El punto fuerte de un buen programador es la habilidad de interpretar y seguir instrucciones.",
|
"Primero, Lee - Lee la documentación o el mensaje de error. El punto fuerte de un buen programador es la habilidad de interpretar y seguir instrucciones.",
|
||||||
"Luego, Busca - Busca en Google. Buenas búsquedas o queries requieren bastante práctica. Cuando búsques en Google, idealmente tienes que incluir el lenguaje o framework que estés usando. También tendrás que limitar los resultados de búsqueda a un periodo reciente.",
|
"Luego, Busca - Busca en Google. Buenas búsquedas o queries requieren bastante práctica. Cuando búsques en Google, idealmente tienes que incluir el lenguaje o framework que estés usando. También tendrás que limitar los resultados de búsqueda a un periodo reciente.",
|
||||||
"Ahora, en caso no hayas encontrado la respuesta a tu pregunta, Pregunta - Pregunta a tus amigos. En caso estes en problemas, puedes preguntar a otros campers. Tenemos una sala de chat especificamente para obtener ayuda sobre las herramientas que utilizamos en los desafíos de Free Code Camp. Ingresa a <a href='https://freecodecamp.slack.com/messages/help/' target='_blank'>https://freecodecamp.slack.com/messages/help/</a>. Mantén este chat abierto mientras trabajas en los desafíos subsiguientes.",
|
"Ahora, en caso no hayas encontrado la respuesta a tu pregunta, Pregunta - Pregunta a tus amigos. En caso estes en problemas, puedes preguntar a otros campers. Tenemos una sala de chat especificamente para obtener ayuda sobre las herramientas que utilizamos en los desafíos de Free Code Camp. Ingresa a <a href='https://gitter.im/FreeCodeCamp/Help' target='_blank'>https://gitter.im/FreeCodeCamp/Help</a>. Mantén este chat abierto mientras trabajas en los desafíos subsiguientes.",
|
||||||
"Finalmente, Publica - Publica tu pregunta en Stack Overflow. Antes de hacer esto lee la guía de Stack Overflow para publicar buenas preguntas: <a href='http://stackoverflow.com/help/how-to-ask'>http://stackoverflow.com/help/how-to-ask</a>. Tendrás que hacerlo en inglés, en caso no sepas como, pide que te ayuden a traducir tu pregunta en el canal #espanol de Slack.",
|
"Finalmente, Publica - Publica tu pregunta en Stack Overflow. Antes de hacer esto lee la guía de Stack Overflow para publicar buenas preguntas: <a href='http://stackoverflow.com/help/how-to-ask'>http://stackoverflow.com/help/how-to-ask</a>. Tendrás que hacerlo en inglés, en caso no sepas como, pide que te ayuden a traducir tu pregunta en el canal #espanol de Slack.",
|
||||||
"Aquí está nuestra guia detallada en como obtener ayuda: <a href='/field-guide/how-do-i-get-help-when-i-get-stuck' target='_blank'>http://freecodecamp.com/field-guide/how-do-i-get-help-when-i-get-stuck</a>.",
|
"Aquí está nuestra guia detallada en como obtener ayuda: <a href='/field-guide/how-do-i-get-help-when-i-get-stuck' target='_blank'>http://freecodecamp.com/field-guide/how-do-i-get-help-when-i-get-stuck</a>.",
|
||||||
"Ahora que tienes en claro el procedimiento a seguir cuando necesites ayuda. ¡Empecémos a programar! Continua con el siguiente desafío."
|
"Ahora que tienes en claro el procedimiento a seguir cuando necesites ayuda. ¡Empecémos a programar! Continua con el siguiente desafío."
|
||||||
|
@ -225,7 +225,7 @@
|
|||||||
" </p>",
|
" </p>",
|
||||||
" <p class='large-p'>This is the most time-efficient way to handle being stuck, and it's the most respectful of other people's time, too.</p>",
|
" <p class='large-p'>This is the most time-efficient way to handle being stuck, and it's the most respectful of other people's time, too.</p>",
|
||||||
" <p class='large-p'>Most of the time, you'll solve your problem after just one or two steps of this algorithm.</p>",
|
" <p class='large-p'>Most of the time, you'll solve your problem after just one or two steps of this algorithm.</p>",
|
||||||
" <p class='large-p'>We have a special chat room just for getting help: <a href='https://freecodecamp.slack.com/messages/help/' target='_blank'>https://freecodecamp.slack.com/messages/help/</a></p>",
|
" <p class='large-p'>We have a special chat room just for getting help: <a href='//gitter.im/freecodecamp/help/' target='_blank'>https://gitter.im/freecodecamp/help/</a></p>",
|
||||||
" <p class='large-p'>Also, if you need to post on Stack Overflow, be sure to read their guide to asking good questions: <a href='http://stackoverflow.com/help/how-to-ask' target='_blank'>http://stackoverflow.com/help/how-to-ask</a>.</p>",
|
" <p class='large-p'>Also, if you need to post on Stack Overflow, be sure to read their guide to asking good questions: <a href='http://stackoverflow.com/help/how-to-ask' target='_blank'>http://stackoverflow.com/help/how-to-ask</a>.</p>",
|
||||||
" <p class='large-p'>Learning to code is hard. But it's a lot easier if you ask for help when you need it!</p>",
|
" <p class='large-p'>Learning to code is hard. But it's a lot easier if you ask for help when you need it!</p>",
|
||||||
"</div>"
|
"</div>"
|
||||||
@ -279,7 +279,7 @@
|
|||||||
" <p class='large-p'>",
|
" <p class='large-p'>",
|
||||||
" <ol>",
|
" <ol>",
|
||||||
" <li class='large-li'>Follow <a href='http://www.hdpvrcapture.com/wordpress/?p=5951' target='_blank'>this tutorial</a> to set up your computer for streaming.</li>",
|
" <li class='large-li'>Follow <a href='http://www.hdpvrcapture.com/wordpress/?p=5951' target='_blank'>this tutorial</a> to set up your computer for streaming.</li>",
|
||||||
" <li class='large-li'>Contact Jason Ruekert - he's @jsonify in Slack. He's in charge of our Twitch.tv channel. Tell him what you'd like to stream, and when you're available to stream.</li>",
|
" <li class='large-li'>Contact Jason Ruekert - he's @jsonify in Gitter. He's in charge of our Twitch.tv channel. Tell him what you'd like to stream, and when you're available to stream.</li>",
|
||||||
" <li class='large-li'>Jason will pair with you using Screen Hero to verify your computer is configured properly to stream.</li>",
|
" <li class='large-li'>Jason will pair with you using Screen Hero to verify your computer is configured properly to stream.</li>",
|
||||||
" </ol>",
|
" </ol>",
|
||||||
" </p>",
|
" </p>",
|
||||||
@ -492,7 +492,7 @@
|
|||||||
" <img class='img-responsive img-center' src='https://www.evernote.com/shard/s116/sh/e13808ea-f084-46f1-95bc-63db524383d5/0586484d44360e73e9acda956dad7f6a/deep/0/Free-Code-Camp-Testland.png' alt='A screen shot showing you the group description box on the Facebook page.'>",
|
" <img class='img-responsive img-center' src='https://www.evernote.com/shard/s116/sh/e13808ea-f084-46f1-95bc-63db524383d5/0586484d44360e73e9acda956dad7f6a/deep/0/Free-Code-Camp-Testland.png' alt='A screen shot showing you the group description box on the Facebook page.'>",
|
||||||
" <li class='large-li'>Click the \"Upload a photo button. To start out, you'll probably just want to use Free Code Camp's banner (download it <a href='https://s3.amazonaws.com/freecodecamp/wide-social-banner.png'>here</a>), or a scenic shot of your city. Later you can update this with a picture from one of your city's Free Code Camp events.</li>",
|
" <li class='large-li'>Click the \"Upload a photo button. To start out, you'll probably just want to use Free Code Camp's banner (download it <a href='https://s3.amazonaws.com/freecodecamp/wide-social-banner.png'>here</a>), or a scenic shot of your city. Later you can update this with a picture from one of your city's Free Code Camp events.</li>",
|
||||||
" <img class='img-responsive img-center' src='https://www.evernote.com/shard/s116/sh/fabe226a-5a26-4508-b015-1dac35192a21/37f8119dc57845a31e972217714b038b/deep/0/Free-Code-Camp-Testland.png' alt='A screenshot showing the \"Upload a photo\" button.'>",
|
" <img class='img-responsive img-center' src='https://www.evernote.com/shard/s116/sh/fabe226a-5a26-4508-b015-1dac35192a21/37f8119dc57845a31e972217714b038b/deep/0/Free-Code-Camp-Testland.png' alt='A screenshot showing the \"Upload a photo\" button.'>",
|
||||||
" <li class='large-li'>Message @quincylarson in Slack with a link to your city's group page and he'll include it here.</li>",
|
" <li class='large-li'>Message @quincylarson on Gitter with a link to your city's group page and he'll include it here.</li>",
|
||||||
" <li class='large-li'>Join our <a href='https://www.facebook.com/groups/freecodecampers/'>Local Leaders Facebook group</a>, where we share ideas about involving campers in your city.</li>",
|
" <li class='large-li'>Join our <a href='https://www.facebook.com/groups/freecodecampers/'>Local Leaders Facebook group</a>, where we share ideas about involving campers in your city.</li>",
|
||||||
" </ol>",
|
" </ol>",
|
||||||
" </p>",
|
" </p>",
|
||||||
@ -625,8 +625,8 @@
|
|||||||
" </ul>",
|
" </ul>",
|
||||||
" </p>",
|
" </p>",
|
||||||
" <p class='large-p'>It's notoriously difficult to estimate how long building software projects will take, so feel free to ask our volunteer team for help.</p>",
|
" <p class='large-p'>It's notoriously difficult to estimate how long building software projects will take, so feel free to ask our volunteer team for help.</p>",
|
||||||
" <p class='large-p'>You'll continue to meet with your stakeholder at least twice a month in your project's Slack channel.</p>",
|
" <p class='large-p'>You'll continue to meet with your stakeholder at least twice a month in your project's Gitter or Slack channel.</p>",
|
||||||
" <p class='large-p'>You should also ask questions in your project's Slack channel as they come up throughout the week, and your stakeholder can answer them asynchronously.</p>",
|
" <p class='large-p'>You should also ask questions in your project's Gitter or Slack channel as they come up throughout the week, and your stakeholder can answer them asynchronously.</p>",
|
||||||
" <p class='large-p'>Getting \"blocked\" on a task can take away your sense of forward momentum, so be sure to proactively seek answers to any ambiguities you encounter.</p>",
|
" <p class='large-p'>Getting \"blocked\" on a task can take away your sense of forward momentum, so be sure to proactively seek answers to any ambiguities you encounter.</p>",
|
||||||
" <p class='large-p'>Ultimately, the project will be considered complete once both the stakeholder's needs have been met, and you and your pair are happy with the project. Then you can add it to your portfolio!</p>",
|
" <p class='large-p'>Ultimately, the project will be considered complete once both the stakeholder's needs have been met, and you and your pair are happy with the project. Then you can add it to your portfolio!</p>",
|
||||||
" <h3>Working with your Pair</h3>",
|
" <h3>Working with your Pair</h3>",
|
||||||
@ -634,7 +634,7 @@
|
|||||||
" <p class='large-p'>Here are our recommended ways of collaborating:</p>",
|
" <p class='large-p'>Here are our recommended ways of collaborating:</p>",
|
||||||
" <p class='large-p'>",
|
" <p class='large-p'>",
|
||||||
" <ul>",
|
" <ul>",
|
||||||
" <li class='large-li'>• Slack has robust private messaging functionality. It's the main way our team communicates, and we recommend it over email.</li>",
|
" <li class='large-li'>• Gitter has robust private messaging functionality. It's the main way our team communicates, and we recommend it over email.</li>",
|
||||||
" <li class='large-li'>• Trello is great for managing projects. Work with your stakeholder to create Trello cards, and update these cards regularly as you make progress on them.</li>",
|
" <li class='large-li'>• Trello is great for managing projects. Work with your stakeholder to create Trello cards, and update these cards regularly as you make progress on them.</li>",
|
||||||
" <li class='large-li'>• Screen Hero or Team Viewer - These are the ideal way to pair program. Tools like TMUX are good, but difficult to use. We discourage you from using screen sharing tools where only one person has control of the keyboard and mouse - that isn't real pair programming.</li>",
|
" <li class='large-li'>• Screen Hero or Team Viewer - These are the ideal way to pair program. Tools like TMUX are good, but difficult to use. We discourage you from using screen sharing tools where only one person has control of the keyboard and mouse - that isn't real pair programming.</li>",
|
||||||
" <li class='large-li'>• Write clear and readable code, commit messages, branch names, and pull request messages.</li>",
|
" <li class='large-li'>• Write clear and readable code, commit messages, branch names, and pull request messages.</li>",
|
||||||
@ -719,9 +719,9 @@
|
|||||||
" </ol>",
|
" </ol>",
|
||||||
" </p>",
|
" </p>",
|
||||||
" <p class='large-p'>Free Code Camp should be a harassment-free experience for everyone, regardless of gender, gender identity and expression, age, sexual orientation, disability, physical appearance, body size, race, national origin, or religion (or lack thereof).</p>",
|
" <p class='large-p'>Free Code Camp should be a harassment-free experience for everyone, regardless of gender, gender identity and expression, age, sexual orientation, disability, physical appearance, body size, race, national origin, or religion (or lack thereof).</p>",
|
||||||
" <p class='large-p'>We do not tolerate harassment of campers in any form, anywhere on Free Code Camp's online media (Slack, Twitch, etc.) or during pair programming. Harassment includes sexual language and imagery, deliberate intimidation, stalking, unwelcome sexual attention, libel, and any malicious hacking or social engineering.</p>",
|
" <p class='large-p'>We do not tolerate harassment of campers in any form, anywhere on Free Code Camp's online media (Gitter, Twitch, etc.) or during pair programming. Harassment includes sexual language and imagery, deliberate intimidation, stalking, unwelcome sexual attention, libel, and any malicious hacking or social engineering.</p>",
|
||||||
" <p class='large-p'>If a camper engages in harassing behavior, our team will take any action we deem appropriate, up to and including banning them from Free Code Camp.</p>",
|
" <p class='large-p'>If a camper engages in harassing behavior, our team will take any action we deem appropriate, up to and including banning them from Free Code Camp.</p>",
|
||||||
" <p class='large-p'>We want everyone to feel safe and respected. If you are being harassed or notice that someone else is being harassed, say something! Message @quincylarson, @terakilobyte and @codenonprofit in Slack (preferably with a screen shot of the offending language) so we can take fast action.</p>",
|
" <p class='large-p'>We want everyone to feel safe and respected. If you are being harassed or notice that someone else is being harassed, say something! Message @quincylarson, @terakilobyte and @codenonprofit on Gitter (preferably with a screen shot of the offending language) so we can take fast action.</p>",
|
||||||
" <p class='large-p'>If you have questions about this code of conduct, email us at <a href='mailto:team@freecodecamp.com'>team@freecodecamp.com</a>.</p>",
|
" <p class='large-p'>If you have questions about this code of conduct, email us at <a href='mailto:team@freecodecamp.com'>team@freecodecamp.com</a>.</p>",
|
||||||
"</div>"
|
"</div>"
|
||||||
]
|
]
|
||||||
@ -777,10 +777,10 @@
|
|||||||
" <h3>We're happy to do a quick interview for your publication or show. Here's whom you should contact about what, and how to best reach them:</h3>",
|
" <h3>We're happy to do a quick interview for your publication or show. Here's whom you should contact about what, and how to best reach them:</h3>",
|
||||||
" <p class='large-p'>",
|
" <p class='large-p'>",
|
||||||
" <ol>",
|
" <ol>",
|
||||||
" <li class='large-li'>Want to talk to about Free Code Camp's curriculum or long-term vision? Reach out to Quincy Larson. He's <a href='https://twitter.com/ossia' target='_blank'>@ossia</a> on Twitter and @quincylarson on Slack.</li>",
|
" <li class='large-li'>Want to talk to about Free Code Camp's curriculum or long-term vision? Reach out to Quincy Larson. He's <a href='https://twitter.com/ossia' target='_blank'>@ossia</a> on Twitter and @quincylarson on Gitter.</li>",
|
||||||
" <li class='large-li'>Want to talk about Free Code Camp's open source codebase, infrastructure, or JavaScript in general? Talk to Nathan Leniz. He's <a href='https://twitter.com/terakilobyte' target='_blank'>@terakilobyte</a> on Twitter and @terakilobyte on Slack.</li>",
|
" <li class='large-li'>Want to talk about Free Code Camp's open source codebase, infrastructure, or JavaScript in general? Talk to Nathan Leniz. He's <a href='https://twitter.com/terakilobyte' target='_blank'>@terakilobyte</a> on Twitter and @terakilobyte on Gitter.</li>",
|
||||||
" <li class='large-li'>Want to explore our efforts to empower nonprofits with code? Michael D. Johnson eats, sleeps and breathes that. He's <a href='https://twitter.com/figitalboy' target='_blank'>@figitalboy</a> on Twitter and @codenonprofit on Slack.</li>",
|
" <li class='large-li'>Want to explore our efforts to empower nonprofits with code? Michael D. Johnson eats, sleeps and breathes that. He's <a href='https://twitter.com/figitalboy' target='_blank'>@figitalboy</a> on Twitter and @codenonprofit on Gitter.</li>",
|
||||||
" <li class='large-li'>Want to get a camper's perspective on our community? Talk with Bianca Mihai (@biancamihai on Slack and <a href='https://twitter.com/bubuslubu' target='_blank'>@bubuslubu</a> on Twitter) or Suzanne Atkinson (@adventurebear on Slack and <a href='https://twitter.com/SteelCityCoach' target='_blank'>@steelcitycoach</a> on Twitter).",
|
" <li class='large-li'>Want to get a camper's perspective on our community? Talk with Bianca Mihai (@biancamihai on Gitter and <a href='https://twitter.com/bubuslubu' target='_blank'>@bubuslubu</a> on Twitter) or Suzanne Atkinson (@adventurebear on Gitter and <a href='https://twitter.com/SteelCityCoach' target='_blank'>@steelcitycoach</a> on Twitter).",
|
||||||
" </ol>",
|
" </ol>",
|
||||||
" </p>",
|
" </p>",
|
||||||
" <p class='large-p'>We strive to be helpful and transparent in everything we do. We'll do what we can to help you share our community with your audience.</p>",
|
" <p class='large-p'>We strive to be helpful and transparent in everything we do. We'll do what we can to help you share our community with your audience.</p>",
|
||||||
@ -838,7 +838,7 @@
|
|||||||
" <p class='large-p'>Contributing to our field guide is a great way to establish your history on GitHub, add to your portfolio, and help other campers. If you have a question about JavaScript or programming in general that you'd like us to add to the field guide, here are two ways to get it into the guide:</p>",
|
" <p class='large-p'>Contributing to our field guide is a great way to establish your history on GitHub, add to your portfolio, and help other campers. If you have a question about JavaScript or programming in general that you'd like us to add to the field guide, here are two ways to get it into the guide:</p>",
|
||||||
" <p class='large-p'>",
|
" <p class='large-p'>",
|
||||||
" <ol>",
|
" <ol>",
|
||||||
" <li class='large-li'>You can message @danraley in Slack with your question.</li>",
|
" <li class='large-li'>You can message @danraley on Gitter with your question.</li>",
|
||||||
" <li class='large-li'>You can also contribute to this field guide directly via GitHub pull request, by cloning Free Code Camp's <a href='https://github.com/FreeCodeCamp/freecodecamp'>main repository</a> and modifying <a href='https://github.com/FreeCodeCamp/freecodecamp/blob/master/seed_data/field-guides.json'>field-guides.json</a>.</li>",
|
" <li class='large-li'>You can also contribute to this field guide directly via GitHub pull request, by cloning Free Code Camp's <a href='https://github.com/FreeCodeCamp/freecodecamp'>main repository</a> and modifying <a href='https://github.com/FreeCodeCamp/freecodecamp/blob/master/seed_data/field-guides.json'>field-guides.json</a>.</li>",
|
||||||
" </ol>",
|
" </ol>",
|
||||||
" </p>",
|
" </p>",
|
||||||
@ -852,7 +852,7 @@
|
|||||||
"description": [
|
"description": [
|
||||||
"<div class=\"col-xs-12 col-sm-10 col-sm-offset-1\">",
|
"<div class=\"col-xs-12 col-sm-10 col-sm-offset-1\">",
|
||||||
" <p class='h2'>Our translation effort is driven by bilingual campers like you.</h2>",
|
" <p class='h2'>Our translation effort is driven by bilingual campers like you.</h2>",
|
||||||
" <p class='large-p'>If you're able to help us, you can join our <a href='https://trello.com/b/m7zhwXka/fcc-translation' target='_blank'>Trello board</a> by sending @quincylarson your email address in Slack.</p>",
|
" <p class='large-p'>If you're able to help us, you can join our <a href='https://trello.com/b/m7zhwXka/fcc-translation' target='_blank'>Trello board</a> by sending @quincylarson your email address on Gitter.</p>",
|
||||||
"</div>"
|
"</div>"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -865,7 +865,7 @@
|
|||||||
" <p class='h2'>Translation is an all-or-nothing proposal.</h2>",
|
" <p class='h2'>Translation is an all-or-nothing proposal.</h2>",
|
||||||
" <p class='large-p'>We won't be able to add new languages to Free Code Camp until all of our challenges are translated into that language.</p>",
|
" <p class='large-p'>We won't be able to add new languages to Free Code Camp until all of our challenges are translated into that language.</p>",
|
||||||
" <p class='large-p'>In addition to translating these initially, we'll also need to maintain the translation as the challenges are gradually updated.</p>",
|
" <p class='large-p'>In addition to translating these initially, we'll also need to maintain the translation as the challenges are gradually updated.</p>",
|
||||||
" <p class='large-p'>If you're able to help us, you can join our <a href='https://trello.com/b/m7zhwXka/fcc-translation' target='_blank'>Trello board</a> by sending @quincylarson your email address in Slack.</p>",
|
" <p class='large-p'>If you're able to help us, you can join our <a href='https://trello.com/b/m7zhwXka/fcc-translation' target='_blank'>Trello board</a> by sending @quincylarson your email address on Gitter.</p>",
|
||||||
"</div>"
|
"</div>"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -7,6 +7,7 @@ var Rx = require('rx'),
|
|||||||
secrets = require('../config/secrets');
|
secrets = require('../config/secrets');
|
||||||
|
|
||||||
var MongoClient = mongodb.MongoClient;
|
var MongoClient = mongodb.MongoClient;
|
||||||
|
Rx.config.longStackSupport = true;
|
||||||
|
|
||||||
var providers = [
|
var providers = [
|
||||||
'facebook',
|
'facebook',
|
||||||
@ -160,15 +161,33 @@ var storyCount = dbObservable
|
|||||||
})
|
})
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
|
var commentCount = dbObservable
|
||||||
|
.flatMap(function(db) {
|
||||||
|
return createQuery(db, 'comments', {});
|
||||||
|
})
|
||||||
|
.bufferWithCount(20)
|
||||||
|
.withLatestFrom(dbObservable, function(comments, db) {
|
||||||
|
return {
|
||||||
|
comments: comments,
|
||||||
|
db: db
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.flatMap(function(dats) {
|
||||||
|
return insertMany(dats.db, 'comment', dats.comments, { w: 1 });
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
|
||||||
Rx.Observable.combineLatest(
|
Rx.Observable.combineLatest(
|
||||||
userIdentityCount,
|
userIdentityCount,
|
||||||
userSavesCount,
|
userSavesCount,
|
||||||
storyCount,
|
storyCount,
|
||||||
function(userIdentCount, userCount, storyCount) {
|
commentCount,
|
||||||
|
function(userIdentCount, userCount, storyCount, commentCount) {
|
||||||
return {
|
return {
|
||||||
userIdentCount: userIdentCount * 20,
|
userIdentCount: userIdentCount * 20,
|
||||||
userCount: userCount * 20,
|
userCount: userCount * 20,
|
||||||
storyCount: storyCount * 20
|
storyCount: storyCount * 20,
|
||||||
|
commentCount: commentCount * 20
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.subscribe(
|
.subscribe(
|
||||||
@ -180,8 +199,7 @@ Rx.Observable.combineLatest(
|
|||||||
console.error('an error occured', err, err.stack);
|
console.error('an error occured', err, err.stack);
|
||||||
},
|
},
|
||||||
function() {
|
function() {
|
||||||
|
|
||||||
console.log('finished with ', count);
|
console.log('finished with ', count);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id": "bd7157d8c441cbafaeb5bdef",
|
"id": "bd7157d8c441cbafaeb5bdef",
|
||||||
"requestedDeliverables": [
|
|
||||||
"website",
|
|
||||||
"donor",
|
|
||||||
"inventory",
|
|
||||||
"volunteer",
|
|
||||||
"form"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We help the many less-fortunate Jewish families in our community, by providing them with nutritious food and energy to grow, learn, work, and give them hope for a better and brighter future.",
|
"whatDoesNonprofitDo": "We help the many less-fortunate Jewish families in our community, by providing them with nutritious food and energy to grow, learn, work, and give them hope for a better and brighter future.",
|
||||||
"websiteLink": "http://chasdeikaduri.org/",
|
"websiteLink": "http://chasdeikaduri.org/",
|
||||||
"name": "Chasdei Kaduri",
|
"name": "Chasdei Kaduri",
|
||||||
@ -21,17 +14,13 @@
|
|||||||
],
|
],
|
||||||
"projectDescription": "Campers will create a system will integrate the food inventory, donor and delivery driver management systems as well as replace the current application system with a custom form solution. System will include a more streamlined operations management, with user printable lists of inventory, drivers, and deliveries.",
|
"projectDescription": "Campers will create a system will integrate the food inventory, donor and delivery driver management systems as well as replace the current application system with a custom form solution. System will include a more streamlined operations management, with user printable lists of inventory, drivers, and deliveries.",
|
||||||
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54c7e02f2c173c37015b2f36/604x309/00580a0567a4b3afda29d52b09e7e829/rQQ6zwq31Uya8ie9QHC-MlvfXxqftm9UPPe524JUhmwSEaZjQ7oL7U1tVoHLUj-gVUwM-7uzBGFsAXD_A_cx_JyAZP4Td-GMBJ-AebJNRAQP0m0v253eKMkURp63aG4%3Ds0-d-e1-ft.png",
|
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54c7e02f2c173c37015b2f36/604x309/00580a0567a4b3afda29d52b09e7e829/rQQ6zwq31Uya8ie9QHC-MlvfXxqftm9UPPe524JUhmwSEaZjQ7oL7U1tVoHLUj-gVUwM-7uzBGFsAXD_A_cx_JyAZP4Td-GMBJ-AebJNRAQP0m0v253eKMkURp63aG4%3Ds0-d-e1-ft.png",
|
||||||
"imageUrl": "http://chasdeikaduri.org/images/523455_516325865106850_1885515210_n.jpg",
|
"imageUrl": "https://pbs.twimg.com/media/B3d6B8PIYAAa6QL.jpg",
|
||||||
"estimatedHours": 200,
|
"estimatedHours": 300,
|
||||||
"interestedCampers": [],
|
"currentStatus": "completed",
|
||||||
"confirmedCampers": [],
|
"moneySaved": 60000
|
||||||
"currentStatus": "completed"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bd7158d8c464cbafaeb4bdef",
|
"id": "bd7158d8c464cbafaeb4bdef",
|
||||||
"requestedDeliverables": [
|
|
||||||
"other"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We connect simple technology with last mile communities to reduce poverty.",
|
"whatDoesNonprofitDo": "We connect simple technology with last mile communities to reduce poverty.",
|
||||||
"websiteLink": "http://kopernik.info/",
|
"websiteLink": "http://kopernik.info/",
|
||||||
"name": "Kopernik",
|
"name": "Kopernik",
|
||||||
@ -43,13 +32,11 @@
|
|||||||
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54d29f1e4c726fd765fa87ef/54d29f6388812dd367a243ab/x/018d9d3be5439870c56cccba5b3aa8bf/kopernik-logo-global.png",
|
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54d29f1e4c726fd765fa87ef/54d29f6388812dd367a243ab/x/018d9d3be5439870c56cccba5b3aa8bf/kopernik-logo-global.png",
|
||||||
"imageUrl": "http://kopernik.info/sites/default/files/updates/Presenting_the_low_carbon_t.jpg",
|
"imageUrl": "http://kopernik.info/sites/default/files/updates/Presenting_the_low_carbon_t.jpg",
|
||||||
"estimatedHours": 100,
|
"estimatedHours": 100,
|
||||||
"currentStatus": "completed"
|
"currentStatus": "completed",
|
||||||
|
"moneySaved": 20000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bd1326d9c245cbafaeb4bdef",
|
"id": "bd1326d9c245cbafaeb4bdef",
|
||||||
"requestedDeliverables": [
|
|
||||||
"website"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We distribute biodegradable toothbrushes globally to children in need.",
|
"whatDoesNonprofitDo": "We distribute biodegradable toothbrushes globally to children in need.",
|
||||||
"websiteLink": "http://www.operationbrush.org/",
|
"websiteLink": "http://www.operationbrush.org/",
|
||||||
"name": "Operation Brush",
|
"name": "Operation Brush",
|
||||||
@ -61,15 +48,11 @@
|
|||||||
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54d9810307b159a4d9027aa2/54d981bfe5eb145560fbb769/x/cf7f318bfe4aee631b0d0eeef272225c/logo.png",
|
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54d9810307b159a4d9027aa2/54d981bfe5eb145560fbb769/x/cf7f318bfe4aee631b0d0eeef272225c/logo.png",
|
||||||
"imageUrl": "http://www.operationbrush.org/images/temp/hands1.png",
|
"imageUrl": "http://www.operationbrush.org/images/temp/hands1.png",
|
||||||
"estimatedHours": 100,
|
"estimatedHours": 100,
|
||||||
"interestedCampers": [],
|
"currentStatus": "completed",
|
||||||
"confirmedCampers": [],
|
"moneySaved": 20000
|
||||||
"currentStatus": "completed"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bd1325d8c464cbafaeb5bdef",
|
"id": "bd1325d8c464cbafaeb5bdef",
|
||||||
"requestedDeliverables": [
|
|
||||||
"community"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We are the largest roller derby league in the world with around 250 adults and 150 junior skater members plus 500+ volunteers.",
|
"whatDoesNonprofitDo": "We are the largest roller derby league in the world with around 250 adults and 150 junior skater members plus 500+ volunteers.",
|
||||||
"websiteLink": "http://www.rosecityrollers.com/about/our-charities/",
|
"websiteLink": "http://www.rosecityrollers.com/about/our-charities/",
|
||||||
"name": "Rose City Rollers",
|
"name": "Rose City Rollers",
|
||||||
@ -81,13 +64,11 @@
|
|||||||
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54c1daf2d72d8eb868910b60/54c1dd4ecffcb09fc52b68a1/x/a8148f08769b449217e433bab8f39ddd/RCR-color.jpg",
|
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54c1daf2d72d8eb868910b60/54c1dd4ecffcb09fc52b68a1/x/a8148f08769b449217e433bab8f39ddd/RCR-color.jpg",
|
||||||
"imageUrl": "http://www.rosecityrollers.com/wp-content/uploads/2015/01/BZ7_5923-X3-675x375.jpg",
|
"imageUrl": "http://www.rosecityrollers.com/wp-content/uploads/2015/01/BZ7_5923-X3-675x375.jpg",
|
||||||
"estimatedHours": 200,
|
"estimatedHours": 200,
|
||||||
"currentStatus": "started"
|
"currentStatus": "started",
|
||||||
|
"moneySaved": 40000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bd1325d8c464cbafaeb6bde1",
|
"id": "bd1325d8c464cbafaeb6bde1",
|
||||||
"requestedDeliverables": [
|
|
||||||
"website"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We provide urgently needed pediatric heart surgery and follow-up care for indigent children from developing countries",
|
"whatDoesNonprofitDo": "We provide urgently needed pediatric heart surgery and follow-up care for indigent children from developing countries",
|
||||||
"websiteLink": "http://www.saveachildsheart.com/global/young-leadership-program/",
|
"websiteLink": "http://www.saveachildsheart.com/global/young-leadership-program/",
|
||||||
"name": "Save a Child's Heart",
|
"name": "Save a Child's Heart",
|
||||||
@ -98,14 +79,12 @@
|
|||||||
"projectDescription": "Campers will create a single page fundraising website. In exchange for a donation, a user can customize a graphical 'heart' in someone's name or anonymously. The page will display all of the hearts on a 'wall of hearts.'",
|
"projectDescription": "Campers will create a single page fundraising website. In exchange for a donation, a user can customize a graphical 'heart' in someone's name or anonymously. The page will display all of the hearts on a 'wall of hearts.'",
|
||||||
"logoUrl": "https://trello-attachments.s3.amazonaws.com/548b36629137780091a973cc/666x666/6c7a366ffb659649f6377d4a431687cd/country-logos-1-300dpi.jpg",
|
"logoUrl": "https://trello-attachments.s3.amazonaws.com/548b36629137780091a973cc/666x666/6c7a366ffb659649f6377d4a431687cd/country-logos-1-300dpi.jpg",
|
||||||
"imageUrl": "http://www.saveachildsheart.com/wp-content/uploads/2013/10/7.2.5_Internation_Photograohy_Exhibition.jpg",
|
"imageUrl": "http://www.saveachildsheart.com/wp-content/uploads/2013/10/7.2.5_Internation_Photograohy_Exhibition.jpg",
|
||||||
"estimatedHours": 100,
|
"estimatedHours": 200,
|
||||||
"currentStatus": "completed"
|
"currentStatus": "completed",
|
||||||
|
"moneySaved": 40000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bd1325d8c464cbafaeb4bdef",
|
"id": "bd1325d8c464cbafaeb4bdef",
|
||||||
"requestedDeliverables": [
|
|
||||||
"website"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We empower youth with technology by providing age appropriate resources and education.",
|
"whatDoesNonprofitDo": "We empower youth with technology by providing age appropriate resources and education.",
|
||||||
"websiteLink": "http://savvycyberkids.org/",
|
"websiteLink": "http://savvycyberkids.org/",
|
||||||
"name": "Savvy Cyber Kids",
|
"name": "Savvy Cyber Kids",
|
||||||
@ -117,13 +96,11 @@
|
|||||||
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54ee3c7bf205562680177b59/218x190/1dc460de4edc9fdd4b481b24e93cfb23/logo.png",
|
"logoUrl": "https://trello-attachments.s3.amazonaws.com/54ee3c7bf205562680177b59/218x190/1dc460de4edc9fdd4b481b24e93cfb23/logo.png",
|
||||||
"imageUrl": "http://www.privatewifi.com/wp-content/uploads/2014/10/Halpert.jpg",
|
"imageUrl": "http://www.privatewifi.com/wp-content/uploads/2014/10/Halpert.jpg",
|
||||||
"estimatedHours": 200,
|
"estimatedHours": 200,
|
||||||
"currentStatus": "started"
|
"currentStatus": "started",
|
||||||
|
"moneySaved": 40000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bd1325d8c464cbafaeb7bcef",
|
"id": "bd1325d8c464cbafaeb7bcef",
|
||||||
"requestedDeliverables": [
|
|
||||||
"other"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We bring a new edge to arts and medicine in the Bay Area through powerful live performances of new music to those who feel marginalized by their affliction.",
|
"whatDoesNonprofitDo": "We bring a new edge to arts and medicine in the Bay Area through powerful live performances of new music to those who feel marginalized by their affliction.",
|
||||||
"websiteLink": "http://transcendentpathways.org/",
|
"websiteLink": "http://transcendentpathways.org/",
|
||||||
"name": "Transcendent Pathways",
|
"name": "Transcendent Pathways",
|
||||||
@ -135,13 +112,11 @@
|
|||||||
"logoUrl": "http://static1.squarespace.com/static/521b8957e4b024f66a58b214/t/521b8e9de4b093a8696eb9b8/1398718364447/?format=750w",
|
"logoUrl": "http://static1.squarespace.com/static/521b8957e4b024f66a58b214/t/521b8e9de4b093a8696eb9b8/1398718364447/?format=750w",
|
||||||
"imageUrl": "https://trello-attachments.s3.amazonaws.com/54fdb0328917ca64e9e8a79f/54fdc3b710f67caf6da14719/x/49fbe0012179bf254928f3f2a44810b4/Screen_2BShot_2B2013-08-26_2Bat_2B1.32.35_2BPM.png",
|
"imageUrl": "https://trello-attachments.s3.amazonaws.com/54fdb0328917ca64e9e8a79f/54fdc3b710f67caf6da14719/x/49fbe0012179bf254928f3f2a44810b4/Screen_2BShot_2B2013-08-26_2Bat_2B1.32.35_2BPM.png",
|
||||||
"estimatedHours": 200,
|
"estimatedHours": 200,
|
||||||
"currentStatus": "started"
|
"currentStatus": "started",
|
||||||
|
"moneySaved": 40000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bd1325d8c464cbafaeb8bdef",
|
"id": "bd1325d8c464cbafaeb8bdef",
|
||||||
"requestedDeliverables": [
|
|
||||||
"other"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We have provide volunteer matching fairs and silent art auctions at events across Canada. Rather than bid money on artwork, participants bid volunteer hours.",
|
"whatDoesNonprofitDo": "We have provide volunteer matching fairs and silent art auctions at events across Canada. Rather than bid money on artwork, participants bid volunteer hours.",
|
||||||
"websiteLink": "http://www.timeraiser.ca/",
|
"websiteLink": "http://www.timeraiser.ca/",
|
||||||
"name": "Timeraiser",
|
"name": "Timeraiser",
|
||||||
@ -152,19 +127,12 @@
|
|||||||
"projectDescription": "Campers will build a mobile responsive web form to allow Timeraiser eventgoers to select which nonprofit organizations they're interested in volunteering with. System will have Salesforce integration and reporting capabilities.",
|
"projectDescription": "Campers will build a mobile responsive web form to allow Timeraiser eventgoers to select which nonprofit organizations they're interested in volunteering with. System will have Salesforce integration and reporting capabilities.",
|
||||||
"logoUrl": "http://www.timeraiser.ca/uploads/5/6/1/4/5614163/1277176.png?480",
|
"logoUrl": "http://www.timeraiser.ca/uploads/5/6/1/4/5614163/1277176.png?480",
|
||||||
"imageUrl": "http://www.timeraiser.ca/uploads/5/6/1/4/5614163/______________4571248_orig.png",
|
"imageUrl": "http://www.timeraiser.ca/uploads/5/6/1/4/5614163/______________4571248_orig.png",
|
||||||
"currentStatus": "started",
|
"estimatedHours": 200,
|
||||||
"interestedCampers": [],
|
"currentStatus": "completed",
|
||||||
"confirmedCampers": [],
|
"moneySaved": 40000
|
||||||
"estimatedHours": 100,
|
|
||||||
"currentStatus": "completed"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bd1325d8c464cbafaeb7bdef",
|
"id": "bd1325d8c464cbafaeb7bdef",
|
||||||
"requestedDeliverables": [
|
|
||||||
"website",
|
|
||||||
"inventory",
|
|
||||||
"form"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We focus on raising funds to assist injured homeless animals.",
|
"whatDoesNonprofitDo": "We focus on raising funds to assist injured homeless animals.",
|
||||||
"websiteLink": "http://www.peoplesavinganimals.org/",
|
"websiteLink": "http://www.peoplesavinganimals.org/",
|
||||||
"name": "People Saving Animals",
|
"name": "People Saving Animals",
|
||||||
@ -178,15 +146,11 @@
|
|||||||
"logoUrl": "https://scontent-sjc2-1.xx.fbcdn.net/hphotos-xfa1/v/t1.0-9/59709_501505959886494_1605714757_n.jpg?oh=e12c08c046d824765a02242b7c8c3bb5&oe=560CFA6A",
|
"logoUrl": "https://scontent-sjc2-1.xx.fbcdn.net/hphotos-xfa1/v/t1.0-9/59709_501505959886494_1605714757_n.jpg?oh=e12c08c046d824765a02242b7c8c3bb5&oe=560CFA6A",
|
||||||
"imageUrl": "https://scontent-sjc2-1.xx.fbcdn.net/hphotos-xta1/t31.0-8/11270516_844556088914811_757350153964826829_o.jpg",
|
"imageUrl": "https://scontent-sjc2-1.xx.fbcdn.net/hphotos-xta1/t31.0-8/11270516_844556088914811_757350153964826829_o.jpg",
|
||||||
"estimatedHours": 300,
|
"estimatedHours": 300,
|
||||||
"currentStatus": "started"
|
"currentStatus": "started",
|
||||||
|
"moneySaved": 60000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bd1325d8c464cbafaeb6bde2",
|
"id": "bd1325d8c464cbafaeb6bde2",
|
||||||
"requestedDeliverables": [
|
|
||||||
"inventory",
|
|
||||||
"form",
|
|
||||||
"other"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We preserve Florida's health by regulating septic contractors and reviewing logs of sewage collection and disposal.",
|
"whatDoesNonprofitDo": "We preserve Florida's health by regulating septic contractors and reviewing logs of sewage collection and disposal.",
|
||||||
"websiteLink": "http://www.floridahealth.gov/",
|
"websiteLink": "http://www.floridahealth.gov/",
|
||||||
"name": "Florida Department of Health",
|
"name": "Florida Department of Health",
|
||||||
@ -200,13 +164,11 @@
|
|||||||
"logoUrl": "http://www.floridahealth.gov/_new/_files/images/DOH_logo.png",
|
"logoUrl": "http://www.floridahealth.gov/_new/_files/images/DOH_logo.png",
|
||||||
"imageUrl": "http://www.dep.state.fl.us/central/Home/Watershed/Home.jpg",
|
"imageUrl": "http://www.dep.state.fl.us/central/Home/Watershed/Home.jpg",
|
||||||
"estimatedHours": 200,
|
"estimatedHours": 200,
|
||||||
"currentStatus": "started"
|
"currentStatus": "started",
|
||||||
|
"moneySaved": 40000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bd1325d8c464cbafaeb6bde3",
|
"id": "bd1325d8c464cbafaeb6bde3",
|
||||||
"requestedDeliverables": [
|
|
||||||
"website"
|
|
||||||
],
|
|
||||||
"whatDoesNonprofitDo": "We strengthen the value of songwriting and independent music in Columbus, Ohio.",
|
"whatDoesNonprofitDo": "We strengthen the value of songwriting and independent music in Columbus, Ohio.",
|
||||||
"websiteLink": "http://columbussongwritersassociation.com",
|
"websiteLink": "http://columbussongwritersassociation.com",
|
||||||
"name": "Columbus Songwriters Association",
|
"name": "Columbus Songwriters Association",
|
||||||
@ -218,6 +180,87 @@
|
|||||||
"logoUrl": "https://columbussongwritersassociation.files.wordpress.com/2014/06/csa-logo.jpeg?w=705&h=435&crop=1",
|
"logoUrl": "https://columbussongwritersassociation.files.wordpress.com/2014/06/csa-logo.jpeg?w=705&h=435&crop=1",
|
||||||
"imageUrl": "https://columbussongwritersassociation.files.wordpress.com/2015/03/10502364_918551148225410_5082247612691070613_n.jpg?w=705&h=344&crop=1",
|
"imageUrl": "https://columbussongwritersassociation.files.wordpress.com/2015/03/10502364_918551148225410_5082247612691070613_n.jpg?w=705&h=344&crop=1",
|
||||||
"estimatedHours": 100,
|
"estimatedHours": 100,
|
||||||
"currentStatus": "completed"
|
"currentStatus": "completed",
|
||||||
|
"moneySaved": 20000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bd1325d8c464cbafaeb4bbb",
|
||||||
|
"whatDoesNonprofitDo": "We leverage all the benefits of cycling to support and improve the lives of youth and teens in the Triangle region.",
|
||||||
|
"websiteLink": "http://www.trianglebikeworks.org",
|
||||||
|
"name": "Triangle Bike Works",
|
||||||
|
"endUser": "Youth and teens in the Triangle region.",
|
||||||
|
"approvedDeliverables": [
|
||||||
|
"website"
|
||||||
|
],
|
||||||
|
"projectDescription": "Campers will build a website with donation integration.",
|
||||||
|
"logoUrl": "http://i.imgur.com/T5OkXuT.png",
|
||||||
|
"imageUrl": "http://i.imgur.com/7bOaMPq.jpg",
|
||||||
|
"estimatedHours": 100,
|
||||||
|
"currentStatus": "open",
|
||||||
|
"moneySaved": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bd1325d8c464cbafaeb4bccc",
|
||||||
|
"whatDoesNonprofitDo": "We work to eradicate female genital mutilation in the US and Gambia. We work with survivors and communities.",
|
||||||
|
"websiteLink": "http://safehandsforgirls.org/",
|
||||||
|
"name": "Safe Hands for Girls",
|
||||||
|
"endUser": "Supporters",
|
||||||
|
"approvedDeliverables": [
|
||||||
|
"website"
|
||||||
|
],
|
||||||
|
"projectDescription": "Campers will build a website with donation management.",
|
||||||
|
"logoUrl": "http://i.imgur.com/QnAY6Ji.png",
|
||||||
|
"imageUrl": "http://i.imgur.com/s9E4oa9.jpg",
|
||||||
|
"estimatedHours": 100,
|
||||||
|
"currentStatus": "open",
|
||||||
|
"moneySaved": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bd1325d8c464cbafaeb4beff",
|
||||||
|
"whatDoesNonprofitDo": "We're a part of the Department of Psychiatry at Mass General Hospital. We teach an innovative way for helping people that have challenging behaviors.",
|
||||||
|
"websiteLink": "http://www.thinkkids.org/",
|
||||||
|
"name": "Think Kids at Massachusetts General Hospital",
|
||||||
|
"endUser": "Volunteers, Administrators",
|
||||||
|
"approvedDeliverables": [
|
||||||
|
"volunteer"
|
||||||
|
],
|
||||||
|
"projectDescription": "We would like help developing a simple online based portal for both our trainees and trainers where we can store and share documents, track their progress, and incorporate a blackboard/chat forum.",
|
||||||
|
"logoUrl": "http://www.thinkkids.org/wp-content/themes/think-kids/images/logo.png",
|
||||||
|
"imageUrl": "http://i.imgur.com/hiGJms5.png",
|
||||||
|
"estimatedHours": 300,
|
||||||
|
"currentStatus": "open",
|
||||||
|
"moneySaved": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bd1325d8c464cbaeaeb4bdef",
|
||||||
|
"whatDoesNonprofitDo": "We enable, educate, and empower students from rural backgrounds in Uttar Pradesh, India.",
|
||||||
|
"websiteLink": "http://www.milaan.in/",
|
||||||
|
"name": "Milaan",
|
||||||
|
"endUser": "Supporters",
|
||||||
|
"approvedDeliverables": [
|
||||||
|
"website"
|
||||||
|
],
|
||||||
|
"projectDescription": "Campers will build a basic website for the US operations of Milaan. ",
|
||||||
|
"logoUrl": "http://i.imgur.com/GLq1qqD.png",
|
||||||
|
"imageUrl": "http://www.milaan.in/wp-content/uploads/2014/07/IMG_2624-e1432218749722.jpg",
|
||||||
|
"estimatedHours": 100,
|
||||||
|
"currentStatus": "open",
|
||||||
|
"moneySaved": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bd1325d8c464cbafaeb4beee",
|
||||||
|
"whatDoesNonprofitDo": "We're committed to closing the opportunity gap for children in Baltimore City by providing high quality after school and in-school programs.",
|
||||||
|
"websiteLink": "http://childfirstauthority.org/",
|
||||||
|
"name": "Child First Authority",
|
||||||
|
"endUser": "School Coordinators",
|
||||||
|
"approvedDeliverables": [
|
||||||
|
"volunteer"
|
||||||
|
],
|
||||||
|
"projectDescription": "Campers will build a dynamic database that will allow 7 community school coordinators to (1) input student-level absenteeism data, (2) code and track outreach efforts, (3) code root causes for absenteeism, and (4) track trends in each area. Currently, Child First uses an unwieldy excel spreadsheet to do this.",
|
||||||
|
"logoUrl": "http://i.imgur.com/YlPsQmN.jpg",
|
||||||
|
"imageUrl": "http://childfirstauthority.org/wp-content/uploads/2012/09/CFAHEADER7.gif",
|
||||||
|
"estimatedHours": 200,
|
||||||
|
"currentStatus": "open",
|
||||||
|
"moneySaved": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -41,26 +41,19 @@ var R = require('ramda'),
|
|||||||
observableQueryFromModel = require('../utils/rx').observableQueryFromModel,
|
observableQueryFromModel = require('../utils/rx').observableQueryFromModel,
|
||||||
|
|
||||||
userMigration = require('../utils/middleware').userMigration,
|
userMigration = require('../utils/middleware').userMigration,
|
||||||
ifNoUserRedirectTo = require('../utils/middleware').ifNoUserRedirectTo;
|
ifNoUserRedirectTo = require('../utils/middleware').ifNoUserRedirectTo,
|
||||||
|
ifNoUserSend = require('../utils/middleware').ifNoUserSend;
|
||||||
|
|
||||||
var challengeMapWithNames = utils.getChallengeMapWithNames();
|
var challengeMapWithNames = utils.getChallengeMapWithNames();
|
||||||
var challengeMapWithIds = utils.getChallengeMapWithIds();
|
var challengeMapWithIds = utils.getChallengeMapWithIds();
|
||||||
var challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames();
|
var challengeMapWithDashedNames = utils.getChallengeMapWithDashedNames();
|
||||||
|
var challangesRegex = /^(bonfire|waypoint|zipline|basejump)/i;
|
||||||
|
|
||||||
|
var dasherize = utils.dasherize;
|
||||||
|
var unDasherize = utils.unDasherize;
|
||||||
|
|
||||||
var getMDNLinks = utils.getMDNLinks;
|
var getMDNLinks = utils.getMDNLinks;
|
||||||
|
|
||||||
var challangesRegex = /^(bonfire|waypoint|zipline|basejump)/i;
|
|
||||||
function dasherize(name) {
|
|
||||||
return ('' + name)
|
|
||||||
.toLowerCase()
|
|
||||||
.replace(/\s/g, '-')
|
|
||||||
.replace(/[^a-z0-9\-\.]/gi, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function unDasherize(name) {
|
|
||||||
return ('' + name).replace(/\-/g, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateUserProgress(user, challengeId, completedChallenge) {
|
function updateUserProgress(user, challengeId, completedChallenge) {
|
||||||
var index = user.uncompletedChallenges.indexOf(challengeId);
|
var index = user.uncompletedChallenges.indexOf(challengeId);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
@ -75,17 +68,32 @@ module.exports = function(app) {
|
|||||||
var router = app.loopback.Router();
|
var router = app.loopback.Router();
|
||||||
var Challenge = app.models.Challenge;
|
var Challenge = app.models.Challenge;
|
||||||
var User = app.models.User;
|
var User = app.models.User;
|
||||||
|
var redirectNonUser =
|
||||||
|
ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works');
|
||||||
|
var send200toNonUser = ifNoUserSend(true);
|
||||||
|
|
||||||
router.post('/completed-challenge/', completedChallenge);
|
router.post(
|
||||||
router.post('/completed-zipline-or-basejump', completedZiplineOrBasejump);
|
'/completed-challenge/',
|
||||||
router.post('/completed-bonfire', completedBonfire);
|
send200toNonUser,
|
||||||
|
completedChallenge
|
||||||
|
);
|
||||||
|
router.post(
|
||||||
|
'/completed-zipline-or-basejump',
|
||||||
|
send200toNonUser,
|
||||||
|
completedZiplineOrBasejump
|
||||||
|
);
|
||||||
|
router.post(
|
||||||
|
'/completed-bonfire',
|
||||||
|
send200toNonUser,
|
||||||
|
completedBonfire
|
||||||
|
);
|
||||||
|
|
||||||
// the follow routes are covered by userMigration
|
// the follow routes are covered by userMigration
|
||||||
router.use(userMigration);
|
router.use(userMigration);
|
||||||
router.get('/map', challengeMap);
|
router.get('/map', challengeMap);
|
||||||
router.get(
|
router.get(
|
||||||
'/challenges/next-challenge',
|
'/challenges/next-challenge',
|
||||||
ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works'),
|
redirectNonUser,
|
||||||
returnNextChallenge
|
returnNextChallenge
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -93,7 +101,7 @@ module.exports = function(app) {
|
|||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/challenges/',
|
'/challenges/',
|
||||||
ifNoUserRedirectTo('/challenges/learn-how-free-code-camp-works'),
|
redirectNonUser,
|
||||||
returnCurrentChallenge
|
returnCurrentChallenge
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -308,7 +316,6 @@ module.exports = function(app) {
|
|||||||
.withLatestFrom(
|
.withLatestFrom(
|
||||||
Rx.Observable.just(req.user),
|
Rx.Observable.just(req.user),
|
||||||
function(pairedWith, user) {
|
function(pairedWith, user) {
|
||||||
debug('yo');
|
|
||||||
return {
|
return {
|
||||||
user: user,
|
user: user,
|
||||||
pairedWith: pairedWith
|
pairedWith: pairedWith
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
var R = require('ramda'),
|
var Rx = require('rx');
|
||||||
// Rx = require('rx'),
|
var debug = require('debug')('freecc:fieldguides');
|
||||||
debug = require('debug')('freecc:fieldguides'),
|
var observeMethod = require('../utils/rx').observeMethod;
|
||||||
utils = require('../utils');
|
var saveUser = require('../utils/rx').saveUser;
|
||||||
|
var utils = require('../utils');
|
||||||
|
|
||||||
|
var allFieldGuideNamesAndIds = utils.allFieldGuideNamesAndIds();
|
||||||
|
|
||||||
module.exports = function(app) {
|
module.exports = function(app) {
|
||||||
var router = app.loopback.Router();
|
var router = app.loopback.Router();
|
||||||
var FieldGuide = app.models.FieldGuide;
|
var FieldGuide = app.models.FieldGuide;
|
||||||
|
var findFieldGuideById = observeMethod(FieldGuide, 'findById');
|
||||||
|
var findOneFieldGuide = observeMethod(FieldGuide, 'findOne');
|
||||||
|
|
||||||
router.get('/field-guide/all-articles', showAllFieldGuides);
|
router.get('/field-guide/all-articles', showAllFieldGuides);
|
||||||
router.get('/field-guide/:fieldGuideName', returnIndividualFieldGuide);
|
router.get('/field-guide/:fieldGuideName', returnIndividualFieldGuide);
|
||||||
@ -15,60 +20,66 @@ module.exports = function(app) {
|
|||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
||||||
function returnIndividualFieldGuide(req, res, next) {
|
function returnIndividualFieldGuide(req, res, next) {
|
||||||
var dashedNameFromQuery = req.params.fieldGuideName;
|
var dashedName = req.params.fieldGuideName;
|
||||||
if (req.user) {
|
var userSave = Rx.Observable.just(req.user)
|
||||||
var completed = req.user.completedFieldGuides;
|
.filter(function(user) {
|
||||||
|
return !!user;
|
||||||
|
})
|
||||||
|
.map(function(user) {
|
||||||
|
var completed = user.completedFieldGuides;
|
||||||
|
|
||||||
var uncompletedFieldGuides = utils.allFieldGuideIds()
|
var uncompletedFieldGuides = utils.allFieldGuideIds()
|
||||||
.filter(function (elem) {
|
.filter(function(id) {
|
||||||
if (completed.indexOf(elem) === -1) {
|
if (completed.indexOf(id) === -1) {
|
||||||
return elem;
|
return id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
req.user.uncompletedFieldGuides = uncompletedFieldGuides;
|
user.uncompletedFieldGuides = uncompletedFieldGuides;
|
||||||
// TODO(berks): handle callback properly
|
return user;
|
||||||
req.user.save(function(err) {
|
})
|
||||||
if (err) { return next(err); }
|
.flatMap(function(user) {
|
||||||
|
return saveUser(user);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
FieldGuide.find({ where: {'dashedName': dashedNameFromQuery}},
|
var query = { where: { dashedName: { like: dashedName, options: 'i' } } };
|
||||||
function(err, fieldGuideFromMongo) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fieldGuideFromMongo.length < 1) {
|
debug('find fieldGuide', query);
|
||||||
|
Rx.Observable.combineLatest(
|
||||||
|
// find that field guide
|
||||||
|
findOneFieldGuide(query),
|
||||||
|
userSave,
|
||||||
|
Rx.helpers.identity
|
||||||
|
)
|
||||||
|
.subscribe(
|
||||||
|
// don't care about return from userSave
|
||||||
|
function(fieldGuide) {
|
||||||
|
if (!fieldGuide) {
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: '404: We couldn\'t find a field guide entry with that name. ' +
|
msg: '404: We couldn\'t find a field guide entry with ' +
|
||||||
'Please double check the name.'
|
'that name. Please double check the name.'
|
||||||
});
|
});
|
||||||
|
return res.redirect('/field-guide/all-articles');
|
||||||
return res.redirect('/');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var fieldGuide = R.head(fieldGuideFromMongo);
|
if (fieldGuide.dashedName !== dashedName) {
|
||||||
fieldGuide.name.toLowerCase().replace(/\s/g, '-').replace(/\?/g, '');
|
return res.redirect('../field-guide/' + fieldGuide.dashedName);
|
||||||
|
}
|
||||||
// if (fieldGuide.dashedName !== dashedNameFromQuery) {
|
|
||||||
// return res.redirect('../field-guide/' + fieldGuide.dashedName);
|
|
||||||
// }
|
|
||||||
res.render('field-guide/show', {
|
res.render('field-guide/show', {
|
||||||
title: fieldGuide.name,
|
title: fieldGuide.name,
|
||||||
fieldGuideId: fieldGuide.id,
|
fieldGuideId: fieldGuide.id,
|
||||||
description: fieldGuide.description.join('')
|
description: fieldGuide.description.join('')
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
next
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAllFieldGuides(req, res) {
|
function showAllFieldGuides(req, res) {
|
||||||
var allFieldGuideNamesAndIds = utils.allFieldGuideNamesAndIds();
|
|
||||||
|
|
||||||
var completedFieldGuides = [];
|
var completedFieldGuides = [];
|
||||||
if (req.user && req.user.completedFieldGuides) {
|
if (req.user && req.user.completedFieldGuides) {
|
||||||
completedFieldGuides = req.user.completedFieldGuides;
|
completedFieldGuides = req.user.completedFieldGuides;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.render('field-guide/all-articles', {
|
res.render('field-guide/all-articles', {
|
||||||
allFieldGuideNamesAndIds: allFieldGuideNamesAndIds,
|
allFieldGuideNamesAndIds: allFieldGuideNamesAndIds,
|
||||||
completedFieldGuides: completedFieldGuides
|
completedFieldGuides: completedFieldGuides
|
||||||
@ -76,7 +87,9 @@ module.exports = function(app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showCompletedFieldGuideFunction(req, res) {
|
function showCompletedFieldGuideFunction(req, res) {
|
||||||
req.flash('success', {
|
req.flash(
|
||||||
|
'success',
|
||||||
|
{
|
||||||
msg: [
|
msg: [
|
||||||
'You\'ve read all our current Field Guide entries. ' +
|
'You\'ve read all our current Field Guide entries. ' +
|
||||||
'If you have ideas for other Field Guide articles, ' +
|
'If you have ideas for other Field Guide articles, ' +
|
||||||
@ -85,7 +98,8 @@ module.exports = function(app) {
|
|||||||
'issues/new?&body=Please describe your idea for a Field Guide' +
|
'issues/new?&body=Please describe your idea for a Field Guide' +
|
||||||
' article and include links if possible.\'>GitHub</a>.'
|
' article and include links if possible.\'>GitHub</a>.'
|
||||||
].join('')
|
].join('')
|
||||||
});
|
}
|
||||||
|
);
|
||||||
return res.redirect('../field-guide/how-do-i-use-this-guide');
|
return res.redirect('../field-guide/how-do-i-use-this-guide');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,24 +109,25 @@ module.exports = function(app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!req.user.uncompletedFieldGuides.length) {
|
if (!req.user.uncompletedFieldGuides.length) {
|
||||||
return showCompletedFieldGuideFunction(req, res, next);
|
return showCompletedFieldGuideFunction(req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldGuide.findById(req.user.uncompletedFieldGuides[0],
|
findFieldGuideById(req.user.uncompletedFieldGuides[0]).subscribe(
|
||||||
function(err, fieldGuide) {
|
function(fieldGuide) {
|
||||||
|
|
||||||
if (err) { return next(err); }
|
|
||||||
if (!fieldGuide) {
|
if (!fieldGuide) {
|
||||||
debug('bad juju in field guide %s',
|
debug(
|
||||||
req.user.uncompletedFieldGuides[0]);
|
'field guide %s not found',
|
||||||
|
req.user.uncompletedFieldGuides[0]
|
||||||
|
);
|
||||||
return res.redirect('../field-guide/how-do-i-use-this-guide');
|
return res.redirect('../field-guide/how-do-i-use-this-guide');
|
||||||
}
|
}
|
||||||
return res.redirect('../field-guide/' + fieldGuide.dashedName);
|
return res.redirect('../field-guide/' + fieldGuide.dashedName);
|
||||||
});
|
},
|
||||||
|
next
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
function completedFieldGuide(req, res, next) {
|
function completedFieldGuide(req, res, next) {
|
||||||
var fieldGuideId = req.body.fieldGuideInfo.fieldGuideId;
|
var fieldGuideId = req.body.fieldGuideInfo.fieldGuideId;
|
||||||
|
|
||||||
req.user.completedFieldGuides.push(fieldGuideId);
|
req.user.completedFieldGuides.push(fieldGuideId);
|
||||||
@ -123,10 +138,11 @@ function completedFieldGuide(req, res, next) {
|
|||||||
req.user.uncompletedFieldGuides.splice(index, 1);
|
req.user.uncompletedFieldGuides.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
req.user.save(function (err) {
|
saveUser(req.user).subscribe(
|
||||||
if (err) {
|
function() {
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
res.send(true);
|
res.send(true);
|
||||||
});
|
},
|
||||||
}
|
next
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
|
var debug = require('debug')('freecc:nonprofits');
|
||||||
|
var observeMethod = require('../utils/rx').observeMethod;
|
||||||
|
var unDasherize = require('../utils').unDasherize;
|
||||||
|
var dasherize = require('../utils').dasherize;
|
||||||
|
|
||||||
module.exports = function(app) {
|
module.exports = function(app) {
|
||||||
var router = app.loopback.Router();
|
var router = app.loopback.Router();
|
||||||
var Nonprofit = app.models.Nonprofit;
|
var Nonprofit = app.models.Nonprofit;
|
||||||
|
var findNonprofits = observeMethod(Nonprofit, 'find');
|
||||||
|
var findOneNonprofit = observeMethod(Nonprofit, 'findOne');
|
||||||
|
|
||||||
router.get('/nonprofits/directory', nonprofitsDirectory);
|
router.get('/nonprofits/directory', nonprofitsDirectory);
|
||||||
router.get('/nonprofits/:nonprofitName', returnIndividualNonprofit);
|
router.get('/nonprofits/:nonprofitName', returnIndividualNonprofit);
|
||||||
@ -8,59 +15,70 @@ module.exports = function(app) {
|
|||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
||||||
function nonprofitsDirectory(req, res, next) {
|
function nonprofitsDirectory(req, res, next) {
|
||||||
Nonprofit.find(
|
var sum = 0;
|
||||||
{ where: { estimatedHours: { gt: 0 } } },
|
findNonprofits({}).subscribe(
|
||||||
function(err, nonprofits) {
|
function(nonprofits) {
|
||||||
if (err) { return next(err); }
|
nonprofits = nonprofits.sort(function(a, b) {
|
||||||
|
return b.moneySaved - a.moneySaved;
|
||||||
|
});
|
||||||
|
totalSavings = function() {
|
||||||
|
for(i = 0; i < nonprofits.length; i++) {
|
||||||
|
sum += nonprofits[i].moneySaved;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}();
|
||||||
res.render('nonprofits/directory', {
|
res.render('nonprofits/directory', {
|
||||||
title: 'Nonprofits we help',
|
title: 'Nonprofits we help',
|
||||||
nonprofits: nonprofits
|
nonprofits: nonprofits,
|
||||||
|
totalSavings: totalSavings.toString().replace(/000$/, ',000')
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
next
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function returnIndividualNonprofit(req, res, next) {
|
function returnIndividualNonprofit(req, res, next) {
|
||||||
var dashedName = req.params.nonprofitName;
|
var dashedName = req.params.nonprofitName;
|
||||||
var nonprofitName = dashedName.replace(/\-/g, ' ');
|
var nonprofitName = unDasherize(dashedName);
|
||||||
|
var query = { where: { name: {
|
||||||
|
like: nonprofitName,
|
||||||
|
options: 'i'
|
||||||
|
} } };
|
||||||
|
|
||||||
Nonprofit.find(
|
debug('looking for %s', nonprofitName);
|
||||||
{ where: { name: new RegExp(nonprofitName, 'i') } },
|
debug('query', query);
|
||||||
function(err, nonprofit) {
|
findOneNonprofit(query).subscribe(
|
||||||
if (err) {
|
function(nonprofit) {
|
||||||
return next(err);
|
if (!nonprofit) {
|
||||||
}
|
|
||||||
|
|
||||||
if (nonprofit.length < 1) {
|
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: "404: We couldn't find a nonprofit with that name. " +
|
msg: "404: We couldn't find a nonprofit with that name. " +
|
||||||
'Please double check the name.'
|
'Please double check the name.'
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.redirect('/nonprofits');
|
return res.redirect('/nonprofits');
|
||||||
}
|
}
|
||||||
|
|
||||||
nonprofit = nonprofit.pop();
|
var dashedNameFull = dasherize(nonprofit.name);
|
||||||
var dashedNameFull = nonprofit.name.toLowerCase().replace(/\s/g, '-');
|
|
||||||
if (dashedNameFull !== dashedName) {
|
if (dashedNameFull !== dashedName) {
|
||||||
return res.redirect('../nonprofit/' + dashedNameFull);
|
return res.redirect('../nonprofit/' + dashedNameFull);
|
||||||
}
|
}
|
||||||
|
|
||||||
var buttonActive = false;
|
var buttonActive = false;
|
||||||
if (req.user) {
|
if (
|
||||||
if (req.user.uncompletedBonfires.length === 0) {
|
req.user &&
|
||||||
if (req.user.completedCoursewares.length > 63) {
|
req.user.uncompletedBonfires.length === 0 &&
|
||||||
|
req.user.completedCoursewares.length > 63
|
||||||
|
) {
|
||||||
var hasShownInterest =
|
var hasShownInterest =
|
||||||
nonprofit.interestedCampers.filter(function ( obj ) {
|
nonprofit.interestedCampers.filter(function(user) {
|
||||||
return obj.username === req.user.username;
|
return user.username === req.user.username;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasShownInterest.length === 0) {
|
if (hasShownInterest.length === 0) {
|
||||||
buttonActive = true;
|
buttonActive = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.render('nonprofits/show', {
|
res.render('nonprofits/show', {
|
||||||
dashedName: dashedNameFull,
|
dashedName: dashedNameFull,
|
||||||
@ -95,35 +113,11 @@ module.exports = function(app) {
|
|||||||
interestedCampers: nonprofit.interestedCampers,
|
interestedCampers: nonprofit.interestedCampers,
|
||||||
assignedCampers: nonprofit.assignedCampers,
|
assignedCampers: nonprofit.assignedCampers,
|
||||||
buttonActive: buttonActive,
|
buttonActive: buttonActive,
|
||||||
|
moneySaved: nonprofit.moneySaved,
|
||||||
currentStatus: nonprofit.currentStatus
|
currentStatus: nonprofit.currentStatus
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
next
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
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.username,
|
|
||||||
picture: req.user.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');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
|
@ -2,15 +2,12 @@ var Rx = require('rx'),
|
|||||||
Twit = require('twit'),
|
Twit = require('twit'),
|
||||||
async = require('async'),
|
async = require('async'),
|
||||||
moment = require('moment'),
|
moment = require('moment'),
|
||||||
Slack = require('node-slack'),
|
|
||||||
request = require('request'),
|
request = require('request'),
|
||||||
debug = require('debug')('freecc:cntr:resources'),
|
debug = require('debug')('freecc:cntr:resources'),
|
||||||
|
|
||||||
constantStrings = require('../utils/constantStrings.json'),
|
constantStrings = require('../utils/constantStrings.json'),
|
||||||
bootcampJson = require('../utils/bootcamps.json'),
|
bootcampJson = require('../utils/bootcamps.json'),
|
||||||
secrets = require('../../config/secrets');
|
secrets = require('../../config/secrets');
|
||||||
|
|
||||||
var slack = new Slack(secrets.slackHook);
|
|
||||||
module.exports = function(app) {
|
module.exports = function(app) {
|
||||||
var router = app.loopback.Router();
|
var router = app.loopback.Router();
|
||||||
var User = app.models.User;
|
var User = app.models.User;
|
||||||
@ -24,8 +21,6 @@ module.exports = function(app) {
|
|||||||
router.get('/api/trello', trelloCalls);
|
router.get('/api/trello', trelloCalls);
|
||||||
router.get('/api/codepen/twitter/:screenName', twitter);
|
router.get('/api/codepen/twitter/:screenName', twitter);
|
||||||
router.get('/sitemap.xml', sitemap);
|
router.get('/sitemap.xml', sitemap);
|
||||||
router.post('/get-help', getHelp);
|
|
||||||
router.post('/get-pair', getPair);
|
|
||||||
router.get('/chat', chat);
|
router.get('/chat', chat);
|
||||||
router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
|
router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
|
||||||
router.get('/coding-bootcamp-cost-calculator.json', bootcampCalculatorJson);
|
router.get('/coding-bootcamp-cost-calculator.json', bootcampCalculatorJson);
|
||||||
@ -34,77 +29,15 @@ module.exports = function(app) {
|
|||||||
router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
|
router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
|
||||||
router.get('/nonprofits', nonprofits);
|
router.get('/nonprofits', nonprofits);
|
||||||
router.get('/nonprofits-form', nonprofitsForm);
|
router.get('/nonprofits-form', nonprofitsForm);
|
||||||
|
router.get('/our-sponsors', sponsors);
|
||||||
router.get('/jobs-form', jobsForm);
|
router.get('/jobs-form', jobsForm);
|
||||||
router.get('/submit-cat-photo', catPhotoSubmit);
|
router.get('/submit-cat-photo', catPhotoSubmit);
|
||||||
router.get('/unsubscribe/:email', unsubscribe);
|
router.get('/unsubscribe/:email', unsubscribe);
|
||||||
router.get('/unsubscribed', unsubscribed);
|
router.get('/unsubscribed', unsubscribed);
|
||||||
router.get('/cats.json', getCats);
|
router.get('/cats.json', getCats);
|
||||||
router.get('/api/slack', slackInvite);
|
|
||||||
|
|
||||||
app.use(router);
|
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) {
|
function twitter(req, res, next) {
|
||||||
// sends out random tweets about javascript
|
// sends out random tweets about javascript
|
||||||
var T = new Twit({
|
var T = new Twit({
|
||||||
@ -134,56 +67,6 @@ module.exports = function(app) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getHelp(req, res) {
|
|
||||||
var userName = req.user.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.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) {
|
function sitemap(req, res, next) {
|
||||||
var appUrl = 'http://www.freecodecamp.com';
|
var appUrl = 'http://www.freecodecamp.com';
|
||||||
var now = moment(new Date()).format('YYYY-MM-DD');
|
var now = moment(new Date()).format('YYYY-MM-DD');
|
||||||
@ -337,6 +220,10 @@ module.exports = function(app) {
|
|||||||
res.send(bootcampJson);
|
res.send(bootcampJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function chat(req, res) {
|
||||||
|
res.redirect('https://gitter.im/FreeCodeCamp/FreeCodeCamp');
|
||||||
|
}
|
||||||
|
|
||||||
function jobsForm(req, res) {
|
function jobsForm(req, res) {
|
||||||
res.render('resources/jobs-form', {
|
res.render('resources/jobs-form', {
|
||||||
title: 'Employer Partnership Form for Job Postings,' +
|
title: 'Employer Partnership Form for Job Postings,' +
|
||||||
@ -351,6 +238,12 @@ module.exports = function(app) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sponsors(req, res) {
|
||||||
|
res.render('sponsors/sponsors', {
|
||||||
|
title: 'The Sponsors who make Free Code Camp Possible'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function nonprofits(req, res) {
|
function nonprofits(req, res) {
|
||||||
res.render('resources/nonprofits', {
|
res.render('resources/nonprofits', {
|
||||||
title: 'A guide to our Nonprofit Projects'
|
title: 'A guide to our Nonprofit Projects'
|
||||||
|
@ -1,16 +1,84 @@
|
|||||||
var nodemailer = require('nodemailer'),
|
var Rx = require('rx'),
|
||||||
|
nodemailer = require('nodemailer'),
|
||||||
|
assign = require('object.assign'),
|
||||||
sanitizeHtml = require('sanitize-html'),
|
sanitizeHtml = require('sanitize-html'),
|
||||||
moment = require('moment'),
|
moment = require('moment'),
|
||||||
mongodb = require('mongodb'),
|
mongodb = require('mongodb'),
|
||||||
// debug = require('debug')('freecc:cntr:story'),
|
debug = require('debug')('freecc:cntr:story'),
|
||||||
utils = require('../utils'),
|
utils = require('../utils'),
|
||||||
|
observeMethod = require('../utils/rx').observeMethod,
|
||||||
|
saveUser = require('../utils/rx').saveUser,
|
||||||
|
saveInstance = require('../utils/rx').saveInstance,
|
||||||
MongoClient = mongodb.MongoClient,
|
MongoClient = mongodb.MongoClient,
|
||||||
secrets = require('../../config/secrets');
|
secrets = require('../../config/secrets');
|
||||||
|
|
||||||
|
var foundationDate = 1413298800000;
|
||||||
|
var time48Hours = 172800000;
|
||||||
|
|
||||||
|
var unDasherize = utils.unDasherize;
|
||||||
|
var dasherize = utils.dasherize;
|
||||||
|
var getURLTitle = utils.getURLTitle;
|
||||||
|
|
||||||
|
var transporter = nodemailer.createTransport({
|
||||||
|
service: 'Mandrill',
|
||||||
|
auth: {
|
||||||
|
user: secrets.mandrill.user,
|
||||||
|
pass: secrets.mandrill.password
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function sendMailWhillyNilly(mailOptions) {
|
||||||
|
transporter.sendMail(mailOptions, function(err) {
|
||||||
|
if (err) {
|
||||||
|
console.log('err sending mail whilly nilly', err);
|
||||||
|
console.log('logging err but not carring');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hotRank(timeValue, rank) {
|
||||||
|
/*
|
||||||
|
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
|
||||||
|
* tMS = postedOnDate - foundationTime;
|
||||||
|
* Ranking...
|
||||||
|
* f(ts, 1, rank) = log(10)z + (ts)/45000;
|
||||||
|
*/
|
||||||
|
var z = Math.log(rank) / Math.log(10);
|
||||||
|
var hotness = z + (timeValue / time48Hours);
|
||||||
|
return hotness;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortByRank(a, b) {
|
||||||
|
return hotRank(b.timePosted - foundationDate, b.rank) -
|
||||||
|
hotRank(a.timePosted - foundationDate, a.rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanData(data, opts) {
|
||||||
|
var options = assign(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
allowedTags: [],
|
||||||
|
allowedAttributes: []
|
||||||
|
},
|
||||||
|
opts || {}
|
||||||
|
);
|
||||||
|
return sanitizeHtml(data, options).replace(/";/g, '"');
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = function(app) {
|
module.exports = function(app) {
|
||||||
var router = app.loopback.Router();
|
var router = app.loopback.Router();
|
||||||
var User = app.models.User;
|
var User = app.models.User;
|
||||||
|
var findUserById = observeMethod(User, 'findById');
|
||||||
|
var findOneUser = observeMethod(User, 'findOne');
|
||||||
|
|
||||||
var Story = app.models.Story;
|
var Story = app.models.Story;
|
||||||
|
var findStory = observeMethod(Story, 'find');
|
||||||
|
var findOneStory = observeMethod(Story, 'findOne');
|
||||||
|
var findStoryById = observeMethod(Story, 'findById');
|
||||||
|
var countStories = observeMethod(Story, 'count');
|
||||||
|
|
||||||
|
var Comment = app.models.Comment;
|
||||||
|
var findCommentById = observeMethod(Comment, 'findById');
|
||||||
|
|
||||||
router.get('/stories/hotStories', hotJSON);
|
router.get('/stories/hotStories', hotJSON);
|
||||||
router.get('/stories/comments/:id', comments);
|
router.get('/stories/comments/:id', comments);
|
||||||
@ -21,43 +89,32 @@ module.exports = function(app) {
|
|||||||
router.get('/stories/submit/new-story', preSubmit);
|
router.get('/stories/submit/new-story', preSubmit);
|
||||||
router.post('/stories/preliminary', newStory);
|
router.post('/stories/preliminary', newStory);
|
||||||
router.post('/stories/', storySubmission);
|
router.post('/stories/', storySubmission);
|
||||||
router.get('/stories/', hot);
|
router.get('/news/', hot);
|
||||||
router.post('/stories/search', getStories);
|
router.post('/stories/search', getStories);
|
||||||
router.get('/stories/:storyName', returnIndividualStory);
|
router.get('/news/:storyName', returnIndividualStory);
|
||||||
router.post('/stories/upvote/', upvote);
|
router.post('/stories/upvote/', upvote);
|
||||||
|
router.get('/stories/:storyName', redirectToNews);
|
||||||
|
|
||||||
app.use(router);
|
app.use(router);
|
||||||
|
|
||||||
function hotRank(timeValue, rank) {
|
function redirectToNews(req, res) {
|
||||||
/*
|
var url = req.originalUrl.replace(/^\/stories/, '/news');
|
||||||
* Hotness ranking algorithm: http://amix.dk/blog/post/19588
|
return res.redirect(url);
|
||||||
* tMS = postedOnDate - foundationTime;
|
|
||||||
* Ranking...
|
|
||||||
* f(ts, 1, rank) = log(10)z + (ts)/45000;
|
|
||||||
*/
|
|
||||||
var time48Hours = 172800000;
|
|
||||||
var hotness;
|
|
||||||
var z = Math.log(rank) / Math.log(10);
|
|
||||||
hotness = z + (timeValue / time48Hours);
|
|
||||||
return hotness;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hotJSON(req, res, next) {
|
function hotJSON(req, res, next) {
|
||||||
Story.find({order: 'timePosted DESC', limit: 1000}, function(err, stories) {
|
var query = {
|
||||||
if (err) {
|
order: 'timePosted DESC',
|
||||||
return next(err);
|
limit: 1000
|
||||||
}
|
};
|
||||||
var foundationDate = 1413298800000;
|
findStory(query).subscribe(
|
||||||
|
function(stories) {
|
||||||
var sliceVal = stories.length >= 100 ? 100 : stories.length;
|
var sliceVal = stories.length >= 100 ? 100 : stories.length;
|
||||||
return res.json(stories.map(function(elem) {
|
var data = stories.sort(sortByRank).slice(0, sliceVal);
|
||||||
return elem;
|
res.json(data);
|
||||||
}).sort(function(a, b) {
|
},
|
||||||
return hotRank(b.timePosted - foundationDate, b.rank, b.headline)
|
next
|
||||||
- hotRank(a.timePosted - foundationDate, a.rank, a.headline);
|
);
|
||||||
}).slice(0, sliceVal));
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function hot(req, res) {
|
function hot(req, res) {
|
||||||
@ -74,32 +131,11 @@ module.exports = function(app) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* no used anywhere
|
|
||||||
function search(req, res) {
|
|
||||||
return res.render('stories/index', {
|
|
||||||
title: 'Search the archives of Camper News',
|
|
||||||
page: 'search'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function recent(req, res) {
|
|
||||||
return res.render('stories/index', {
|
|
||||||
title: 'Recently submitted stories on Camper News',
|
|
||||||
page: 'recent'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
function preSubmit(req, res) {
|
function preSubmit(req, res) {
|
||||||
|
|
||||||
var data = req.query;
|
var data = req.query;
|
||||||
var cleanData = sanitizeHtml(data.url, {
|
var cleanedData = cleanData(data.url);
|
||||||
allowedTags: [],
|
|
||||||
allowedAttributes: []
|
|
||||||
}).replace(/";/g, '"');
|
|
||||||
if (data.url.replace(/&/g, '&') !== cleanData) {
|
|
||||||
|
|
||||||
|
if (data.url.replace(/&/g, '&') !== cleanedData) {
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: 'The data for this post is malformed'
|
msg: 'The data for this post is malformed'
|
||||||
});
|
});
|
||||||
@ -121,46 +157,34 @@ module.exports = function(app) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function returnIndividualStory(req, res, next) {
|
function returnIndividualStory(req, res, next) {
|
||||||
var dashedName = req.params.storyName;
|
var dashedName = req.params.storyName;
|
||||||
|
var storyName = unDasherize(dashedName);
|
||||||
|
|
||||||
var storyName = dashedName.replace(/\-/g, ' ').trim();
|
findOneStory({ where: { storyLink: storyName } }).subscribe(
|
||||||
|
function(story) {
|
||||||
Story.find({where: {'storyLink': storyName}}, function(err, story) {
|
if (!story) {
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (story.length < 1) {
|
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: "404: We couldn't find a story with that name. " +
|
msg: "404: We couldn't find a story with that name. " +
|
||||||
'Please double check the name.'
|
'Please double check the name.'
|
||||||
});
|
});
|
||||||
|
return res.redirect('/news');
|
||||||
return res.redirect('/stories/');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
story = story.pop();
|
|
||||||
var dashedNameFull = story.storyLink.toLowerCase()
|
var dashedNameFull = story.storyLink.toLowerCase()
|
||||||
.replace(/\s+/g, ' ')
|
.replace(/\s+/g, ' ')
|
||||||
.replace(/\s/g, '-');
|
.replace(/\s/g, '-');
|
||||||
|
|
||||||
if (dashedNameFull !== dashedName) {
|
if (dashedNameFull !== dashedName) {
|
||||||
return res.redirect('../stories/' + dashedNameFull);
|
return res.redirect('../stories/' + dashedNameFull);
|
||||||
}
|
}
|
||||||
|
|
||||||
var userVoted = false;
|
var username = req.user ? req.user.username : '';
|
||||||
try {
|
// true if any of votes are made by user
|
||||||
var votedObj = story.upVotes.filter(function(a) {
|
var userVoted = story.upVotes.some(function(upvote) {
|
||||||
return a['upVotedByUsername'] === req.user['profile']['username'];
|
return upvote.upVotedByUsername === username;
|
||||||
});
|
});
|
||||||
if (votedObj.length > 0) {
|
|
||||||
userVoted = true;
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
userVoted = false;
|
|
||||||
}
|
|
||||||
res.render('stories/index', {
|
res.render('stories/index', {
|
||||||
title: story.headline,
|
title: story.headline,
|
||||||
link: story.link,
|
link: story.link,
|
||||||
@ -178,7 +202,9 @@ module.exports = function(app) {
|
|||||||
storyMetaDescription: story.metaDescription,
|
storyMetaDescription: story.metaDescription,
|
||||||
hasUserVoted: userVoted
|
hasUserVoted: userVoted
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
|
next
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStories(req, res, next) {
|
function getStories(req, res, next) {
|
||||||
@ -224,57 +250,52 @@ module.exports = function(app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function upvote(req, res, next) {
|
function upvote(req, res, next) {
|
||||||
var data = req.body.data;
|
var id = req.body.data.id;
|
||||||
Story.find({'id': data.id}, function(err, story) {
|
var story$ = findStoryById(id).shareReplay();
|
||||||
if (err) {
|
|
||||||
return next(err);
|
story$.flatMap(function(story) {
|
||||||
|
// find story author
|
||||||
|
return findUserById(story.author.userId);
|
||||||
|
})
|
||||||
|
.flatMap(function(user) {
|
||||||
|
// if user deletes account then this will not exist
|
||||||
|
if (user) {
|
||||||
|
user.progressTimestamps.push(Date.now());
|
||||||
}
|
}
|
||||||
story = story.pop();
|
return saveUser(user);
|
||||||
story.rank++;
|
})
|
||||||
story.upVotes.push(
|
.flatMap(function() {
|
||||||
{
|
req.user.progressTimestamps.push(Date.now());
|
||||||
|
return saveUser(req.user);
|
||||||
|
})
|
||||||
|
.flatMap(function() {
|
||||||
|
return story$;
|
||||||
|
})
|
||||||
|
.flatMap(function(story) {
|
||||||
|
debug('upvoting');
|
||||||
|
story.rank += 1;
|
||||||
|
story.upVotes.push({
|
||||||
upVotedBy: req.user.id,
|
upVotedBy: req.user.id,
|
||||||
upVotedByUsername: req.user.username
|
upVotedByUsername: req.user.username
|
||||||
}
|
|
||||||
);
|
|
||||||
story.markModified('rank');
|
|
||||||
story.save();
|
|
||||||
// NOTE(Berks): This logic is full of wholes and race conditions
|
|
||||||
// this could be the source of many 'can't set headers after
|
|
||||||
// they are sent'
|
|
||||||
// errors. This needs cleaning
|
|
||||||
User.findOne(
|
|
||||||
{ where: { id: story.author.userId } },
|
|
||||||
function(err, user) {
|
|
||||||
if (err) { return next(err); }
|
|
||||||
|
|
||||||
user.progressTimestamps.push(Date.now() || 0);
|
|
||||||
user.save(function (err) {
|
|
||||||
req.user.save(function (err) {
|
|
||||||
if (err) { return next(err); }
|
|
||||||
});
|
});
|
||||||
req.user.progressTimestamps.push(Date.now() || 0);
|
return saveInstance(story);
|
||||||
if (err) {
|
})
|
||||||
return next(err);
|
.subscribe(
|
||||||
}
|
function(story) {
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return res.send(story);
|
return res.send(story);
|
||||||
});
|
},
|
||||||
|
next
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function comments(req, res, next) {
|
function comments(req, res, next) {
|
||||||
var data = req.params.id;
|
var id = req.params.id;
|
||||||
Comment.find(
|
findCommentById(id).subscribe(
|
||||||
{ where: {'id': data } },
|
function(comment) {
|
||||||
function(err, comment) {
|
res.send(comment);
|
||||||
if (err) {
|
},
|
||||||
return next(err);
|
next
|
||||||
}
|
);
|
||||||
comment = comment.pop();
|
|
||||||
return res.send(comment);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function newStory(req, res, next) {
|
function newStory(req, res, next) {
|
||||||
@ -282,10 +303,8 @@ module.exports = function(app) {
|
|||||||
return next(new Error('Must be logged in'));
|
return next(new Error('Must be logged in'));
|
||||||
}
|
}
|
||||||
var url = req.body.data.url;
|
var url = req.body.data.url;
|
||||||
var cleanURL = sanitizeHtml(url, {
|
var cleanURL = cleanData(url);
|
||||||
allowedTags: [],
|
|
||||||
allowedAttributes: []
|
|
||||||
}).replace(/"/g, '"');
|
|
||||||
if (cleanURL !== url) {
|
if (cleanURL !== url) {
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: "The URL you submitted doesn't appear valid"
|
msg: "The URL you submitted doesn't appear valid"
|
||||||
@ -299,44 +318,46 @@ module.exports = function(app) {
|
|||||||
if (url.search(/^https?:\/\//g) === -1) {
|
if (url.search(/^https?:\/\//g) === -1) {
|
||||||
url = 'http://' + url;
|
url = 'http://' + url;
|
||||||
}
|
}
|
||||||
Story.find(
|
|
||||||
{ where: {'link': url} },
|
|
||||||
function(err, story) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
if (story.length) {
|
|
||||||
req.flash('errors', {
|
|
||||||
msg: "Someone's already posted that link. Here's the discussion."
|
|
||||||
});
|
|
||||||
return res.json({
|
|
||||||
alreadyPosted: true,
|
|
||||||
storyURL: '/stories/' + story.pop().storyLink
|
|
||||||
});
|
|
||||||
}
|
|
||||||
utils.getURLTitle(url, processResponse);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
function processResponse(err, story) {
|
findStory({ where: { link: url } })
|
||||||
if (err) {
|
.map(function(stories) {
|
||||||
res.json({
|
if (stories.length) {
|
||||||
|
return {
|
||||||
|
alreadyPosted: true,
|
||||||
|
storyURL: '/stories/' + stories.pop().storyLink
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
alreadyPosted: false,
|
alreadyPosted: false,
|
||||||
storyURL: url,
|
storyURL: url
|
||||||
storyTitle: '',
|
};
|
||||||
storyImage: '',
|
})
|
||||||
storyMetaDescription: ''
|
.flatMap(function(data) {
|
||||||
});
|
if (data.alreadyPosted) {
|
||||||
} else {
|
return Rx.Observable.just(data);
|
||||||
res.json({
|
}
|
||||||
|
return Rx.Observable.fromNodeCallback(getURLTitle)(data.storyURL)
|
||||||
|
.map(function(story) {
|
||||||
|
return {
|
||||||
alreadyPosted: false,
|
alreadyPosted: false,
|
||||||
storyURL: url,
|
storyURL: data.storyURL,
|
||||||
storyTitle: story.title,
|
storyTitle: story.title,
|
||||||
storyImage: story.image,
|
storyImage: story.image,
|
||||||
storyMetaDescription: story.description
|
storyMetaDescription: story.description
|
||||||
|
};
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.subscribe(
|
||||||
|
function(story) {
|
||||||
|
if (story.alreadyPosted) {
|
||||||
|
req.flash('errors', {
|
||||||
|
msg: "Someone's already posted that link. Here's the discussion."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
res.json(story);
|
||||||
|
},
|
||||||
|
next
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function storySubmission(req, res, next) {
|
function storySubmission(req, res, next) {
|
||||||
@ -356,31 +377,29 @@ module.exports = function(app) {
|
|||||||
link = 'http://' + link;
|
link = 'http://' + link;
|
||||||
}
|
}
|
||||||
|
|
||||||
Story.count({
|
var query = {
|
||||||
storyLink: { like: new RegExp('^' + storyLink + '(?: [0-9]+)?$', 'i') }
|
storyLink: {
|
||||||
}, function (err, storyCount) {
|
like: ('^' + storyLink + '(?: [0-9]+)?$'),
|
||||||
if (err) {
|
options: 'i'
|
||||||
return next(err);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var savedStory = countStories(query)
|
||||||
|
.flatMap(function(storyCount) {
|
||||||
// if duplicate storyLink add unique number
|
// if duplicate storyLink add unique number
|
||||||
storyLink = (storyCount === 0) ? storyLink : storyLink + ' ' + storyCount;
|
storyLink = (storyCount === 0) ?
|
||||||
|
storyLink :
|
||||||
|
storyLink + ' ' + storyCount;
|
||||||
|
|
||||||
var link = data.link;
|
var link = data.link;
|
||||||
if (link.search(/^https?:\/\//g) === -1) {
|
if (link.search(/^https?:\/\//g) === -1) {
|
||||||
link = 'http://' + link;
|
link = 'http://' + link;
|
||||||
}
|
}
|
||||||
var story = new Story({
|
var newStory = new Story({
|
||||||
headline: sanitizeHtml(data.headline, {
|
headline: cleanData(data.headline),
|
||||||
allowedTags: [],
|
|
||||||
allowedAttributes: []
|
|
||||||
}).replace(/"/g, '"'),
|
|
||||||
timePosted: Date.now(),
|
timePosted: Date.now(),
|
||||||
link: link,
|
link: link,
|
||||||
description: sanitizeHtml(data.description, {
|
description: cleanData(data.description),
|
||||||
allowedTags: [],
|
|
||||||
allowedAttributes: []
|
|
||||||
}).replace(/"/g, '"'),
|
|
||||||
rank: 1,
|
rank: 1,
|
||||||
upVotes: [({
|
upVotes: [({
|
||||||
upVotedBy: req.user.id,
|
upVotedBy: req.user.id,
|
||||||
@ -398,21 +417,20 @@ module.exports = function(app) {
|
|||||||
metaDescription: data.storyMetaDescription,
|
metaDescription: data.storyMetaDescription,
|
||||||
originalStoryAuthorEmail: req.user.email
|
originalStoryAuthorEmail: req.user.email
|
||||||
});
|
});
|
||||||
story.save(function (err) {
|
return saveInstance(newStory);
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
req.user.progressTimestamps.push(Date.now() || 0);
|
|
||||||
req.user.save(function (err) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
res.send(JSON.stringify({
|
|
||||||
storyLink: story.storyLink.replace(/\s+/g, '-').toLowerCase()
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
req.user.progressTimestamps.push(Date.now());
|
||||||
|
return saveUser(req.user)
|
||||||
|
.flatMap(savedStory)
|
||||||
|
.subscribe(
|
||||||
|
function(story) {
|
||||||
|
res.json({
|
||||||
|
storyLink: dasherize(story.storyLink)
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
next
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function commentSubmit(req, res, next) {
|
function commentSubmit(req, res, next) {
|
||||||
@ -420,11 +438,8 @@ module.exports = function(app) {
|
|||||||
if (!req.user) {
|
if (!req.user) {
|
||||||
return next(new Error('Not authorized'));
|
return next(new Error('Not authorized'));
|
||||||
}
|
}
|
||||||
var sanitizedBody = sanitizeHtml(data.body,
|
var sanitizedBody = cleanData(data.body);
|
||||||
{
|
|
||||||
allowedTags: [],
|
|
||||||
allowedAttributes: []
|
|
||||||
}).replace(/"/g, '"');
|
|
||||||
if (data.body !== sanitizedBody) {
|
if (data.body !== sanitizedBody) {
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: 'HTML is not allowed'
|
msg: 'HTML is not allowed'
|
||||||
@ -449,7 +464,13 @@ module.exports = function(app) {
|
|||||||
commentOn: Date.now()
|
commentOn: Date.now()
|
||||||
});
|
});
|
||||||
|
|
||||||
commentSave(comment, Story, res, next);
|
commentSave(comment, findStoryById).subscribe(
|
||||||
|
function() {},
|
||||||
|
next,
|
||||||
|
function() {
|
||||||
|
res.send(true);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function commentOnCommentSubmit(req, res, next) {
|
function commentOnCommentSubmit(req, res, next) {
|
||||||
@ -458,13 +479,7 @@ module.exports = function(app) {
|
|||||||
return next(new Error('Not authorized'));
|
return next(new Error('Not authorized'));
|
||||||
}
|
}
|
||||||
|
|
||||||
var sanitizedBody = sanitizeHtml(
|
var sanitizedBody = cleanData(data.body);
|
||||||
data.body,
|
|
||||||
{
|
|
||||||
allowedTags: [],
|
|
||||||
allowedAttributes: []
|
|
||||||
}
|
|
||||||
).replace(/"/g, '"');
|
|
||||||
|
|
||||||
if (data.body !== sanitizedBody) {
|
if (data.body !== sanitizedBody) {
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
@ -490,119 +505,101 @@ module.exports = function(app) {
|
|||||||
topLevel: false,
|
topLevel: false,
|
||||||
commentOn: Date.now()
|
commentOn: Date.now()
|
||||||
});
|
});
|
||||||
commentSave(comment, Comment, res, next);
|
commentSave(comment, findCommentById).subscribe(
|
||||||
|
function() {},
|
||||||
|
next,
|
||||||
|
function() {
|
||||||
|
res.send(true);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function commentEdit(req, res, next) {
|
function commentEdit(req, res, next) {
|
||||||
|
findCommentById(req.params.id)
|
||||||
Comment.find({ id: req.params.id }, function(err, cmt) {
|
.doOnNext(function(comment) {
|
||||||
if (err) {
|
if (!req.user && comment.author.userId !== req.user.id) {
|
||||||
return next(err);
|
throw new Error('Not authorized');
|
||||||
}
|
}
|
||||||
cmt = cmt.pop();
|
})
|
||||||
|
.flatMap(function(comment) {
|
||||||
if (!req.user && cmt.author.userId !== req.user.id) {
|
var sanitizedBody = cleanData(req.body.body);
|
||||||
return next(new Error('Not authorized'));
|
|
||||||
}
|
|
||||||
|
|
||||||
var sanitizedBody = sanitizeHtml(req.body.body, {
|
|
||||||
allowedTags: [],
|
|
||||||
allowedAttributes: []
|
|
||||||
}).replace(/"/g, '"');
|
|
||||||
if (req.body.body !== sanitizedBody) {
|
if (req.body.body !== sanitizedBody) {
|
||||||
req.flash('errors', {
|
req.flash('errors', {
|
||||||
msg: 'HTML is not allowed'
|
msg: 'HTML is not allowed'
|
||||||
});
|
});
|
||||||
return res.send(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
cmt.body = sanitizedBody;
|
|
||||||
cmt.commentOn = Date.now();
|
|
||||||
cmt.save(function(err) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
}
|
||||||
|
comment.body = sanitizedBody;
|
||||||
|
comment.commentOn = Date.now();
|
||||||
|
return saveInstance(comment);
|
||||||
|
})
|
||||||
|
.subscribe(
|
||||||
|
function() {
|
||||||
res.send(true);
|
res.send(true);
|
||||||
});
|
},
|
||||||
|
next
|
||||||
});
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function commentSave(comment, Context, res, next) {
|
function commentSave(comment, findContextById) {
|
||||||
comment.save(function(err, data) {
|
return saveInstance(comment)
|
||||||
if (err) {
|
.flatMap(function(comment) {
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Based on the context retrieve the parent
|
// Based on the context retrieve the parent
|
||||||
// object of the comment (Story/Comment)
|
// object of the comment (Story/Comment)
|
||||||
Context.find({
|
return findContextById(comment.associatedPost);
|
||||||
id: data.associatedPost
|
})
|
||||||
}, function (err, associatedContext) {
|
.flatMap(function(associatedContext) {
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
associatedContext = associatedContext.pop();
|
|
||||||
if (associatedContext) {
|
if (associatedContext) {
|
||||||
associatedContext.comments.push(data.id);
|
associatedContext.comments.push(comment.id);
|
||||||
associatedContext.save(function (err) {
|
|
||||||
if (err) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
res.send(true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
// NOTE(berks): saveInstance is safe
|
||||||
|
// it will automatically call onNext with null and onCompleted if
|
||||||
|
// argument is falsey or has no method save
|
||||||
|
return saveInstance(associatedContext);
|
||||||
|
})
|
||||||
|
.flatMap(function(associatedContext) {
|
||||||
// Find the author of the parent object
|
// Find the author of the parent object
|
||||||
User.findOne({
|
// if no username
|
||||||
'profile.username': associatedContext.author.username
|
var username = associatedContext && associatedContext.author ?
|
||||||
}, function(err, recipient) {
|
associatedContext.author.username :
|
||||||
if (err) {
|
null;
|
||||||
return next(err);
|
|
||||||
}
|
var query = { where: { username: username } };
|
||||||
|
return findOneUser(query);
|
||||||
|
})
|
||||||
|
// if no user is found we don't want to hit the doOnNext
|
||||||
|
// filter here will call onCompleted without running through the following
|
||||||
|
// steps
|
||||||
|
.filter(function(user) {
|
||||||
|
return !!user;
|
||||||
|
})
|
||||||
|
// if this is called user is guarenteed to exits
|
||||||
|
// this is a side effect, hence we use do/tap observable methods
|
||||||
|
.doOnNext(function(user) {
|
||||||
// If the emails of both authors differ,
|
// If the emails of both authors differ,
|
||||||
// only then proceed with email notification
|
// only then proceed with email notification
|
||||||
if (
|
if (
|
||||||
typeof data.author !== 'undefined' &&
|
comment.author &&
|
||||||
data.author.email &&
|
comment.author.email &&
|
||||||
typeof recipient !== 'undefined' &&
|
user.email &&
|
||||||
recipient.email &&
|
(comment.author.email !== user.email)
|
||||||
(data.author.email !== recipient.email)
|
|
||||||
) {
|
) {
|
||||||
var transporter = nodemailer.createTransport({
|
sendMailWhillyNilly({
|
||||||
service: 'Mandrill',
|
to: user.email,
|
||||||
auth: {
|
|
||||||
user: secrets.mandrill.user,
|
|
||||||
pass: secrets.mandrill.password
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var mailOptions = {
|
|
||||||
to: recipient.email,
|
|
||||||
from: 'Team@freecodecamp.com',
|
from: 'Team@freecodecamp.com',
|
||||||
subject: data.author.username +
|
subject: comment.author.username +
|
||||||
' replied to your post on Camper News',
|
' replied to your post on Camper News',
|
||||||
text: [
|
text: [
|
||||||
'Just a quick heads-up: ',
|
'Just a quick heads-up: ',
|
||||||
data.author.username + ' replied to you on Camper News.',
|
comment.author.username,
|
||||||
|
' replied to you on Camper News.',
|
||||||
'You can keep this conversation going.',
|
'You can keep this conversation going.',
|
||||||
'Just head back to the discussion here: ',
|
'Just head back to the discussion here: ',
|
||||||
'http://freecodecamp.com/stories/' + data.originalStoryLink,
|
'http://freecodecamp.com/stories/',
|
||||||
|
comment.originalStoryLink,
|
||||||
'- the Free Code Camp Volunteer Team'
|
'- the Free Code Camp Volunteer Team'
|
||||||
].join('\n')
|
].join('\n')
|
||||||
};
|
|
||||||
|
|
||||||
transporter.sendMail(mailOptions, function (err) {
|
|
||||||
if (err) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
return next(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
require('dotenv').load();
|
require('dotenv').load();
|
||||||
var pmx = require('pmx');
|
var pmx = require('pmx');
|
||||||
pmx.init();
|
pmx.init();
|
||||||
// handle uncaught exceptions. Forever will restart process on shutdown
|
|
||||||
|
|
||||||
var R = require('ramda'),
|
var R = require('ramda'),
|
||||||
assign = require('lodash').assign,
|
assign = require('lodash').assign,
|
||||||
@ -188,6 +187,8 @@ app.use(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// track when connecting to db starts
|
||||||
|
var startTime = Date.now();
|
||||||
boot(app, {
|
boot(app, {
|
||||||
appRootDir: __dirname,
|
appRootDir: __dirname,
|
||||||
dev: process.env.NODE_ENV
|
dev: process.env.NODE_ENV
|
||||||
@ -254,10 +255,7 @@ R.keys(passportProviders).map(function(strategy) {
|
|||||||
* 500 Error Handler.
|
* 500 Error Handler.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
if (true) { // eslint-disable-line
|
|
||||||
// NOTE(berks): adding pmx here for Beta test. Remove for production
|
|
||||||
app.use(pmx.expressErrorHandler());
|
|
||||||
app.use(errorHandler({
|
app.use(errorHandler({
|
||||||
log: true
|
log: true
|
||||||
}));
|
}));
|
||||||
@ -301,20 +299,46 @@ if (true) { // eslint-disable-line
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
module.exports = app;
|
||||||
* Start Express server.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
app.start = function () {
|
||||||
app.listen(app.get('port'), function() {
|
app.listen(app.get('port'), function() {
|
||||||
console.log(
|
console.log(
|
||||||
'FreeCodeCamp server listening on port %d in %s mode',
|
'FreeCodeCamp server listening on port %d in %s mode',
|
||||||
app.get('port'),
|
app.get('port'),
|
||||||
app.get('env')
|
app.get('env')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// start the server if `$ node server.js`
|
// start the server if `$ node server.js`
|
||||||
|
if (require.main === module) {
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
var timeoutHandler;
|
||||||
|
console.log('waiting for db to connect');
|
||||||
|
|
||||||
|
var onConnect = function() {
|
||||||
|
console.log('db connected in %s ms', Date.now() - startTime);
|
||||||
|
if (timeoutHandler) {
|
||||||
|
clearTimeout(timeoutHandler);
|
||||||
|
}
|
||||||
|
app.start();
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = app;
|
var timeoutHandler = setTimeout(function() {
|
||||||
|
var message =
|
||||||
|
'db did not after ' +
|
||||||
|
(Date.now() - startTime) +
|
||||||
|
' ms connect crashing hard';
|
||||||
|
|
||||||
|
console.log(message);
|
||||||
|
// purposely shutdown server
|
||||||
|
// pm2 should restart this in production
|
||||||
|
throw new Error(message);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
app.dataSources.db.on('connected', onConnect);
|
||||||
|
} else {
|
||||||
|
app.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,23 +1,5 @@
|
|||||||
[{
|
[
|
||||||
"name": "App Academy",
|
{
|
||||||
"cost": "18000",
|
|
||||||
"weeks": "12",
|
|
||||||
"finance": false,
|
|
||||||
"housing": "500",
|
|
||||||
"cities": [
|
|
||||||
"new-york-city",
|
|
||||||
"san-francisco"
|
|
||||||
]
|
|
||||||
}, {
|
|
||||||
"name": "Viking Code School",
|
|
||||||
"cost": "18000",
|
|
||||||
"weeks": "14",
|
|
||||||
"housing": "0",
|
|
||||||
"finance": false,
|
|
||||||
"cities": [
|
|
||||||
"online"
|
|
||||||
]
|
|
||||||
}, {
|
|
||||||
"name": "Hack Reactor",
|
"name": "Hack Reactor",
|
||||||
"cost": "17780",
|
"cost": "17780",
|
||||||
"housing": "500",
|
"housing": "500",
|
||||||
|
@ -20,11 +20,6 @@ var allFieldGuideIds, allFieldGuideNames, allNonprofitNames,
|
|||||||
challengeMapWithNames, allChallengeIds,
|
challengeMapWithNames, allChallengeIds,
|
||||||
challengeMapWithDashedNames;
|
challengeMapWithDashedNames;
|
||||||
|
|
||||||
/**
|
|
||||||
* GET /
|
|
||||||
* Resources.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Array.zip = function(left, right, combinerFunction) {
|
Array.zip = function(left, right, combinerFunction) {
|
||||||
var counter,
|
var counter,
|
||||||
results = [];
|
results = [];
|
||||||
@ -60,6 +55,17 @@ Array.zip = function(left, right, combinerFunction) {
|
|||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
dasherize: function dasherize(name) {
|
||||||
|
return ('' + name)
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s/g, '-')
|
||||||
|
.replace(/[^a-z0-9\-\.]/gi, '');
|
||||||
|
},
|
||||||
|
|
||||||
|
unDasherize: function unDasherize(name) {
|
||||||
|
return ('' + name).replace(/\-/g, ' ').trim();
|
||||||
|
},
|
||||||
|
|
||||||
getChallengeMapForDisplay: function () {
|
getChallengeMapForDisplay: function () {
|
||||||
if (!challengeMapForDisplay) {
|
if (!challengeMapForDisplay) {
|
||||||
challengeMapForDisplay = {};
|
challengeMapForDisplay = {};
|
||||||
@ -188,11 +194,17 @@ module.exports = {
|
|||||||
return process.env.NODE_ENV;
|
return process.env.NODE_ENV;
|
||||||
},
|
},
|
||||||
|
|
||||||
getURLTitle: function (url, callback) {
|
getURLTitle: function(url, callback) {
|
||||||
(function () {
|
var result = {
|
||||||
var result = {title: '', image: '', url: '', description: ''};
|
title: '',
|
||||||
request(url, function (error, response, body) {
|
image: '',
|
||||||
if (!error && response.statusCode === 200) {
|
url: '',
|
||||||
|
description: ''
|
||||||
|
};
|
||||||
|
request(url, function(err, response, body) {
|
||||||
|
if (err || response.statusCode !== 200) {
|
||||||
|
return callback(new Error('failed'));
|
||||||
|
}
|
||||||
var $ = cheerio.load(body);
|
var $ = cheerio.load(body);
|
||||||
var metaDescription = $("meta[name='description']");
|
var metaDescription = $("meta[name='description']");
|
||||||
var metaImage = $("meta[property='og:image']");
|
var metaImage = $("meta[property='og:image']");
|
||||||
@ -212,11 +224,7 @@ module.exports = {
|
|||||||
result.image = urlImage;
|
result.image = urlImage;
|
||||||
result.description = description;
|
result.description = description;
|
||||||
callback(null, result);
|
callback(null, result);
|
||||||
} else {
|
|
||||||
callback(new Error('failed'));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
})();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getMDNLinks: function(links) {
|
getMDNLinks: function(links) {
|
||||||
|
@ -43,3 +43,11 @@ exports.ifNoUserRedirectTo = function ifNoUserRedirectTo(url) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.ifNoUserSend = function ifNoUserSend(sendThis) {
|
||||||
|
return function(req, res, next) {
|
||||||
|
if (req.user) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
return res.status(200).send(sendThis);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -1,25 +1,32 @@
|
|||||||
var Rx = require('rx');
|
var Rx = require('rx');
|
||||||
var debug = require('debug')('freecc:rxUtils');
|
var debug = require('debug')('freecc:rxUtils');
|
||||||
|
|
||||||
exports.saveUser = function saveUser(user) {
|
exports.saveInstance = function saveInstance(instance) {
|
||||||
return new Rx.Observable.create(function(observer) {
|
return new Rx.Observable.create(function(observer) {
|
||||||
if (!user || typeof user.save !== 'function') {
|
if (!instance || typeof instance.save !== 'function') {
|
||||||
debug('no user or save method');
|
debug('no instance or save method');
|
||||||
observer.onNext();
|
observer.onNext();
|
||||||
return observer.onCompleted();
|
return observer.onCompleted();
|
||||||
}
|
}
|
||||||
user.save(function(err, savedUser) {
|
instance.save(function(err, savedInstance) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return observer.onError(err);
|
return observer.onError(err);
|
||||||
}
|
}
|
||||||
debug('user saved');
|
debug('instance saved');
|
||||||
observer.onNext(savedUser);
|
observer.onNext(savedInstance);
|
||||||
observer.onCompleted();
|
observer.onCompleted();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// alias saveInstance
|
||||||
|
exports.saveUser = exports.saveInstance;
|
||||||
|
|
||||||
exports.observableQueryFromModel =
|
exports.observableQueryFromModel =
|
||||||
function observableQueryFromModel(Model, method, query) {
|
function observableQueryFromModel(Model, method, query) {
|
||||||
return Rx.Observable.fromNodeCallback(Model[method], Model)(query);
|
return Rx.Observable.fromNodeCallback(Model[method], Model)(query);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.observeMethod = function observeMethod(Model, method) {
|
||||||
|
return Rx.Observable.fromNodeCallback(Model[method], Model);
|
||||||
|
};
|
||||||
|
@ -98,21 +98,21 @@ block content
|
|||||||
li.large-p.negative-10
|
li.large-p.negative-10
|
||||||
a(href="/challenges/#{challenge.dashedName}")= challenge.name
|
a(href="/challenges/#{challenge.dashedName}")= challenge.name
|
||||||
|
|
||||||
#announcementModal.modal(tabindex='-1')
|
//#announcementModal.modal(tabindex='-1')
|
||||||
.modal-dialog.animated.fadeInUp.fast-animation
|
// .modal-dialog.animated.fadeInUp.fast-animation
|
||||||
.modal-content
|
// .modal-content
|
||||||
.modal-header.challenge-list-header Add us to your LinkedIn profile
|
// .modal-header.challenge-list-header Add us to your LinkedIn profile
|
||||||
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
|
// a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
|
||||||
.modal-body
|
// .modal-body
|
||||||
h3.text-left LinkedIn now recognizes Free Code Camp as a university.
|
// h3.text-left LinkedIn now recognizes Free Code Camp as a university.
|
||||||
img.img-responsive.img-center(src='https://www.evernote.com/l/AHTzkHwtg-BHj57bqqDL7WFF8WgrI5V8cxwB/image.png')
|
// img.img-responsive.img-center(src='https://www.evernote.com/l/AHTzkHwtg-BHj57bqqDL7WFF8WgrI5V8cxwB/image.png')
|
||||||
h3.text-left It takes less than a minute to add Free Code Camp to your LinkedIn profile.
|
// h3.text-left It takes less than a minute to add Free Code Camp to your LinkedIn profile.
|
||||||
a.btn.btn-lg.btn-info.btn-block(name='_csrf', value=_csrf, aria-hidden='true', href='/linkedin', target='_blank') Show me how to do this
|
// a.btn.btn-lg.btn-info.btn-block(name='_csrf', value=_csrf, aria-hidden='true', href='/linkedin', target='_blank') Show me how to do this
|
||||||
a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Thanks for the heads-up
|
// a.btn.btn-lg.btn-primary.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Thanks for the heads-up
|
||||||
script.
|
//script.
|
||||||
$(document).ready(function () {
|
// $(document).ready(function () {
|
||||||
if (!localStorage || !localStorage.linkedIn) {
|
// if (!localStorage || !localStorage.linkedIn) {
|
||||||
$('#announcementModal').modal('show');
|
// $('#announcementModal').modal('show');
|
||||||
localStorage.linkedIn = "true";
|
// localStorage.linkedIn = "true";
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
@ -84,14 +84,9 @@ block content
|
|||||||
label.negative-10.btn.btn-primary.btn-block#submitButton
|
label.negative-10.btn.btn-primary.btn-block#submitButton
|
||||||
i.fa.fa-play
|
i.fa.fa-play
|
||||||
| Run code (ctrl + enter)
|
| Run code (ctrl + enter)
|
||||||
#trigger-reset-modal.btn.btn-danger.btn-big.btn-block(data-toggle='modal', data-target='#reset-modal', data-backdrop='true') Reset Code
|
|
||||||
if (user && user.sentSlackInvite)
|
|
||||||
.button-spacer
|
.button-spacer
|
||||||
.btn-group.input-group.btn-group-justified
|
.btn-group.input-group.btn-group-justified
|
||||||
label.btn.btn-success#trigger-help-modal
|
label.btn.btn-success#trigger-reset-modal
|
||||||
i.fa.fa-refresh
|
|
||||||
| Reset
|
|
||||||
label.btn.btn-success#trigger-help-modal
|
|
||||||
i.fa.fa-refresh
|
i.fa.fa-refresh
|
||||||
| Reset
|
| Reset
|
||||||
label.btn.btn-success#trigger-help-modal
|
label.btn.btn-success#trigger-help-modal
|
||||||
|
@ -33,8 +33,6 @@ block content
|
|||||||
label.btn.btn-primary.btn-block.negative-10#next-courseware-button
|
label.btn.btn-primary.btn-block.negative-10#next-courseware-button
|
||||||
.ion-checkmark-circled
|
.ion-checkmark-circled
|
||||||
| Go to my next challenge (ctrl + enter)
|
| Go to my next challenge (ctrl + enter)
|
||||||
|
|
||||||
if (user.sentSlackInvite)
|
|
||||||
.button-spacer
|
.button-spacer
|
||||||
.btn-group.input-group.btn-group-justified
|
.btn-group.input-group.btn-group-justified
|
||||||
label.btn.btn-success#trigger-help-modal
|
label.btn.btn-success#trigger-help-modal
|
||||||
@ -43,10 +41,10 @@ block content
|
|||||||
label.btn.btn-success#trigger-issue-modal
|
label.btn.btn-success#trigger-issue-modal
|
||||||
i.fa.fa-bug
|
i.fa.fa-bug
|
||||||
| Bug
|
| Bug
|
||||||
.button-spacer
|
|
||||||
script.
|
script.
|
||||||
var userLoggedIn = true;
|
var userLoggedIn = true;
|
||||||
else
|
else
|
||||||
|
.button-spacer
|
||||||
a.btn.signup-btn.btn-block.btn-block.negative-15(href='/login') Sign in so you can save your progress
|
a.btn.signup-btn.btn-block.btn-block.negative-15(href='/login') Sign in so you can save your progress
|
||||||
script.
|
script.
|
||||||
var userLoggedIn = false;
|
var userLoggedIn = false;
|
||||||
|
@ -35,7 +35,6 @@ block content
|
|||||||
span.ion-arrow-up-b
|
span.ion-arrow-up-b
|
||||||
| Less information
|
| Less information
|
||||||
#submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter)
|
#submitButton.btn.btn-primary.btn-big.btn-block Run code (ctrl + enter)
|
||||||
if (user && user.sentSlackInvite)
|
|
||||||
.button-spacer
|
.button-spacer
|
||||||
.btn-group.input-group.btn-group-justified
|
.btn-group.input-group.btn-group-justified
|
||||||
label.btn.btn-success#trigger-help-modal
|
label.btn.btn-success#trigger-help-modal
|
||||||
|
@ -21,9 +21,8 @@ block content
|
|||||||
script.
|
script.
|
||||||
var userLoggedIn = true;
|
var userLoggedIn = true;
|
||||||
.button-spacer
|
.button-spacer
|
||||||
if (user.sentSlackInvite)
|
|
||||||
.btn-group.input-group.btn-group-justified
|
.btn-group.input-group.btn-group-justified
|
||||||
.btn.btn-success.btn-big#trigger-help-editorless-modal
|
.btn.btn-success.btn-big#trigger-help-modal-modal
|
||||||
i.fa.fa-medkit
|
i.fa.fa-medkit
|
||||||
| Get help
|
| Get help
|
||||||
.btn.btn-success.btn-big#trigger-issue-modal
|
.btn.btn-success.btn-big#trigger-issue-modal
|
||||||
|
@ -18,10 +18,9 @@ block content
|
|||||||
br
|
br
|
||||||
if (user)
|
if (user)
|
||||||
a.btn.btn-primary.btn-big.btn-block#completed-zipline-or-basejump I've completed this challenge (ctrl + enter)
|
a.btn.btn-primary.btn-big.btn-block#completed-zipline-or-basejump I've completed this challenge (ctrl + enter)
|
||||||
if (user.sentSlackInvite)
|
|
||||||
.button-spacer
|
.button-spacer
|
||||||
.btn-group.input-group.btn-group-justified
|
.btn-group.input-group.btn-group-justified
|
||||||
.btn.btn-success.btn-big#trigger-help-editorless-modal
|
.btn.btn-success.btn-big#trigger-help-modal
|
||||||
i.fa.fa-medkit
|
i.fa.fa-medkit
|
||||||
| Help
|
| Help
|
||||||
.btn.btn-success.btn-big#trigger-pair-modal
|
.btn.btn-success.btn-big#trigger-pair-modal
|
||||||
|
@ -12,10 +12,10 @@ block content
|
|||||||
.text-center
|
.text-center
|
||||||
if user && user.uncompletedFieldGuides.length > 0
|
if user && user.uncompletedFieldGuides.length > 0
|
||||||
.next-field-guide-button.btn.btn-primary.btn-big.btn-block Next article (ctrl + enter)
|
.next-field-guide-button.btn.btn-primary.btn-big.btn-block Next article (ctrl + enter)
|
||||||
.ten-pixel-break
|
.button-spacer
|
||||||
a.btn.btn-info.btn-big.btn-block(href='/field-guide/all-articles') Show me all articles
|
a.btn.btn-info.btn-big.btn-block(href='/field-guide/all-articles') Show me all articles
|
||||||
if !user
|
if !user
|
||||||
.ten-pixel-break
|
.button-spacer
|
||||||
a.btn.btn-big.signup-btn.btn-block(href='/login') Start learning to code (it's free)
|
a.btn.btn-big.signup-btn.btn-block(href='/login') Start learning to code (it's free)
|
||||||
.spacer
|
.spacer
|
||||||
#fieldGuideId.hidden= fieldGuideId
|
#fieldGuideId.hidden= fieldGuideId
|
||||||
|
@ -1,19 +1,6 @@
|
|||||||
extends layout
|
extends layout
|
||||||
block content
|
block content
|
||||||
.jumbotron
|
.jumbotron
|
||||||
if (user && user.progressTimestamps.length > 0)
|
|
||||||
.col-xs-12
|
|
||||||
.embed-responsive.embed-responsive-16by9
|
|
||||||
iframe.embed-responsive-item(src='//player.vimeo.com/video/129168884')
|
|
||||||
br
|
|
||||||
h3 Note: If you're using Firefox and the buttons aren't working, do a full refresh (control + f5 on Windows and command + shift + r on Mac). We're working on this.
|
|
||||||
h3 Note: If you've already completed the HTML, CSS, Bootstrap Waypoints, you do not need to do these new Waypoints.
|
|
||||||
h3 Note: If you were already doing full stack JavaScript Waypoints (Node.js, Angular.js), go ahead and start the Bonfires (Basic Algorithm Scripting).
|
|
||||||
h3 Thanks for your patience everyone! We're confident these curriculum improvements will better prepare you for your nonprofit projects and for the workplace.
|
|
||||||
br
|
|
||||||
br
|
|
||||||
br
|
|
||||||
br
|
|
||||||
.text-center
|
.text-center
|
||||||
h1.hug-top Code with Us
|
h1.hug-top Code with Us
|
||||||
h2 Let's learn to code by building projects for nonprofits
|
h2 Let's learn to code by building projects for nonprofits
|
||||||
|
@ -7,6 +7,8 @@ block content
|
|||||||
.panel-heading.text-center Nonprofits We Help
|
.panel-heading.text-center Nonprofits We Help
|
||||||
.panel-body
|
.panel-body
|
||||||
.col-xs-12.col-sm-12.col-md-10.col-md-offset-1
|
.col-xs-12.col-sm-12.col-md-10.col-md-offset-1
|
||||||
|
h1.text-primary.text-center Our campers have saved nonprofits $#{totalSavings}.
|
||||||
|
.spacer
|
||||||
for nonprofit in nonprofits
|
for nonprofit in nonprofits
|
||||||
.spacer
|
.spacer
|
||||||
.row
|
.row
|
||||||
@ -15,8 +17,10 @@ block content
|
|||||||
.col-xs-12.col-sm-9
|
.col-xs-12.col-sm-9
|
||||||
h2.negative-15= nonprofit.name
|
h2.negative-15= nonprofit.name
|
||||||
h3.negative-15= nonprofit.whatDoesNonprofitDo
|
h3.negative-15= nonprofit.whatDoesNonprofitDo
|
||||||
|
if (nonprofit.moneySaved > 0)
|
||||||
|
h4.negative-15.text-primary Estimated Cost Savings for Nonprofit: $#{nonprofit.moneySaved.toString().replace(/000$/, ',000')}
|
||||||
a.text-center.btn.btn-primary.btn-lg(href='/nonprofits/' + nonprofit.name.toLowerCase().replace(/\s/g, '-')) Read more
|
a.text-center.btn.btn-primary.btn-lg(href='/nonprofits/' + nonprofit.name.toLowerCase().replace(/\s/g, '-')) Read more
|
||||||
|
.spacer
|
||||||
.col-xs-12.col-sm-8.col-sm-offset-2
|
.col-xs-12.col-sm-8.col-sm-offset-2
|
||||||
if (!user)
|
if (!user)
|
||||||
a.btn.btn-cta.signup-btn.btn-block(href="/nonprofits-form") My nonprofit needs coding help
|
a.btn.btn-cta.signup-btn.btn-block(href="/nonprofits-form") My nonprofit needs coding help
|
||||||
|
@ -43,6 +43,9 @@ block content
|
|||||||
if (approvedOther)
|
if (approvedOther)
|
||||||
.ion-settings Other tools
|
.ion-settings Other tools
|
||||||
h3 Project Status: #{currentStatus}
|
h3 Project Status: #{currentStatus}
|
||||||
|
if (moneySaved > 0)
|
||||||
|
h3.text-primary Estimated Cost Savings for Nonprofit: $#{moneySaved.toString().replace(/000$/, ',000')}
|
||||||
|
|
||||||
if (interestedCampers && interestedCampers.length > 0)
|
if (interestedCampers && interestedCampers.length > 0)
|
||||||
h3 Interested campers:
|
h3 Interested campers:
|
||||||
.col-xs-12.text-left
|
.col-xs-12.text-left
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
.modal-header.challenge-list-header Ready to pair program?
|
.modal-header.challenge-list-header Ready to pair program?
|
||||||
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
|
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
|
||||||
.modal-body.text-center
|
.modal-body.text-center
|
||||||
h3 This will create a pair programming request.
|
h3 This will take you to our pair programming room where you can request a pair.
|
||||||
h3 You'll need  
|
h3 You'll need  
|
||||||
a(href='/field-guide/how-do-i-install-screenhero' target='_blank') Screen Hero
|
a(href='/field-guide/how-do-i-install-screenhero' target='_blank') Screen Hero
|
||||||
| .
|
| .
|
||||||
h3 Other campers may then message you about pair programming.
|
h3 Other campers may then message you about pair programming.
|
||||||
a.btn.btn-lg.btn-primary.btn-block#i-want-to-pair(name='_csrf', value=_csrf) Create my pair request
|
a.btn.btn-lg.btn-primary.btn-block(href='https://gitter.im/FreeCodeCamp/LetsPair', data-dismiss='modal', aria-hidden='true' target='_blank') Take me to the pair programming room
|
||||||
a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
|
a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
|
||||||
|
|
||||||
#issue-modal.modal(tabindex='-1')
|
#issue-modal.modal(tabindex='-1')
|
||||||
@ -33,20 +33,6 @@
|
|||||||
a(href='/field-guide/how-do-i-get-help-when-i-get-stuck' target='_blank') RSAP
|
a(href='/field-guide/how-do-i-get-help-when-i-get-stuck' target='_blank') RSAP
|
||||||
| .
|
| .
|
||||||
h3 If you've already read the errors and searched Google, you should ask for help.
|
h3 If you've already read the errors and searched Google, you should ask for help.
|
||||||
h3 This will open a help request in our Help chat room.
|
h3 This will take you to our help room.
|
||||||
a.btn.btn-lg.btn-primary.btn-block#i-want-help(name='_csrf', value=_csrf) Ask for help
|
a.btn.btn-lg.btn-primary.btn-block(href='https://gitter.im/FreeCodeCamp/LetsPair', data-dismiss='modal', aria-hidden='true' target='_blank') Take me to the help room
|
||||||
a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
|
|
||||||
|
|
||||||
#help-editorless-modal.modal(tabindex='-1')
|
|
||||||
.modal-dialog.animated.zoomIn.fast-animation
|
|
||||||
.modal-content
|
|
||||||
.modal-header.challenge-list-header Need some help?
|
|
||||||
a.close.closing-x(href='#', data-dismiss='modal', aria-hidden='true') ×
|
|
||||||
.modal-body.text-center
|
|
||||||
h3 Remember to use  
|
|
||||||
a(href='/field-guide/how-do-i-get-help-when-i-get-stuck' target='_blank') RSAP
|
|
||||||
| .
|
|
||||||
h3 If you've already read the errors and searched Google, you should ask for help.
|
|
||||||
h3 This will open a help request in our Help chat room.
|
|
||||||
a.btn.btn-lg.btn-primary.btn-block#i-want-help-editorless(name='_csrf', value=_csrf) Ask for help
|
|
||||||
a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
|
a.btn.btn-lg.btn-info.btn-block(href='#', data-dismiss='modal', aria-hidden='true') Cancel
|
||||||
|
@ -16,7 +16,7 @@ nav.navbar.navbar-default.navbar-fixed-top.nav-height
|
|||||||
li
|
li
|
||||||
a(href='//gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') Chat
|
a(href='//gitter.im/FreeCodeCamp/FreeCodeCamp', target='_blank') Chat
|
||||||
li
|
li
|
||||||
a(href='/stories') News
|
a(href='/news') News
|
||||||
li
|
li
|
||||||
a(href='/field-guide') Guide
|
a(href='/field-guide') Guide
|
||||||
li
|
li
|
||||||
|
@ -32,7 +32,7 @@ script.
|
|||||||
window.moment || document.write('<script src="/bower_components/moment/min/moment.min.js"><\/script>');
|
window.moment || document.write('<script src="/bower_components/moment/min/moment.min.js"><\/script>');
|
||||||
|
|
||||||
// Leave the below lines alone!
|
// Leave the below lines alone!
|
||||||
script(src="/js/main_0.0.2.js")
|
script(src="/js/main_0.0.3.js")
|
||||||
|
|
||||||
script(src="/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js")
|
script(src="/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js")
|
||||||
|
|
||||||
|
75
server/views/sponsors/sponsors.jade
Normal file
75
server/views/sponsors/sponsors.jade
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
extends ../layout
|
||||||
|
block content
|
||||||
|
script.
|
||||||
|
.panel.panel-info
|
||||||
|
.panel-heading.text-center
|
||||||
|
h1 These Sponsors make Free Code Camp possible
|
||||||
|
.panel-body
|
||||||
|
img.img-responsive.img-center.border-radius-5(src='https://s3.amazonaws.com/freecodecamp/wide-social-banner.png')
|
||||||
|
.spacer
|
||||||
|
.row
|
||||||
|
.col-xs-12.col-md-10.col-md-offset-1
|
||||||
|
h1 Interested in becoming a sponsor? Call us any time at +1 888-888-8888
|
||||||
|
.spacer
|
||||||
|
h1.text-center Platinum Sponsors
|
||||||
|
.row
|
||||||
|
.col-xs-6
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-12.col-sm-10.col-md-8.col-sm-offset-1.col-md-offset-2.negative-35
|
||||||
|
h3
|
||||||
|
a(href='#' target='_blank') Name of Company
|
||||||
|
p.large-p Sagittis 9 lives climb the curtains lady tincidunt sleep on your face odd consequat lobortis erat eu? Malesuada eros, ac facilisis waffles reddit cute id together lacinia lived faucibus! Sed, sleeping watched diam cat ut mercedes litora? Meet cute aptent lacinia, eu likes quam neighbors tincidunt scelerisque quis loves she.
|
||||||
|
.col-xs-6
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-12.col-sm-10.col-md-8.col-sm-offset-1.col-md-offset-2.negative-35
|
||||||
|
h3
|
||||||
|
a(href='#' target='_blank') Name of Company
|
||||||
|
p.large-p Sagittis 9 lives climb the curtains lady tincidunt sleep on your face odd consequat lobortis erat eu? Malesuada eros, ac facilisis waffles reddit cute id together lacinia lived faucibus! Sed, sleeping watched diam cat ut mercedes litora? Meet cute aptent lacinia, eu likes quam neighbors tincidunt scelerisque quis loves she.
|
||||||
|
|
||||||
|
h1.text-center Gold Sponsors
|
||||||
|
.row
|
||||||
|
.col-xs-4
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-12.col-sm-10.col-sm-offset-1.negative-35
|
||||||
|
h3
|
||||||
|
a(href='#' target='_blank') Name of Company
|
||||||
|
p.large-p Sagittis 9 lives climb the curtains lady tincidunt sleep on your face odd consequat lobortis erat eu? Malesuada eros, ac facilisis waffles reddit cute id together lacinia lived faucibus! Sed, sleeping watched diam cat ut mercedes litora? Meet cute aptent lacinia, eu likes quam neighbors tincidunt scelerisque quis loves she.
|
||||||
|
.col-xs-4
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-12.col-sm-10.col-sm-offset-1.negative-35
|
||||||
|
h3
|
||||||
|
a(href='#' target='_blank') Name of Company
|
||||||
|
p.large-p Sagittis 9 lives climb the curtains lady tincidunt sleep on your face odd consequat lobortis erat eu? Malesuada eros, ac facilisis waffles reddit cute id together lacinia lived faucibus! Sed, sleeping watched diam cat ut mercedes litora? Meet cute aptent lacinia, eu likes quam neighbors tincidunt scelerisque quis loves she.
|
||||||
|
.col-xs-4
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-12.col-sm-10.col-sm-offset-1.negative-35
|
||||||
|
h3
|
||||||
|
a(href='#' target='_blank') Name of Company
|
||||||
|
p.large-p Sagittis 9 lives climb the curtains lady tincidunt sleep on your face odd consequat lobortis erat eu? Malesuada eros, ac facilisis waffles reddit cute id together lacinia lived faucibus! Sed, sleeping watched diam cat ut mercedes litora? Meet cute aptent lacinia, eu likes quam neighbors tincidunt scelerisque quis loves she.
|
||||||
|
|
||||||
|
h1.text-center Silver Sponsors
|
||||||
|
.row
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
||||||
|
.col-xs-2
|
||||||
|
img.img-responsive.img-center(src='http://placekitten.com/200/300')
|
@ -11,15 +11,16 @@
|
|||||||
input#searchArea.big-text-field.field-responsive.form-control(type='text', placeholder='Search our links')
|
input#searchArea.big-text-field.field-responsive.form-control(type='text', placeholder='Search our links')
|
||||||
span.input-group-btn
|
span.input-group-btn
|
||||||
button#searchbutton.btn.btn-big.btn-primary.btn-responsive(type='button') Search
|
button#searchbutton.btn.btn-big.btn-primary.btn-responsive(type='button') Search
|
||||||
|
.spacer
|
||||||
|
|
||||||
.spacer
|
//.spacer
|
||||||
.row
|
//.row
|
||||||
.col-xs-12.col-sm-8.col-sm-offset-2.well
|
// .col-xs-12.col-sm-8.col-sm-offset-2.well
|
||||||
h4.text-center Which other free resources do you use?
|
// h4.text-center Which other free resources do you use?
|
||||||
img.img-responsive(src='https://www.evernote.com/l/AHRNhlwViM1Kh5qCm6iy7MSWrbdyxYbRkWkB/image.png')
|
// img.img-responsive(src='https://www.evernote.com/l/AHRNhlwViM1Kh5qCm6iy7MSWrbdyxYbRkWkB/image.png')
|
||||||
p Link us to your favorite free coding resources.
|
// p Link us to your favorite free coding resources.
|
||||||
p Use the headline: "Awesome Free Resource: (the name of the book, podcast, or video series)". We'll publish a list of the 25 most-upvoted resources (and the campers who submitted them) in Wednesday's blog post, and in an upcoming Field Guide article. Also - as always - you'll get 1 point every time someone upvotes your post.
|
// p Use the headline: "Awesome Free Resource: (the name of the book, podcast, or video series)". We'll publish a list of the 25 most-upvoted resources (and the campers who submitted them) in Wednesday's blog post, and in an upcoming Field Guide article. Also - as always - you'll get 1 point every time someone upvotes your post.
|
||||||
.spacer
|
//.spacer
|
||||||
|
|
||||||
#search-results
|
#search-results
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user