2013-05-11 20:44:25 +04:00
|
|
|
/**
|
|
|
|
* Main controller for Ghost frontend
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*global require, module */
|
|
|
|
|
2014-01-01 19:27:39 +04:00
|
|
|
var moment = require('moment'),
|
|
|
|
RSS = require('rss'),
|
2014-02-05 12:40:30 +04:00
|
|
|
_ = require('lodash'),
|
2014-01-01 19:27:39 +04:00
|
|
|
url = require('url'),
|
|
|
|
when = require('when'),
|
|
|
|
|
|
|
|
api = require('../api'),
|
|
|
|
config = require('../config'),
|
|
|
|
filters = require('../../server/filters'),
|
2014-02-23 06:16:07 +04:00
|
|
|
template = require('../helpers/template'),
|
2014-05-09 14:11:29 +04:00
|
|
|
errors = require('../errors'),
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2014-02-02 09:29:07 +04:00
|
|
|
frontendControllers,
|
2014-04-12 07:46:15 +04:00
|
|
|
staticPostPermalink,
|
|
|
|
oldRoute,
|
|
|
|
dummyRouter = require('express').Router();
|
|
|
|
|
|
|
|
// Overload this dummyRouter as we only want the layer object.
|
|
|
|
// We don't want to keep in memory many items in an array so we
|
|
|
|
// clear the stack array after every invocation.
|
|
|
|
oldRoute = dummyRouter.route;
|
|
|
|
dummyRouter.route = function () {
|
|
|
|
var layer;
|
|
|
|
|
|
|
|
// Apply old route method
|
|
|
|
oldRoute.apply(dummyRouter, arguments);
|
|
|
|
|
|
|
|
// Grab layer object
|
|
|
|
layer = dummyRouter.stack[0];
|
|
|
|
|
|
|
|
// Reset stack array for memory purposes
|
|
|
|
dummyRouter.stack = [];
|
|
|
|
|
|
|
|
// Return layer
|
|
|
|
return layer;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Cache static post permalink regex
|
|
|
|
staticPostPermalink = dummyRouter.route('/:slug/:edit?');
|
2014-02-02 09:29:07 +04:00
|
|
|
|
2014-02-13 07:26:56 +04:00
|
|
|
function getPostPage(options) {
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
return api.settings.read('postsPerPage').then(function (response) {
|
2014-04-28 03:28:50 +04:00
|
|
|
var postPP = response.settings[0],
|
|
|
|
postsPerPage = parseInt(postPP.value, 10);
|
2014-02-13 07:26:56 +04:00
|
|
|
|
|
|
|
// No negative posts per page, must be number
|
|
|
|
if (!isNaN(postsPerPage) && postsPerPage > 0) {
|
|
|
|
options.limit = postsPerPage;
|
|
|
|
}
|
2014-04-27 20:58:34 +04:00
|
|
|
options.include = 'author,tags,fields';
|
2014-02-13 07:26:56 +04:00
|
|
|
return api.posts.browse(options);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function formatPageResponse(posts, page) {
|
2014-07-29 13:23:02 +04:00
|
|
|
// Delete email from author for frontend output
|
|
|
|
// TODO: do this on API level if no context is available
|
|
|
|
posts = _.each(posts, function (post) {
|
|
|
|
if (post.author) {
|
|
|
|
delete post.author.email;
|
|
|
|
}
|
|
|
|
return post;
|
|
|
|
});
|
2014-02-13 07:26:56 +04:00
|
|
|
return {
|
|
|
|
posts: posts,
|
2014-04-19 19:03:20 +04:00
|
|
|
pagination: page.meta.pagination
|
2014-02-13 07:26:56 +04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2014-07-29 13:23:02 +04:00
|
|
|
function formatResponse(post) {
|
|
|
|
// Delete email from author for frontend output
|
|
|
|
// TODO: do this on API level if no context is available
|
|
|
|
if (post.author) {
|
|
|
|
delete post.author.email;
|
|
|
|
}
|
|
|
|
return {post: post};
|
|
|
|
}
|
|
|
|
|
2014-02-13 07:26:56 +04:00
|
|
|
function handleError(next) {
|
|
|
|
return function (err) {
|
|
|
|
var e = new Error(err.message);
|
2014-02-26 21:51:01 +04:00
|
|
|
e.status = err.code;
|
2014-02-13 07:26:56 +04:00
|
|
|
return next(e);
|
|
|
|
};
|
|
|
|
}
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2014-02-22 05:25:31 +04:00
|
|
|
// Add Request context parameter to the data object
|
|
|
|
// to be passed down to the templates
|
|
|
|
function setReqCtx(req, data) {
|
|
|
|
(Array.isArray(data) ? data : [data]).forEach(function (d) {
|
|
|
|
d.secure = req.secure;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-07-29 07:58:39 +04:00
|
|
|
/**
|
|
|
|
* Returns the paths object of the active theme via way of a promise.
|
|
|
|
* @return {Promise} The promise resolves with the value of the paths.
|
|
|
|
*/
|
|
|
|
function getActiveThemePaths() {
|
|
|
|
return api.settings.read({
|
|
|
|
key: 'activeTheme',
|
|
|
|
context: {
|
|
|
|
internal: true
|
|
|
|
}
|
|
|
|
}).then(function (response) {
|
|
|
|
var activeTheme = response.settings[0],
|
|
|
|
paths = config.paths.availableThemes[activeTheme.value];
|
|
|
|
|
|
|
|
return paths;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-06-25 15:43:15 +04:00
|
|
|
frontendControllers = {
|
2013-09-17 04:54:36 +04:00
|
|
|
'homepage': function (req, res, next) {
|
2013-12-06 12:51:35 +04:00
|
|
|
// Parse the page number
|
|
|
|
var pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1,
|
2014-02-13 07:26:56 +04:00
|
|
|
options = {
|
|
|
|
page: pageParam
|
|
|
|
};
|
2013-08-20 06:18:39 +04:00
|
|
|
|
2014-01-03 04:37:21 +04:00
|
|
|
// No negative pages, or page 1
|
|
|
|
if (isNaN(pageParam) || pageParam < 1 || (pageParam === 1 && req.route.path === '/page/:page/')) {
|
2014-07-17 18:33:21 +04:00
|
|
|
return res.redirect(config.paths.subdir + '/');
|
2014-01-03 04:37:21 +04:00
|
|
|
}
|
|
|
|
|
2014-02-13 07:26:56 +04:00
|
|
|
return getPostPage(options).then(function (page) {
|
2013-09-06 06:09:47 +04:00
|
|
|
|
2014-02-13 07:26:56 +04:00
|
|
|
// If page is greater than number of pages we have, redirect to last page
|
2014-04-19 19:03:20 +04:00
|
|
|
if (pageParam > page.meta.pagination.pages) {
|
2014-07-17 18:33:21 +04:00
|
|
|
return res.redirect(page.meta.pagination.pages === 1 ? config.paths.subdir + '/' : (config.paths.subdir + '/page/' + page.meta.pagination.pages + '/'));
|
2013-12-06 12:51:35 +04:00
|
|
|
}
|
2014-02-13 07:26:56 +04:00
|
|
|
|
2014-02-22 05:25:31 +04:00
|
|
|
setReqCtx(req, page.posts);
|
|
|
|
|
2014-02-13 07:26:56 +04:00
|
|
|
// Render the page of posts
|
|
|
|
filters.doFilter('prePostsRender', page.posts).then(function (posts) {
|
2014-07-29 07:58:39 +04:00
|
|
|
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';
|
|
|
|
}
|
|
|
|
|
|
|
|
res.render(view, formatPageResponse(posts, page));
|
|
|
|
});
|
2014-02-13 07:26:56 +04:00
|
|
|
});
|
|
|
|
}).otherwise(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
|
|
|
|
};
|
|
|
|
|
|
|
|
// Get url for tag page
|
|
|
|
function tagUrl(tag, page) {
|
2014-07-17 18:33:21 +04:00
|
|
|
var url = config.paths.subdir + '/tag/' + tag + '/';
|
2014-02-13 07:26:56 +04:00
|
|
|
|
|
|
|
if (page && page > 1) {
|
|
|
|
url += 'page/' + page + '/';
|
2013-08-21 18:05:17 +04:00
|
|
|
}
|
|
|
|
|
2014-02-13 07:26:56 +04:00
|
|
|
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 getPostPage(options).then(function (page) {
|
2013-08-18 21:41:55 +04:00
|
|
|
// If page is greater than number of pages we have, redirect to last page
|
2014-04-19 19:03:20 +04:00
|
|
|
if (pageParam > page.meta.pagination.pages) {
|
|
|
|
return res.redirect(tagUrl(options.tag, page.meta.pagination.pages));
|
2013-08-18 21:41:55 +04:00
|
|
|
}
|
|
|
|
|
2014-02-22 05:25:31 +04:00
|
|
|
setReqCtx(req, page.posts);
|
2014-04-28 22:54:16 +04:00
|
|
|
if (page.meta.filters.tags) {
|
|
|
|
setReqCtx(req, page.meta.filters.tags[0]);
|
|
|
|
}
|
2014-02-22 05:25:31 +04:00
|
|
|
|
2013-08-18 21:41:55 +04:00
|
|
|
// Render the page of posts
|
2013-11-28 06:45:01 +04:00
|
|
|
filters.doFilter('prePostsRender', page.posts).then(function (posts) {
|
2014-07-29 07:58:39 +04:00
|
|
|
getActiveThemePaths().then(function (paths) {
|
|
|
|
var view = paths.hasOwnProperty('tag.hbs') ? 'tag' : 'index',
|
2014-02-13 07:26:56 +04:00
|
|
|
|
|
|
|
// Format data for template
|
2014-04-28 03:28:50 +04:00
|
|
|
result = _.extend(formatPageResponse(posts, page), {
|
2014-04-27 21:34:10 +04:00
|
|
|
tag: page.meta.filters.tags ? page.meta.filters.tags[0] : ''
|
2014-02-13 07:26:56 +04:00
|
|
|
});
|
|
|
|
|
2014-05-12 22:30:08 +04:00
|
|
|
// If the resulting tag is '' then 404.
|
|
|
|
if (!result.tag) {
|
|
|
|
return next();
|
|
|
|
}
|
2014-04-28 03:28:50 +04:00
|
|
|
res.render(view, result);
|
2014-02-13 07:26:56 +04:00
|
|
|
});
|
2013-05-11 20:44:25 +04:00
|
|
|
});
|
2014-02-13 07:26:56 +04:00
|
|
|
}).otherwise(handleError(next));
|
2013-06-25 15:43:15 +04:00
|
|
|
},
|
2014-07-20 20:32:14 +04:00
|
|
|
'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) {
|
2014-07-23 11:39:51 +04:00
|
|
|
var url = config.paths.subdir + '/author/' + author + '/';
|
2014-07-20 20:32:14 +04:00
|
|
|
|
|
|
|
if (page && page > 1) {
|
|
|
|
url += 'page/' + page + '/';
|
|
|
|
}
|
|
|
|
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No negative pages, or page 1
|
|
|
|
if (isNaN(pageParam) || pageParam < 1 || (req.params.page !== undefined && pageParam === 1)) {
|
|
|
|
return res.redirect(authorUrl(options.author));
|
|
|
|
}
|
|
|
|
|
|
|
|
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).then(function (posts) {
|
2014-07-29 07:58:39 +04:00
|
|
|
getActiveThemePaths().then(function (paths) {
|
|
|
|
var view = paths.hasOwnProperty('author.hbs') ? 'author' : 'index',
|
2014-07-20 20:32:14 +04:00
|
|
|
|
|
|
|
// Format data for template
|
|
|
|
result = _.extend(formatPageResponse(posts, page), {
|
|
|
|
author: page.meta.filters.author ? page.meta.filters.author : ''
|
|
|
|
});
|
|
|
|
|
|
|
|
// If the resulting author is '' then 404.
|
|
|
|
if (!result.author) {
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
res.render(view, result);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}).otherwise(handleError(next));
|
|
|
|
},
|
|
|
|
|
2013-09-17 04:54:36 +04:00
|
|
|
'single': function (req, res, next) {
|
2014-02-02 09:29:07 +04:00
|
|
|
var path = req.path,
|
|
|
|
params,
|
|
|
|
editFormat,
|
|
|
|
usingStaticPermalink = false;
|
|
|
|
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
api.settings.read('permalinks').then(function (response) {
|
2014-04-28 03:28:50 +04:00
|
|
|
var permalink = response.settings[0],
|
|
|
|
postLookup;
|
|
|
|
|
2014-02-02 09:29:07 +04:00
|
|
|
editFormat = permalink.value[permalink.value.length - 1] === '/' ? ':edit?' : '/:edit?';
|
|
|
|
|
|
|
|
// Convert saved permalink into an express Route object
|
2014-04-12 07:46:15 +04:00
|
|
|
permalink = dummyRouter.route(permalink.value + editFormat);
|
2014-02-02 09:29:07 +04:00
|
|
|
|
|
|
|
// Check if the path matches the permalink structure.
|
|
|
|
//
|
|
|
|
// If there are no matches found we then
|
|
|
|
// need to verify it's not a static post,
|
|
|
|
// and test against that permalink structure.
|
|
|
|
if (permalink.match(path) === false) {
|
|
|
|
// If there are still no matches then return.
|
|
|
|
if (staticPostPermalink.match(path) === false) {
|
2014-05-05 17:51:21 +04:00
|
|
|
// Reject promise chain with type 'NotFound'
|
2014-05-09 14:11:29 +04:00
|
|
|
return when.reject(new errors.NotFoundError());
|
2014-02-02 09:29:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
permalink = staticPostPermalink;
|
|
|
|
usingStaticPermalink = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
params = permalink.params;
|
|
|
|
|
|
|
|
// Sanitize params we're going to use to lookup the post.
|
2014-04-28 03:28:50 +04:00
|
|
|
postLookup = _.pick(permalink.params, 'slug', 'id');
|
2014-04-27 20:58:34 +04:00
|
|
|
// Add author, tag and fields
|
|
|
|
postLookup.include = 'author,tags,fields';
|
2014-02-02 09:29:07 +04:00
|
|
|
|
|
|
|
// Query database to find post
|
|
|
|
return api.posts.read(postLookup);
|
2014-04-16 14:09:03 +04:00
|
|
|
}).then(function (result) {
|
|
|
|
var post = result.posts[0],
|
|
|
|
slugDate = [],
|
|
|
|
slugFormat = [];
|
2014-02-02 09:29:07 +04:00
|
|
|
|
|
|
|
if (!post) {
|
|
|
|
return next();
|
|
|
|
}
|
2013-12-30 11:03:29 +04:00
|
|
|
|
|
|
|
function render() {
|
2014-01-20 01:08:39 +04:00
|
|
|
// If we're ready to render the page but the last param is 'edit' then we'll send you to the edit page.
|
2014-08-07 06:02:20 +04:00
|
|
|
if (params.edit) {
|
|
|
|
params.edit = params.edit.toLowerCase();
|
|
|
|
}
|
2014-04-20 08:48:14 +04:00
|
|
|
if (params.edit === 'edit') {
|
2014-07-17 18:33:21 +04:00
|
|
|
return res.redirect(config.paths.subdir + '/ghost/editor/' + post.id + '/');
|
2014-04-20 08:48:14 +04:00
|
|
|
} else if (params.edit !== undefined) {
|
2014-05-05 17:51:21 +04:00
|
|
|
// reject with type: 'NotFound'
|
2014-05-09 14:11:29 +04:00
|
|
|
return when.reject(new errors.NotFoundError());
|
2014-01-03 08:32:31 +04:00
|
|
|
}
|
2014-02-22 05:25:31 +04:00
|
|
|
|
|
|
|
setReqCtx(req, post);
|
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
filters.doFilter('prePostsRender', post).then(function (post) {
|
2014-07-29 07:58:39 +04:00
|
|
|
getActiveThemePaths().then(function (paths) {
|
|
|
|
var view = template.getThemeViewForPost(paths, post);
|
2014-02-23 06:16:07 +04:00
|
|
|
|
2014-07-29 13:23:02 +04:00
|
|
|
res.render(view, formatResponse(post));
|
2013-12-06 12:51:35 +04:00
|
|
|
});
|
2013-09-17 04:54:36 +04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-02-02 09:29:07 +04:00
|
|
|
// If we've checked the path with the static permalink structure
|
|
|
|
// then the post must be a static post.
|
|
|
|
// If it is not then we must return.
|
|
|
|
if (usingStaticPermalink) {
|
2014-06-12 13:44:10 +04:00
|
|
|
if (post.page) {
|
2014-02-02 09:29:07 +04:00
|
|
|
return render();
|
|
|
|
}
|
2013-12-30 11:03:29 +04:00
|
|
|
|
2014-01-01 19:27:39 +04:00
|
|
|
return next();
|
|
|
|
}
|
|
|
|
|
2014-02-02 09:29:07 +04:00
|
|
|
// If there is any date based paramter in the slug
|
|
|
|
// we will check it against the post published date
|
|
|
|
// to verify it's correct.
|
|
|
|
if (params.year || params.month || params.day) {
|
|
|
|
if (params.year) {
|
|
|
|
slugDate.push(params.year);
|
|
|
|
slugFormat.push('YYYY');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params.month) {
|
|
|
|
slugDate.push(params.month);
|
|
|
|
slugFormat.push('MM');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params.day) {
|
|
|
|
slugDate.push(params.day);
|
|
|
|
slugFormat.push('DD');
|
|
|
|
}
|
2013-12-30 11:03:29 +04:00
|
|
|
|
2014-02-02 09:29:07 +04:00
|
|
|
slugDate = slugDate.join('/');
|
|
|
|
slugFormat = slugFormat.join('/');
|
|
|
|
|
|
|
|
if (slugDate === moment(post.published_at).format(slugFormat)) {
|
2013-12-30 11:03:29 +04:00
|
|
|
return render();
|
|
|
|
}
|
|
|
|
|
2014-02-02 09:29:07 +04:00
|
|
|
return next();
|
|
|
|
}
|
2013-12-30 11:03:29 +04:00
|
|
|
|
2014-05-05 17:51:21 +04:00
|
|
|
return render();
|
2013-12-30 11:03:29 +04:00
|
|
|
|
2013-09-17 04:54:36 +04:00
|
|
|
}).otherwise(function (err) {
|
2014-02-02 09:29:07 +04:00
|
|
|
// If we've thrown an error message
|
2014-05-05 17:51:21 +04:00
|
|
|
// of type: 'NotFound' then we found
|
2014-02-02 09:29:07 +04:00
|
|
|
// no path match.
|
2014-05-09 14:11:29 +04:00
|
|
|
if (err.type === 'NotFoundError') {
|
2014-02-02 09:29:07 +04:00
|
|
|
return next();
|
|
|
|
}
|
|
|
|
|
2014-02-13 07:26:56 +04:00
|
|
|
return handleError(next)(err);
|
2013-06-25 15:43:15 +04:00
|
|
|
});
|
2013-08-28 02:31:43 +04:00
|
|
|
},
|
2013-09-17 04:54:36 +04:00
|
|
|
'rss': function (req, res, next) {
|
2014-07-31 05:41:25 +04:00
|
|
|
function isPaginated() {
|
|
|
|
return req.route.path.indexOf(':page') !== -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isTag() {
|
|
|
|
return req.route.path.indexOf('/tag/') !== -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isAuthor() {
|
|
|
|
return req.route.path.indexOf('/author/') !== -1;
|
|
|
|
}
|
|
|
|
|
2013-08-28 02:31:43 +04:00
|
|
|
// Initialize RSS
|
2014-01-03 04:37:21 +04:00
|
|
|
var pageParam = req.params.page !== undefined ? parseInt(req.params.page, 10) : 1,
|
2014-07-31 05:41:25 +04:00
|
|
|
slugParam = req.params.slug,
|
|
|
|
baseUrl = config.paths.subdir;
|
|
|
|
|
|
|
|
if (isTag()) {
|
|
|
|
baseUrl += '/tag/' + slugParam + '/rss/';
|
|
|
|
} else if (isAuthor()) {
|
|
|
|
baseUrl += '/author/' + slugParam + '/rss/';
|
|
|
|
} else {
|
|
|
|
baseUrl += '/rss/';
|
|
|
|
}
|
2014-01-03 04:37:21 +04:00
|
|
|
|
|
|
|
// No negative pages, or page 1
|
2014-07-31 05:41:25 +04:00
|
|
|
if (isNaN(pageParam) || pageParam < 1 || (pageParam === 1 && isPaginated())) {
|
|
|
|
return res.redirect(baseUrl);
|
2014-01-03 04:37:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return when.settle([
|
Refactor API arguments
closes #2610, refs #2697
- cleanup API index.js, and add docs
- all API methods take consistent arguments: object & options
- browse, read, destroy take options, edit and add take object and options
- the context is passed as part of options, meaning no more .call
everywhere
- destroy expects an object, rather than an id all the way down to the model layer
- route params such as :id, :slug, and :key are passed as an option & used
to perform reads, updates and deletes where possible - settings / themes
may need work here still
- HTTP posts api can find a post by slug
- Add API utils for checkData
2014-05-08 16:41:19 +04:00
|
|
|
api.settings.read('title'),
|
|
|
|
api.settings.read('description'),
|
|
|
|
api.settings.read('permalinks')
|
2013-12-30 05:03:30 +04:00
|
|
|
]).then(function (result) {
|
2013-08-28 02:31:43 +04:00
|
|
|
|
2014-03-06 02:03:39 +04:00
|
|
|
var options = {};
|
|
|
|
if (pageParam) { options.page = pageParam; }
|
2014-07-31 05:41:25 +04:00
|
|
|
if (isTag()) { options.tag = slugParam; }
|
|
|
|
if (isAuthor()) { options.author = slugParam; }
|
2014-03-06 02:03:39 +04:00
|
|
|
|
2014-04-27 20:58:34 +04:00
|
|
|
options.include = 'author,tags,fields';
|
|
|
|
|
2014-03-06 02:03:39 +04:00
|
|
|
return api.posts.browse(options).then(function (page) {
|
|
|
|
|
2014-04-28 03:28:50 +04:00
|
|
|
var title = result[0].value.settings[0].value,
|
|
|
|
description = result[1].value.settings[0].value,
|
|
|
|
permalinks = result[2].value.settings[0],
|
2014-07-22 01:07:21 +04:00
|
|
|
majorMinor = /^(\d+\.)?(\d+)/,
|
|
|
|
trimmedVersion = res.locals.version,
|
2014-02-22 05:25:31 +04:00
|
|
|
siteUrl = config.urlFor('home', {secure: req.secure}, true),
|
2014-04-28 03:28:50 +04:00
|
|
|
feedUrl = config.urlFor('rss', {secure: req.secure}, true),
|
2014-04-19 19:03:20 +04:00
|
|
|
maxPage = page.meta.pagination.pages,
|
2014-03-06 02:03:39 +04:00
|
|
|
feedItems = [],
|
|
|
|
feed;
|
|
|
|
|
2014-07-22 01:07:21 +04:00
|
|
|
trimmedVersion = trimmedVersion ? trimmedVersion.match(majorMinor)[0] : '?';
|
|
|
|
|
2014-07-31 05:41:25 +04:00
|
|
|
if (isTag()) {
|
2014-04-27 21:34:10 +04:00
|
|
|
if (page.meta.filters.tags) {
|
|
|
|
title = page.meta.filters.tags[0].name + ' - ' + title;
|
2014-08-10 07:16:27 +04:00
|
|
|
feedUrl = siteUrl + 'tag/' + page.meta.filters.tags[0].slug + '/rss/';
|
2014-04-27 21:34:10 +04:00
|
|
|
}
|
2014-03-06 02:03:39 +04:00
|
|
|
}
|
|
|
|
|
2014-07-31 05:41:25 +04:00
|
|
|
if (isAuthor()) {
|
|
|
|
if (page.meta.filters.author) {
|
|
|
|
title = page.meta.filters.author.name + ' - ' + title;
|
2014-08-10 07:16:27 +04:00
|
|
|
feedUrl = siteUrl + 'author/' + page.meta.filters.author.slug + '/rss/';
|
2014-07-31 05:41:25 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-06 02:03:39 +04:00
|
|
|
feed = new RSS({
|
|
|
|
title: title,
|
|
|
|
description: description,
|
2014-07-22 01:07:21 +04:00
|
|
|
generator: 'Ghost ' + trimmedVersion,
|
2014-03-06 02:03:39 +04:00
|
|
|
feed_url: feedUrl,
|
|
|
|
site_url: siteUrl,
|
|
|
|
ttl: '60'
|
|
|
|
});
|
|
|
|
|
2013-09-16 15:11:17 +04:00
|
|
|
// If page is greater than number of pages we have, redirect to last page
|
|
|
|
if (pageParam > maxPage) {
|
2014-07-31 05:41:25 +04:00
|
|
|
return res.redirect(baseUrl + maxPage + '/');
|
2013-09-16 15:11:17 +04:00
|
|
|
}
|
|
|
|
|
2014-02-22 05:25:31 +04:00
|
|
|
setReqCtx(req, page.posts);
|
|
|
|
|
2013-11-28 06:45:01 +04:00
|
|
|
filters.doFilter('prePostsRender', page.posts).then(function (posts) {
|
2013-09-16 15:11:17 +04:00
|
|
|
posts.forEach(function (post) {
|
2014-01-03 04:37:21 +04:00
|
|
|
var deferred = when.defer(),
|
|
|
|
item = {
|
2014-03-10 19:42:38 +04:00
|
|
|
title: post.title,
|
2014-01-03 04:37:21 +04:00
|
|
|
guid: post.uuid,
|
2014-01-05 10:40:53 +04:00
|
|
|
url: config.urlFor('post', {post: post, permalinks: permalinks}, true),
|
2014-01-03 04:37:21 +04:00
|
|
|
date: post.published_at,
|
2014-02-05 09:59:07 +04:00
|
|
|
categories: _.pluck(post.tags, 'name'),
|
2014-04-19 19:03:20 +04:00
|
|
|
author: post.author ? post.author.name : null
|
2014-01-03 04:37:21 +04:00
|
|
|
},
|
|
|
|
content = post.html;
|
|
|
|
|
|
|
|
//set img src to absolute url
|
|
|
|
content = content.replace(/src=["|'|\s]?([\w\/\?\$\.\+\-;%:@&=,_]+)["|'|\s]?/gi, function (match, p1) {
|
|
|
|
/*jslint unparam:true*/
|
|
|
|
p1 = url.resolve(siteUrl, p1);
|
|
|
|
return "src='" + p1 + "' ";
|
|
|
|
});
|
|
|
|
//set a href to absolute url
|
|
|
|
content = content.replace(/href=["|'|\s]?([\w\/\?\$\.\+\-;%:@&=,_]+)["|'|\s]?/gi, function (match, p1) {
|
|
|
|
/*jslint unparam:true*/
|
|
|
|
p1 = url.resolve(siteUrl, p1);
|
|
|
|
return "href='" + p1 + "' ";
|
2013-09-26 16:52:53 +04:00
|
|
|
});
|
2014-01-03 04:37:21 +04:00
|
|
|
item.description = content;
|
|
|
|
feed.item(item);
|
2013-12-29 04:15:14 +04:00
|
|
|
feedItems.push(deferred.promise);
|
2014-04-28 03:28:50 +04:00
|
|
|
deferred.resolve();
|
2013-09-16 15:11:17 +04:00
|
|
|
});
|
2013-12-29 04:15:14 +04:00
|
|
|
});
|
2014-01-03 04:37:21 +04:00
|
|
|
|
2013-12-29 04:15:14 +04:00
|
|
|
when.all(feedItems).then(function () {
|
2014-04-23 13:47:25 +04:00
|
|
|
res.set('Content-Type', 'text/xml; charset=UTF-8');
|
2013-09-16 15:11:17 +04:00
|
|
|
res.send(feed.xml());
|
2013-08-28 02:31:43 +04:00
|
|
|
});
|
|
|
|
});
|
2014-02-13 07:26:56 +04:00
|
|
|
}).otherwise(handleError(next));
|
2013-06-25 15:43:15 +04:00
|
|
|
}
|
|
|
|
};
|
2013-05-11 20:44:25 +04:00
|
|
|
|
2013-10-18 21:18:49 +04:00
|
|
|
module.exports = frontendControllers;
|