Skip to content

Commit

Permalink
Add language routing
Browse files Browse the repository at this point in the history
  • Loading branch information
Berkeley Martinez authored and Berkeley Martinez committed Jul 29, 2016
1 parent ee4f1db commit 078560c
Show file tree
Hide file tree
Showing 18 changed files with 305 additions and 79 deletions.
6 changes: 3 additions & 3 deletions common/app/routes/challenges/components/map/Block.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import LangLink from '../../../../utils/Language-Link.jsx';
import { connect } from 'react-redux';
import FA from 'react-fontawesome';
import PureComponent from 'react-pure-render/component';
Expand Down Expand Up @@ -54,7 +54,7 @@ export class Block extends PureComponent {
className={ challengeClassName }
key={ title }
>
<Link to={ `/challenges/${blockName}/${dashedName}` }>
<LangLink to={ `/challenges/${blockName}/${dashedName}` }>
<span
onClick={ () => updateCurrentChallenge(challenge) }
>
Expand All @@ -66,7 +66,7 @@ export class Block extends PureComponent {
''
}
</span>
</Link>
</LangLink>
</p>
);
});
Expand Down
5 changes: 4 additions & 1 deletion common/app/routes/challenges/redux/fetch-challenges-saga.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default function fetchChallengesSaga(action$, getState, { services }) {
))
.flatMap(({ type, payload: { dashedName, block } = {} }) => {
const state = getState();
const lang = state.app.languageTag;
if (type === replaceChallenge) {
const { challenge: newChallenge } = challengeSelector({
...state,
Expand All @@ -38,8 +39,10 @@ export default function fetchChallengesSaga(action$, getState, { services }) {
return Observable.just(null);
}
const options = { service: 'map' };
options.params = { lang };
if (type === fetchChallenge) {
options.params = { dashedName, block };
options.params.dashedName = dashedName;
options.params.block = block;
}
return services.readService$(options)
.flatMap(({ entities, result, redirect } = {}) => {
Expand Down
2 changes: 1 addition & 1 deletion common/app/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { modernChallenges, map, challenges } from './challenges';
import NotFound from '../components/NotFound/index.jsx';

export default {
path: '/',
path: '/:lang',
childRoutes: [
challenges,
modernChallenges,
Expand Down
42 changes: 42 additions & 0 deletions common/app/utils/Language-Link.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import supportedLanguages from '../../utils/supported-languages';

const toLowerCase = String.prototype.toLowerCase;
function addLang(url, lang) {
const maybeLang = toLowerCase.call(url.split('/')[1]);
if (supportedLanguages[maybeLang]) {
return url;
}
if (supportedLanguages[lang]) {
return `/${lang}${url}`;
}
return `/en${url}`;
}

const mapStateToProps = state => ({ lang: state.app.lang });

export class LangLink extends React.Component {
static displayName = 'LangLink';
static propTypes = {
to: PropTypes.string,
lang: PropTypes.string
};

render() {
const {
to,
lang,
...props
} = this.props;
return (
<Link
to={ addLang(to, lang) }
{ ...props }
/>
);
}
}

export default connect(mapStateToProps)(LangLink);
12 changes: 12 additions & 0 deletions common/models/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@
"theme": {
"type": "string",
"default": "default"
},
"languageTag": {
"type": "string",
"description": "A IETF language tag",
"default": "en"
}
},
"validations": [],
Expand Down Expand Up @@ -256,6 +261,13 @@
"principalId": "$owner",
"permission": "ALLOW",
"property": "updateTheme"
},
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": "updateLanguage"
}
],
"methods": {}
Expand Down
4 changes: 4 additions & 0 deletions common/utils/supported-languages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
en: 'English',
es: 'Spanish'
};
2 changes: 1 addition & 1 deletion server/boot/about.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,5 @@ export default function about(app) {
}

router.get('/about', showAbout);
app.use(router);
app.use('/:lang', router);
}
8 changes: 5 additions & 3 deletions server/boot/commit.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ function findNonprofit(name) {

export default function commit(app) {
const router = app.loopback.Router();
const api = app.loopback.Router();
const { Pledge } = app.models;

router.get(
Expand All @@ -68,19 +69,20 @@ export default function commit(app) {
renderDirectory
);

router.post(
api.post(
'/commit/stop-commitment',
sendNonUserToCommit,
stopCommit
);

router.post(
api.post(
'/commit/complete-goal',
sendNonUserToCommit,
completeCommitment
);

app.use(router);
app.use(api);
app.use(':/lang', router);

function commitToNonprofit(req, res, next) {
const { user } = req;
Expand Down
28 changes: 21 additions & 7 deletions server/boot/home.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
import { defaultProfileImage } from '../../common/utils/constantStrings.json';
import supportedLanguages from '../../common/utils/supported-languages';

const message =
'Learn to Code and Help Nonprofits';

module.exports = function(app) {
var router = app.loopback.Router();
router.get('/', addDefaultImage, index);

app.use(
'/:lang',
(req, res, next) => {
// add url language to request for all routers
req._urlLang = req.params.lang;
next();
},
router
);
app.use(router);

function addDefaultImage(req, res, next) {
if (!req.user || req.user.picture) {
return next();
}
req.user.picture = defaultProfileImage;
return req.user.save(function(err) {
if (err) { return next(err); }
return next();
});
return req.user.update$({ picture: defaultProfileImage })
.subscribe(
() => next(),
next
);
}

function index(req, res) {
function index(req, res, next) {
if (!supportedLanguages[req._urlLang]) {
return next();
}

if (req.user) {
return res.redirect('/challenges/current-challenge');
}

return res.render('home', { title: message });
}
};
49 changes: 24 additions & 25 deletions server/boot/randomAPIs.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,32 @@ import secrets from '../../config/secrets';
module.exports = function(app) {
const router = app.loopback.Router();
const User = app.models.User;
router.get('/api/github', githubCalls);
router.get('/chat', chat);
router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
router.get('/twitch', twitch);
router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
const noLangRouter = app.loopback.Router();
noLangRouter.get('/api/github', githubCalls);
noLangRouter.get('/chat', chat);
noLangRouter.get('/twitch', twitch);
noLangRouter.get('/unsubscribe/:email', unsubscribeMonthly);
noLangRouter.get(
'/unsubscribe-notifications/:email',
unsubscribeNotifications
);
noLangRouter.get('/unsubscribe-quincy/:email', unsubscribeQuincy);
noLangRouter.get('/submit-cat-photo', submitCatPhoto);
noLangRouter.get(
'/the-fastest-web-page-on-the-internet',
theFastestWebPageOnTheInternet
);
noLangRouter.get('/shop/cancel-stickers', cancelStickers);
noLangRouter.get('/shop/confirm-stickers', confirmStickers);

router.get('/unsubscribed', unsubscribed);
router.get('/nonprofits', nonprofits);
router.get('/nonprofits-form', nonprofitsForm);
router.get('/unsubscribe/:email', unsubscribeMonthly);
router.get('/unsubscribe-notifications/:email', unsubscribeNotifications);
router.get('/unsubscribe-quincy/:email', unsubscribeQuincy);
router.get('/unsubscribed', unsubscribed);
router.get('/get-started', getStarted);
router.get('/submit-cat-photo', submitCatPhoto);
router.get('/pmi-acp-agile-project-managers', agileProjectManagers);
router.get('/pmi-acp-agile-project-managers-form', agileProjectManagersForm);
router.get('/coding-bootcamp-cost-calculator', bootcampCalculator);
router.get('/stories', showTestimonials);
router.get('/shop', showShop);
router.get('/shop/cancel-stickers', cancelStickers);
router.get('/shop/confirm-stickers', confirmStickers);
router.get('/all-stories', showAllTestimonials);
router.get('/terms', terms);
router.get('/privacy', privacy);
Expand All @@ -34,12 +42,9 @@ module.exports = function(app) {
);
router.get('/code-of-conduct', codeOfConduct);
router.get('/academic-honesty', academicHonesty);
router.get(
'/the-fastest-web-page-on-the-internet',
theFastestWebPageOnTheInternet
);

app.use(router);
app.use(noLangRouter);
app.use('/:lang', router);

function chat(req, res) {
res.redirect('https://gitter.im/FreeCodeCamp/FreeCodeCamp');
Expand Down Expand Up @@ -242,12 +247,6 @@ module.exports = function(app) {
});
}

function getStarted(req, res) {
res.render('resources/get-started', {
title: 'How to get started with Free Code Camp'
});
}

function githubCalls(req, res, next) {
var githubHeaders = {
headers: {
Expand Down
14 changes: 10 additions & 4 deletions server/boot/a-react.js → server/boot/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ const log = debug('fcc:react-server');
// add routes here as they slowly get reactified
// remove their individual controllers
const routes = [
'/videos',
'/videos/*',
'/challenges',
'/challenges/*',
'/map'
Expand All @@ -24,6 +22,12 @@ const devRoutes = [];
export default function reactSubRouter(app) {
var router = app.loopback.Router();

router.get('/videos', (req, res) => res.redirect('/map'));
router.get(
'/videos/:dashedName',
(req, res) => res.redirect(`/challenges/${req.params.dashedName}`)
);

// These routes are in production
routes.forEach((route) => {
router.get(route, serveReactApp);
Expand All @@ -35,13 +39,15 @@ export default function reactSubRouter(app) {
});
}

app.use(router);
app.use('/:lang', router);

function serveReactApp(req, res, next) {
const { lang } = req;
const serviceOptions = { req };
createApp({
serviceOptions,
location: req.path
location: req.originalUrl,
initialState: { app: { languageTag: lang } }
})
// if react-router does not find a route send down the chain
.filter(({ redirect, props }) => {
Expand Down
Loading

0 comments on commit 078560c

Please sign in to comment.