mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-29 15:12:58 +03:00
commit
c0732292ac
@ -102,7 +102,7 @@ function urlPathForPost(post, permalinks) {
|
|||||||
function urlFor(context, data, absolute) {
|
function urlFor(context, data, absolute) {
|
||||||
var urlPath = '/',
|
var urlPath = '/',
|
||||||
secure,
|
secure,
|
||||||
knownObjects = ['post', 'tag', 'user'],
|
knownObjects = ['post', 'tag', 'author'],
|
||||||
|
|
||||||
// this will become really big
|
// this will become really big
|
||||||
knownPaths = {
|
knownPaths = {
|
||||||
@ -130,6 +130,9 @@ function urlFor(context, data, absolute) {
|
|||||||
} else if (context === 'tag' && data.tag) {
|
} else if (context === 'tag' && data.tag) {
|
||||||
urlPath = '/tag/' + data.tag.slug + '/';
|
urlPath = '/tag/' + data.tag.slug + '/';
|
||||||
secure = data.tag.secure;
|
secure = data.tag.secure;
|
||||||
|
} else if (context === 'author' && data.author) {
|
||||||
|
urlPath = '/author/' + data.author.slug + '/';
|
||||||
|
secure = data.author.secure;
|
||||||
}
|
}
|
||||||
// other objects are recognised but not yet supported
|
// other objects are recognised but not yet supported
|
||||||
} else if (_.isString(context) && _.indexOf(_.keys(knownPaths), context) !== -1) {
|
} else if (_.isString(context) && _.indexOf(_.keys(knownPaths), context) !== -1) {
|
||||||
|
@ -165,6 +165,66 @@ frontendControllers = {
|
|||||||
});
|
});
|
||||||
}).otherwise(handleError(next));
|
}).otherwise(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 + '/author/' + author + '/';
|
||||||
|
|
||||||
|
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) {
|
||||||
|
api.settings.read({key: 'activeTheme', context: {internal: true}}).then(function (response) {
|
||||||
|
var activeTheme = response.settings[0],
|
||||||
|
paths = config().paths.availableThemes[activeTheme.value],
|
||||||
|
view = paths.hasOwnProperty('author.hbs') ? 'author' : 'index',
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
},
|
||||||
|
|
||||||
'single': function (req, res, next) {
|
'single': function (req, res, next) {
|
||||||
var path = req.path,
|
var path = req.path,
|
||||||
params,
|
params,
|
||||||
|
@ -193,8 +193,14 @@ function isTag(jsonData) {
|
|||||||
jsonData.hasOwnProperty('description') && jsonData.hasOwnProperty('parent');
|
jsonData.hasOwnProperty('description') && jsonData.hasOwnProperty('parent');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isUser(jsonData) {
|
||||||
|
return jsonData.hasOwnProperty('bio') && jsonData.hasOwnProperty('website') &&
|
||||||
|
jsonData.hasOwnProperty('status') && jsonData.hasOwnProperty('location');
|
||||||
|
}
|
||||||
|
|
||||||
module.exports.tables = db;
|
module.exports.tables = db;
|
||||||
module.exports.checks = {
|
module.exports.checks = {
|
||||||
isPost: isPost,
|
isPost: isPost,
|
||||||
isTag: isTag
|
isTag: isTag,
|
||||||
|
isUser: isUser
|
||||||
};
|
};
|
||||||
|
@ -154,6 +154,11 @@ coreHelpers.url = function (options) {
|
|||||||
return when(config.urlFor('tag', {tag: this}, absolute));
|
return when(config.urlFor('tag', {tag: this}, absolute));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (schema.isUser(this)) {
|
||||||
|
return when(config.urlFor('author', {author: this}, absolute));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return when(config.urlFor(this, absolute));
|
return when(config.urlFor(this, absolute));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -200,9 +205,26 @@ coreHelpers.asset = function (context, options) {
|
|||||||
// Returns the full name of the author of a given post, or a blank string
|
// Returns the full name of the author of a given post, or a blank string
|
||||||
// if the author could not be determined.
|
// if the author could not be determined.
|
||||||
//
|
//
|
||||||
coreHelpers.author = function (context, options) {
|
coreHelpers.author = function (options) {
|
||||||
|
options = options || {};
|
||||||
|
options.hash = options.hash || {};
|
||||||
|
|
||||||
/*jshint unused:false*/
|
/*jshint unused:false*/
|
||||||
return this.author ? this.author.name : '';
|
var autolink = _.isString(options.hash.autolink) && options.hash.autolink === 'false' ? false : true,
|
||||||
|
output = '';
|
||||||
|
|
||||||
|
if (this.author && this.author.name) {
|
||||||
|
if (autolink) {
|
||||||
|
output = linkTemplate({
|
||||||
|
url: config.urlFor('author', {author: this.author}),
|
||||||
|
text: _.escape(this.author.name)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
output = _.escape(this.author.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new hbs.handlebars.SafeString(output);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ### Tags Helper
|
// ### Tags Helper
|
||||||
@ -217,10 +239,13 @@ coreHelpers.author = function (context, options) {
|
|||||||
// Note that the standard {{#each tags}} implementation is unaffected by this helper
|
// Note that the standard {{#each tags}} implementation is unaffected by this helper
|
||||||
// and can be used for more complex templates.
|
// and can be used for more complex templates.
|
||||||
coreHelpers.tags = function (options) {
|
coreHelpers.tags = function (options) {
|
||||||
var autolink = _.isString(options.hash.autolink) && options.hash.autolink === "false" ? false : true,
|
options = options || {};
|
||||||
separator = _.isString(options.hash.separator) ? options.hash.separator : ', ',
|
options.hash = options.hash || {};
|
||||||
prefix = _.isString(options.hash.prefix) ? options.hash.prefix : '',
|
|
||||||
suffix = _.isString(options.hash.suffix) ? options.hash.suffix : '',
|
var autolink = options.hash && _.isString(options.hash.autolink) && options.hash.autolink === 'false' ? false : true,
|
||||||
|
separator = options.hash && _.isString(options.hash.separator) ? options.hash.separator : ', ',
|
||||||
|
prefix = options.hash && _.isString(options.hash.prefix) ? options.hash.prefix : '',
|
||||||
|
suffix = options.hash && _.isString(options.hash.suffix) ? options.hash.suffix : '',
|
||||||
output = '';
|
output = '';
|
||||||
|
|
||||||
function createTagList(tags) {
|
function createTagList(tags) {
|
||||||
@ -481,7 +506,7 @@ coreHelpers.ghost_foot = function (options) {
|
|||||||
|
|
||||||
coreHelpers.meta_title = function (options) {
|
coreHelpers.meta_title = function (options) {
|
||||||
/*jshint unused:false*/
|
/*jshint unused:false*/
|
||||||
var title = "",
|
var title = '',
|
||||||
blog;
|
blog;
|
||||||
|
|
||||||
if (_.isString(this.relativeUrl)) {
|
if (_.isString(this.relativeUrl)) {
|
||||||
@ -496,7 +521,7 @@ coreHelpers.meta_title = function (options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return filters.doFilter('meta_title', title).then(function (title) {
|
return filters.doFilter('meta_title', title).then(function (title) {
|
||||||
title = title || "";
|
title = title || '';
|
||||||
return title.trim();
|
return title.trim();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -516,7 +541,7 @@ coreHelpers.meta_description = function (options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return filters.doFilter('meta_description', description).then(function (description) {
|
return filters.doFilter('meta_description', description).then(function (description) {
|
||||||
description = description || "";
|
description = description || '';
|
||||||
return description.trim();
|
return description.trim();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -554,7 +579,7 @@ coreHelpers.foreach = function (context, options) {
|
|||||||
j = 0,
|
j = 0,
|
||||||
columns = options.hash.columns,
|
columns = options.hash.columns,
|
||||||
key,
|
key,
|
||||||
ret = "",
|
ret = '',
|
||||||
data;
|
data;
|
||||||
|
|
||||||
if (options.data) {
|
if (options.data) {
|
||||||
@ -639,7 +664,7 @@ coreHelpers.has = function (options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!tagList) {
|
if (!tagList) {
|
||||||
errors.logWarn("Invalid or no attribute given to has helper");
|
errors.logWarn('Invalid or no attribute given to has helper');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -707,7 +732,7 @@ function registerAsyncHelper(hbs, name, fn) {
|
|||||||
when.resolve(fn.call(this, options)).then(function (result) {
|
when.resolve(fn.call(this, options)).then(function (result) {
|
||||||
cb(result);
|
cb(result);
|
||||||
}).otherwise(function (err) {
|
}).otherwise(function (err) {
|
||||||
errors.logAndThrowError(err, "registerAsyncThemeHelper: " + name);
|
errors.logAndThrowError(err, 'registerAsyncThemeHelper: ' + name);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,9 @@ var _ = require('lodash'),
|
|||||||
Showdown = require('showdown'),
|
Showdown = require('showdown'),
|
||||||
ghostgfm = require('../../shared/lib/showdown/extensions/ghostgfm'),
|
ghostgfm = require('../../shared/lib/showdown/extensions/ghostgfm'),
|
||||||
converter = new Showdown.converter({extensions: [ghostgfm]}),
|
converter = new Showdown.converter({extensions: [ghostgfm]}),
|
||||||
Tag = require('./tag').Tag,
|
Tag = require('./tag').Tag,
|
||||||
Tags = require('./tag').Tags,
|
Tags = require('./tag').Tags,
|
||||||
|
User = require('./user').User,
|
||||||
ghostBookshelf = require('./base'),
|
ghostBookshelf = require('./base'),
|
||||||
xmlrpc = require('../xmlrpc'),
|
xmlrpc = require('../xmlrpc'),
|
||||||
|
|
||||||
@ -278,7 +279,8 @@ Post = ghostBookshelf.Model.extend({
|
|||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
var postCollection = Posts.forge(),
|
var postCollection = Posts.forge(),
|
||||||
tagInstance = options.tag !== undefined ? Tag.forge({slug: options.tag}) : false;
|
tagInstance = options.tag !== undefined ? Tag.forge({slug: options.tag}) : false,
|
||||||
|
authorInstance = options.author !== undefined ? User.forge({slug: options.author}) : false;
|
||||||
|
|
||||||
if (options.limit) {
|
if (options.limit) {
|
||||||
options.limit = parseInt(options.limit) || 15;
|
options.limit = parseInt(options.limit) || 15;
|
||||||
@ -329,7 +331,14 @@ Post = ghostBookshelf.Model.extend({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return when(fetchTagQuery())
|
function fetchAuthorQuery() {
|
||||||
|
if (authorInstance) {
|
||||||
|
return authorInstance.fetch();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return when.join(fetchTagQuery(), fetchAuthorQuery())
|
||||||
|
|
||||||
// Set the limit & offset for the query, fetching
|
// Set the limit & offset for the query, fetching
|
||||||
// with the opts (to specify any eager relations, etc.)
|
// with the opts (to specify any eager relations, etc.)
|
||||||
@ -344,6 +353,11 @@ Post = ghostBookshelf.Model.extend({
|
|||||||
.query('join', 'posts_tags', 'posts_tags.post_id', '=', 'posts.id')
|
.query('join', 'posts_tags', 'posts_tags.post_id', '=', 'posts.id')
|
||||||
.query('where', 'posts_tags.tag_id', '=', tagInstance.id);
|
.query('where', 'posts_tags.tag_id', '=', tagInstance.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (authorInstance) {
|
||||||
|
postCollection
|
||||||
|
.query('where', 'author_id', '=', authorInstance.id);
|
||||||
|
}
|
||||||
return postCollection
|
return postCollection
|
||||||
.query('limit', options.limit)
|
.query('limit', options.limit)
|
||||||
.query('offset', options.limit * (options.page - 1))
|
.query('offset', options.limit * (options.page - 1))
|
||||||
@ -371,6 +385,9 @@ Post = ghostBookshelf.Model.extend({
|
|||||||
qb.join('posts_tags', 'posts_tags.post_id', '=', 'posts.id');
|
qb.join('posts_tags', 'posts_tags.post_id', '=', 'posts.id');
|
||||||
qb.where('posts_tags.tag_id', '=', tagInstance.id);
|
qb.where('posts_tags.tag_id', '=', tagInstance.id);
|
||||||
}
|
}
|
||||||
|
if (authorInstance) {
|
||||||
|
qb.where('author_id', '=', authorInstance.id);
|
||||||
|
}
|
||||||
|
|
||||||
return qb.count(tableName + '.' + idAttribute + ' as aggregate');
|
return qb.count(tableName + '.' + idAttribute + ' as aggregate');
|
||||||
})
|
})
|
||||||
@ -419,6 +436,13 @@ Post = ghostBookshelf.Model.extend({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (authorInstance) {
|
||||||
|
meta.filters = {};
|
||||||
|
if (!authorInstance.isNew()) {
|
||||||
|
meta.filters.author = authorInstance.toJSON();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
})
|
})
|
||||||
.catch(errors.logAndThrowError);
|
.catch(errors.logAndThrowError);
|
||||||
|
@ -20,11 +20,19 @@ frontendRoutes = function () {
|
|||||||
res.redirect(301, subdir + '/rss/');
|
res.redirect(301, subdir + '/rss/');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Tags
|
||||||
router.get('/tag/:slug/rss/', frontend.rss);
|
router.get('/tag/:slug/rss/', frontend.rss);
|
||||||
router.get('/tag/:slug/rss/:page/', frontend.rss);
|
router.get('/tag/:slug/rss/:page/', frontend.rss);
|
||||||
router.get('/tag/:slug/page/:page/', frontend.tag);
|
router.get('/tag/:slug/page/:page/', frontend.tag);
|
||||||
router.get('/tag/:slug/', frontend.tag);
|
router.get('/tag/:slug/', frontend.tag);
|
||||||
|
|
||||||
|
// Authors
|
||||||
|
router.get('/author/:slug/rss/', frontend.rss);
|
||||||
|
router.get('/author/:slug/rss/:page/', frontend.rss);
|
||||||
|
router.get('/author/:slug/page/:page/', frontend.author);
|
||||||
|
router.get('/author/:slug/', frontend.author);
|
||||||
|
|
||||||
|
// Default
|
||||||
router.get('/page/:page/', frontend.homepage);
|
router.get('/page/:page/', frontend.homepage);
|
||||||
router.get('/', frontend.homepage);
|
router.get('/', frontend.homepage);
|
||||||
router.get('*', frontend.single);
|
router.get('*', frontend.single);
|
||||||
|
@ -179,13 +179,21 @@ describe('Core Helpers', function () {
|
|||||||
should.exist(handlebars.helpers.author);
|
should.exist(handlebars.helpers.author);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Returns the full name of the author from the context', function () {
|
it('Returns the link to the author from the context', function () {
|
||||||
var data = {'author': {'name': 'abc123'}},
|
var data = {'author': {'name': 'abc 123', slug: 'abc123', bio: '', website: '', status: '', location: ''}},
|
||||||
result = helpers.author.call(data);
|
result = helpers.author.call(data);
|
||||||
|
|
||||||
String(result).should.equal('abc123');
|
String(result).should.equal('<a href="/author/abc123/">abc 123</a>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Returns the full name of the author from the context if no autolink', function () {
|
||||||
|
var data = {'author': {'name': 'abc 123', slug: 'abc123'}},
|
||||||
|
result = helpers.author.call(data, {hash: {autolink: 'false'}});
|
||||||
|
|
||||||
|
String(result).should.equal('abc 123');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('Returns a blank string where author data is missing', function () {
|
it('Returns a blank string where author data is missing', function () {
|
||||||
var data = {'author': null},
|
var data = {'author': null},
|
||||||
result = helpers.author.call(data);
|
result = helpers.author.call(data);
|
||||||
@ -894,8 +902,7 @@ describe('Core Helpers', function () {
|
|||||||
it('can autolink tags to tag pages', function () {
|
it('can autolink tags to tag pages', function () {
|
||||||
var tags = [{name: 'foo', slug: 'foo-bar'}, {name: 'bar', slug: 'bar'}],
|
var tags = [{name: 'foo', slug: 'foo-bar'}, {name: 'bar', slug: 'bar'}],
|
||||||
rendered = handlebars.helpers.tags.call(
|
rendered = handlebars.helpers.tags.call(
|
||||||
{tags: tags},
|
{tags: tags}
|
||||||
{'hash': {}}
|
|
||||||
);
|
);
|
||||||
should.exist(rendered);
|
should.exist(rendered);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user