Skip to content

Commit

Permalink
refactors the frontend controller
Browse files Browse the repository at this point in the history
closes TryGhost#5192
- combines homepage, author, tag routes into one function (with different hash params)
- provides some abstraction for channels
  • Loading branch information
acburdine committed May 5, 2015
1 parent 2ac3b14 commit 08ebc6f
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 117 deletions.
168 changes: 61 additions & 107 deletions core/server/controllers/frontend.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,55 +143,30 @@ function renderPost(req, res) {
};
}

frontendControllers = {
homepage: function (req, res, next) {
// Parse the page number
function renderChannel(channelOpts) {
channelOpts = channelOpts || {};

return function renderChannel(req, res, next) {
var pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1,
options = {
page: pageParam
};
},
filter, filterKey;

// No negative pages, or page 1
if (isNaN(pageParam) || pageParam < 1 || (pageParam === 1 && req.route.path === '/page/:page/')) {
return res.redirect(config.paths.subdir + '/');
if (channelOpts.keyword && channelOpts.hasSlug) {
options[channelOpts.name] = req.params.slug;
}

return getPostPage(options).then(function (page) {
// If page is greater than number of pages we have, redirect to last page
if (pageParam > page.meta.pagination.pages) {
return res.redirect(page.meta.pagination.pages === 1 ? config.paths.subdir + '/' : (config.paths.subdir + '/page/' + page.meta.pagination.pages + '/'));
}

setReqCtx(req, page.posts);

// Render the page of posts
filters.doFilter('prePostsRender', page.posts, res.locals).then(function (posts) {
getActiveThemePaths().then(function (paths) {
var view = paths.hasOwnProperty('home.hbs') ? 'home' : 'index';

// If we're on a page then we always render the index
// template.
if (pageParam > 1) {
view = 'index';
}
function createUrl(page) {
var url = config.paths.subdir + '/';

setResponseContext(req, res);
res.render(view, formatPageResponse(posts, page));
});
});
}).catch(handleError(next));
},
tag: function (req, res, next) {
// Parse the page number
var pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1,
options = {
page: pageParam,
tag: req.params.slug
};
if (channelOpts.keyword) {
url += channelOpts.keyword + '/';
}

// Get url for tag page
function tagUrl(tag, page) {
var url = config.paths.subdir + '/' + config.routeKeywords.tag + '/' + tag + '/';
if (channelOpts.hasSlug) {
url += options[channelOpts.name] + '/';
}

if (page && page > 1) {
url += 'page/' + page + '/';
Expand All @@ -200,97 +175,76 @@ frontendControllers = {
return url;
}

// No negative pages, or page 1
if (isNaN(pageParam) || pageParam < 1 || (req.params.page !== undefined && pageParam === 1)) {
return res.redirect(tagUrl(options.tag));
return res.redirect(createUrl());
}

return getPostPage(options).then(function (page) {
// If page is greater than number of pages we have, redirect to last page
if (pageParam > page.meta.pagination.pages) {
return res.redirect(tagUrl(options.tag, page.meta.pagination.pages));
return res.redirect(createUrl(page.meta.pagination.pages));
}

setReqCtx(req, page.posts);
if (page.meta.filters.tags) {
setReqCtx(req, page.meta.filters.tags[0]);
if (channelOpts.filter && page.meta.filters[channelOpts.filter]) {
filterKey = page.meta.filters[channelOpts.filter];
filter = (_.isArray(filterKey)) ? filterKey[0] : filterKey;
setReqCtx(req, filter);
}

// Render the page of posts
filters.doFilter('prePostsRender', page.posts, res.locals).then(function (posts) {
getActiveThemePaths().then(function (paths) {
var view = template.getThemeViewForTag(paths, options.tag),
// Format data for template
result = formatPageResponse(posts, page, {
tag: page.meta.filters.tags ? page.meta.filters.tags[0] : ''
});

// If the resulting tag is '' then 404.
if (!result.tag) {
return next();
var view = 'index',
result,
extra = {};

if (channelOpts.firstPageTemplate && paths.hasOwnProperty(channelOpts.firstPageTemplate + '.hbs')) {
view = (pageParam > 1) ? 'index' : channelOpts.firstPageTemplate;
} else if (channelOpts.slugTemplate) {
view = template.getThemeViewForChannel(paths, channelOpts.name, options[channelOpts.name]);
} else if (paths.hasOwnProperty(channelOpts.name + '.hbs')) {
view = channelOpts.name;
}
setResponseContext(req, res);
res.render(view, result);
});
});
}).catch(handleError(next));
},
author: function (req, res, next) {
// Parse the page number
var pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1,
options = {
page: pageParam,
author: req.params.slug
};

// Get url for tag page
function authorUrl(author, page) {
var url = config.paths.subdir + '/' + config.routeKeywords.author + '/' + author + '/';

if (page && page > 1) {
url += config.routeKeywords.page + '/' + page + '/';
}

return url;
}
if (channelOpts.filter) {
extra[channelOpts.name] = (filterKey) ? filter : '';

// No negative pages, or page 1
if (isNaN(pageParam) || pageParam < 1 || (req.params.page !== undefined && pageParam === 1)) {
return res.redirect(authorUrl(options.author));
}
if (!extra[channelOpts.name]) {
return next();
}

return getPostPage(options).then(function (page) {
// If page is greater than number of pages we have, redirect to last page
if (pageParam > page.meta.pagination.pages) {
return res.redirect(authorUrl(options.author, page.meta.pagination.pages));
}

setReqCtx(req, page.posts);
if (page.meta.filters.author) {
setReqCtx(req, page.meta.filters.author);
}

// Render the page of posts
filters.doFilter('prePostsRender', page.posts, res.locals).then(function (posts) {
getActiveThemePaths().then(function (paths) {
var view = paths.hasOwnProperty('author.hbs') ? 'author' : 'index',
// Format data for template
result = formatPageResponse(posts, page, {
author: page.meta.filters.author ? page.meta.filters.author : ''
});

// If the resulting author is '' then 404.
if (!result.author) {
return next();
result = formatPageResponse(posts, page, extra);
} else {
result = formatPageResponse(posts, page);
}

setResponseContext(req, res);
res.render(view, result);
});
});
}).catch(handleError(next));
},
};
}

frontendControllers = {
homepage: renderChannel({
name: 'home',
firstPageTemplate: 'home'
}),
tag: renderChannel({
name: 'tag',
keyword: 'tag',
hasSlug: true,
filter: 'tags',
slugTemplate: true
}),
author: renderChannel({
name: 'author',
keyword: 'author',
hasSlug: true,
filter: 'author',
slugTemplate: true
}),
preview: function (req, res, next) {
var params = {
uuid: req.params.uuid,
Expand Down
20 changes: 10 additions & 10 deletions core/server/helpers/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,21 @@ templates.getThemeViewForPost = function (themePaths, post) {
return view;
};

// Given a theme object and a tag slug this will return
// Given a theme object and a slug this will return
// which theme template page should be used.
// If no default or custom tag template exists then 'index'
// will be returned
// If no custom tag template exists but a default does then
// 'tag' will be returned
// If given a tag slug and a custom tag template
// If no custom template exists but a default does then
// the default will be returned
// If given a slug and a custom template
// exits it will return that view.
templates.getThemeViewForTag = function (themePaths, tag) {
var customTagView = 'tag-' + tag,
view = 'tag';
templates.getThemeViewForChannel = function (themePaths, channelName, channel) {
var customChannelView = channelName + '-' + channel,
view = channelName;

if (themePaths.hasOwnProperty(customTagView + '.hbs')) {
view = customTagView;
} else if (!themePaths.hasOwnProperty('tag.hbs')) {
if (themePaths.hasOwnProperty(customChannelView + '.hbs')) {
view = customChannelView;
} else if (!themePaths.hasOwnProperty(channelName + '.hbs')) {
view = 'index';
}

Expand Down

0 comments on commit 08ebc6f

Please sign in to comment.