diff --git a/.gitignore b/.gitignore index 09bc2f7a6b..b12c980b29 100644 --- a/.gitignore +++ b/.gitignore @@ -63,7 +63,7 @@ CHANGELOG.md /core/test/coverage # ignore all custom json files for config -config.*.json +/config.*.json # Built asset files /core/built diff --git a/core/server/controllers/frontend/channel-config.js b/core/server/controllers/frontend/channel-config.js index 3ebb5bd53d..708e1f24de 100644 --- a/core/server/controllers/frontend/channel-config.js +++ b/core/server/controllers/frontend/channel-config.js @@ -1,56 +1,27 @@ var _ = require('lodash'), - config = require('../../config'), - utils = require('../../utils'), - channelConfig; + channels = []; -channelConfig = function channelConfig() { - var defaults = { - index: { - name: 'index', - route: '/', - frontPageTemplate: 'home' - }, - tag: { - name: 'tag', - route: utils.url.urlJoin('/', config.get('routeKeywords').tag, ':slug/'), - postOptions: { - filter: 'tags:\'%s\'+tags.visibility:\'public\'' - }, - data: { - tag: { - type: 'read', - resource: 'tags', - options: {slug: '%s', visibility: 'public'} - } - }, - slugTemplate: true, - editRedirect: '#/settings/tags/:slug/' - }, - author: { - name: 'author', - route: utils.url.urlJoin('/', config.get('routeKeywords').author, ':slug/'), - postOptions: { - filter: 'author:\'%s\'' - }, - data: { - author: { - type: 'read', - resource: 'users', - options: {slug: '%s'} - } - }, - slugTemplate: true, - editRedirect: '#/team/:slug/' - } - }; +function loadConfig() { + var channelConfig = {}; - return defaults; -}; + // This is a very dirty temporary hack so that we can test out channels with some Beta testers + // If you are reading this code, and considering using it, best reach out to us on Slack + // Definitely don't be angry at us if the structure of the JSON changes or this goes away. + try { + channelConfig = require('../../../../config.channels.json'); + } catch (err) { + channelConfig = require('./config.channels.json'); + } + + return channelConfig; +} module.exports.list = function list() { - return channelConfig(); -}; + _.each(loadConfig(), function (channelConfig, channelName) { + var channel = _.cloneDeep(channelConfig); + channel.name = channelName; + channels.push(channel); + }); -module.exports.get = function get(name) { - return _.cloneDeep(channelConfig()[name]); + return channels; }; diff --git a/core/server/controllers/frontend/channels.js b/core/server/controllers/frontend/channels.js index dc2ec37fbb..7b34660bb6 100644 --- a/core/server/controllers/frontend/channels.js +++ b/core/server/controllers/frontend/channels.js @@ -96,8 +96,12 @@ channelsRouter = function router() { var channelsRouter = express.Router({mergeParams: true}); _.each(channelConfig.list(), function (channel) { + var channelRoute = channel.route.replace(/:rkw-([a-zA-Z]+)/, function (fullMatch, keyword) { + return config.get('routeKeywords')[keyword]; + }); + // Mount this channel router on the parent channels router - channelsRouter.use(channel.route, buildChannelRouter(channel)); + channelsRouter.use(channelRoute, buildChannelRouter(channel)); }); return channelsRouter; diff --git a/core/server/controllers/frontend/config.channels.json b/core/server/controllers/frontend/config.channels.json new file mode 100644 index 0000000000..b9814e049b --- /dev/null +++ b/core/server/controllers/frontend/config.channels.json @@ -0,0 +1,41 @@ +{ + "index": { + "route": "/", + "frontPageTemplate": "home" + }, + "tag": { + "route": "/:rkw-tag/:slug/", + "postOptions": { + "filter": "tags:'%s'+tags.visibility:public" + }, + "data": { + "tag": { + "type": "read", + "resource": "tags", + "options": { + "slug": "%s", + "visibility": "public" + } + } + }, + "slugTemplate": true, + "editRedirect": "#/settings/tags/:slug/" + }, + "author": { + "route": "/:rkw-author/:slug/", + "postOptions": { + "filter": "author:'%s'" + }, + "data": { + "author": { + "type": "read", + "resource": "users", + "options": { + "slug": "%s" + } + } + }, + "slugTemplate": true, + "editRedirect": "#/team/:slug/" + } +} diff --git a/core/test/unit/controllers/frontend/channel-config_spec.js b/core/test/unit/controllers/frontend/channel-config_spec.js index 615b304e44..5bc426e7f1 100644 --- a/core/test/unit/controllers/frontend/channel-config_spec.js +++ b/core/test/unit/controllers/frontend/channel-config_spec.js @@ -1,23 +1,26 @@ -/*jshint expr:true*/ -var should = require('should'), - channelConfig = require('../../../../server/controllers/frontend/channel-config'); +var should = require('should'), // jshint ignore:line + _ = require('lodash'), + rewire = require('rewire'), + channelUtils = require('../../../utils/channelUtils'), + channelConfig = rewire('../../../../server/controllers/frontend/channel-config'); describe('Channel Config', function () { - it('should get the index config', function () { - var result = channelConfig.get('index'); - should.exist(result); - result.name.should.eql('index'); + var channelReset; + + before(function () { + channelReset = channelConfig.__set__('loadConfig', function () { + return channelUtils.getDefaultChannels(); + }); }); - it('should get the author config', function () { - var result = channelConfig.get('author'); - should.exist(result); - result.name.should.eql('author'); + after(function () { + channelReset(); }); - it('should get the tag config', function () { - var result = channelConfig.get('tag'); - should.exist(result); - result.name.should.eql('tag'); + it('should build a list of channels', function () { + var channels = channelConfig.list(); + channels.should.be.an.Object(); + + _.map(channels, 'name').should.eql(['index', 'tag', 'author']); }); }); diff --git a/core/test/unit/controllers/frontend/context_spec.js b/core/test/unit/controllers/frontend/context_spec.js index 533aac2eca..cb8f71c870 100644 --- a/core/test/unit/controllers/frontend/context_spec.js +++ b/core/test/unit/controllers/frontend/context_spec.js @@ -3,7 +3,7 @@ var should = require('should'), _ = require('lodash'), // Stuff we are testing - channelConfig = require('../../../../server/controllers/frontend/channel-config'), + channelUtils = require('../../../utils/channelUtils'), setResponseContext = require('../../../../server/controllers/frontend/context'), labs = require('../../../../server/utils/labs'), @@ -38,7 +38,7 @@ describe('Contexts', function () { res.locals.relativeUrl = url; if (channel && _.isString(channel)) { - res.locals.channel = channelConfig.get(channel); + res.locals.channel = channelUtils.getTestChannel(channel); } else if (channel && _.isNumber(channel)) { pageParam = channel; } else if (channel) { diff --git a/core/test/unit/controllers/frontend/render-channel_spec.js b/core/test/unit/controllers/frontend/render-channel_spec.js index 94a2542e14..6a0ff226f6 100644 --- a/core/test/unit/controllers/frontend/render-channel_spec.js +++ b/core/test/unit/controllers/frontend/render-channel_spec.js @@ -3,7 +3,7 @@ var should = require('should'), // jshint ignore:line sinon = require('sinon'), rewire = require('rewire'), - channelConfig = require('../../../../server/controllers/frontend/channel-config').get, + channelUtils = require('../../../utils/channelUtils'), // stuff being tested renderChannel = rewire('../../../../server/controllers/frontend/render-channel'), @@ -28,7 +28,7 @@ describe('Render Channel', function () { }, res = { locals: { - channel: channelConfig('tag') + channel: channelUtils.getTestChannel('tag') } }, promise = { @@ -43,7 +43,7 @@ describe('Render Channel', function () { it('should return correct tag config', function () { renderChannel.__set__('fetchData', function (channelOpts) { channelOpts.name.should.eql('tag'); - channelOpts.postOptions.filter.should.eql('tags:\'%s\'+tags.visibility:\'public\''); + channelOpts.postOptions.filter.should.eql('tags:\'%s\'+tags.visibility:public'); channelOpts.data.tag.options.should.eql({slug: '%s', visibility: 'public'}); return promise; diff --git a/core/test/unit/rss_spec.js b/core/test/unit/rss_spec.js index 2c424cf551..579a9b3b9c 100644 --- a/core/test/unit/rss_spec.js +++ b/core/test/unit/rss_spec.js @@ -4,7 +4,7 @@ var should = require('should'), _ = require('lodash'), Promise = require('bluebird'), testUtils = require('../utils'), - channelConfig = require('../../server/controllers/frontend/channel-config'), + channelUtils = require('../utils/channelUtils'), api = require('../../server/api'), settingsCache = require('../../server/settings/cache'), rss = rewire('../../server/data/xml/rss'), @@ -97,7 +97,7 @@ describe('RSS', function () { done(); }; - res.locals.channel = channelConfig.get('index'); + res.locals.channel = channelUtils.getTestChannel('index'); res.locals.channel.isRSS = true; rss(req, res, failTest(done)); }); @@ -140,7 +140,7 @@ describe('RSS', function () { done(); }; - res.locals.channel = channelConfig.get('index'); + res.locals.channel = channelUtils.getTestChannel('index'); res.locals.channel.isRSS = true; rss(req, res, failTest(done)); }); @@ -176,7 +176,7 @@ describe('RSS', function () { done(); }; - res.locals.channel = channelConfig.get('index'); + res.locals.channel = channelUtils.getTestChannel('index'); res.locals.channel.isRSS = true; rss(req, res, failTest(done)); }); @@ -204,7 +204,7 @@ describe('RSS', function () { done(); }; - res.locals.channel = channelConfig.get('index'); + res.locals.channel = channelUtils.getTestChannel('index'); res.locals.channel.isRSS = true; rss(req, res, failTest(done)); }); @@ -238,7 +238,7 @@ describe('RSS', function () { done(); }; - res.locals.channel = channelConfig.get('index'); + res.locals.channel = channelUtils.getTestChannel('index'); res.locals.channel.isRSS = true; rss(req, res, failTest(done)); }); @@ -270,7 +270,7 @@ describe('RSS', function () { done(); }; - res.locals.channel = channelConfig.get('index'); + res.locals.channel = channelUtils.getTestChannel('index'); res.locals.channel.isRSS = true; rss(req, res, failTest(done)); }); @@ -327,7 +327,7 @@ describe('RSS', function () { done(); }; - res.locals.channel = channelConfig.get('index'); + res.locals.channel = channelUtils.getTestChannel('index'); res.locals.channel.isRSS = true; rss(req, res, failTest(done)); }); @@ -336,7 +336,7 @@ describe('RSS', function () { // setup req.originalUrl = '/tag/magic/rss/'; req.params.slug = 'magic'; - res.locals.channel = channelConfig.get('tag'); + res.locals.channel = channelUtils.getTestChannel('tag'); res.locals.channel.isRSS = true; // test @@ -344,7 +344,7 @@ describe('RSS', function () { apiBrowseStub.calledOnce.should.be.true(); apiBrowseStub.calledWith({ page: 1, - filter: 'tags:\'magic\'+tags.visibility:\'public\'', + filter: 'tags:\'magic\'+tags.visibility:public', include: 'author,tags' }).should.be.true(); apiTagStub.calledOnce.should.be.true(); @@ -361,7 +361,7 @@ describe('RSS', function () { req.originalUrl = '/tag/magic/rss/2/'; req.params.slug = 'magic'; req.params.page = '2'; - res.locals.channel = channelConfig.get('tag'); + res.locals.channel = channelUtils.getTestChannel('tag'); res.locals.channel.isRSS = true; // test @@ -369,7 +369,7 @@ describe('RSS', function () { apiBrowseStub.calledOnce.should.be.true(); apiBrowseStub.calledWith({ page: '2', - filter: 'tags:\'magic\'+tags.visibility:\'public\'', + filter: 'tags:\'magic\'+tags.visibility:public', include: 'author,tags' }).should.be.true(); @@ -385,7 +385,7 @@ describe('RSS', function () { it('should process the data correctly for an author feed', function (done) { req.originalUrl = '/author/joe/rss/'; req.params.slug = 'joe'; - res.locals.channel = channelConfig.get('author'); + res.locals.channel = channelUtils.getTestChannel('author'); res.locals.channel.isRSS = true; // test @@ -405,7 +405,7 @@ describe('RSS', function () { req.originalUrl = '/author/joe/rss/2/'; req.params.slug = 'joe'; req.params.page = '2'; - res.locals.channel = channelConfig.get('author'); + res.locals.channel = channelUtils.getTestChannel('author'); res.locals.channel.isRSS = true; // test @@ -449,7 +449,7 @@ describe('RSS', function () { results: {posts: [], meta: {pagination: {pages: 1}}} }); }); - res.locals.channel = channelConfig.get('index'); + res.locals.channel = channelUtils.getTestChannel('index'); res.locals.channel.isRSS = true; function secondCall() { @@ -502,7 +502,7 @@ describe('RSS', function () { req = {params: {page: 4}, route: {path: '/rss/:page/'}}; req.originalUrl = req.route.path.replace(':page', req.params.page); - res.locals.channel = channelConfig.get('index'); + res.locals.channel = channelUtils.getTestChannel('index'); res.locals.channel.isRSS = true; rss(req, res, function (err) { @@ -519,7 +519,7 @@ describe('RSS', function () { req = {params: {page: 4}, route: {path: '/rss/:page/'}}; req.originalUrl = req.route.path.replace(':page', req.params.page); - res.locals.channel = channelConfig.get('index'); + res.locals.channel = channelUtils.getTestChannel('index'); res.locals.channel.isRSS = true; rss(req, res, function (err) { diff --git a/core/test/utils/channelUtils.js b/core/test/utils/channelUtils.js new file mode 100644 index 0000000000..b06c168dfb --- /dev/null +++ b/core/test/utils/channelUtils.js @@ -0,0 +1,14 @@ +var defaultChannels = require('../../server/controllers/frontend/config.channels.json'); + +// This is a function to get a fake or test channel +// It's currently based on the default config in Ghost itself +module.exports.getTestChannel = function getTestChannel(channelName) { + var channel = defaultChannels[channelName]; + channel.name = channelName; + + return channel; +}; + +module.exports.getDefaultChannels = function getDefaultChannels() { + return defaultChannels; +};