mirror of
https://github.com/TryGhost/Ghost.git
synced 2023-12-13 21:00:40 +01:00
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
This commit is contained in:
parent
4378495e8e
commit
c02ebb0dcf
52 changed files with 1522 additions and 1185 deletions
|
@ -15,11 +15,11 @@ api.notifications = require('./notifications');
|
|||
api.settings = require('./settings');
|
||||
|
||||
db = {
|
||||
'exportContent': function () {
|
||||
var self = this;
|
||||
'exportContent': function (options) {
|
||||
options = options || {};
|
||||
|
||||
// Export data, otherwise send error 500
|
||||
return canThis(self.user).exportContent.db().then(function () {
|
||||
return canThis(options.context).exportContent.db().then(function () {
|
||||
return dataExport().then(function (exportedData) {
|
||||
return when.resolve({ db: [exportedData] });
|
||||
}).otherwise(function (error) {
|
||||
|
@ -30,10 +30,10 @@ db = {
|
|||
});
|
||||
},
|
||||
'importContent': function (options) {
|
||||
var databaseVersion,
|
||||
self = this;
|
||||
options = options || {};
|
||||
var databaseVersion;
|
||||
|
||||
return canThis(self.user).importContent.db().then(function () {
|
||||
return canThis(options.context).importContent.db().then(function () {
|
||||
if (!options.importfile || !options.importfile.path || options.importfile.name.indexOf('json') === -1) {
|
||||
/**
|
||||
* Notify of an error if it occurs
|
||||
|
@ -46,7 +46,7 @@ db = {
|
|||
return when.reject(new errors.InternalServerError('Please select a .json file to import.'));
|
||||
}
|
||||
|
||||
return api.settings.read.call({ internal: true }, { key: 'databaseVersion' }).then(function (response) {
|
||||
return api.settings.read({key: 'databaseVersion', context: { internal: true }}).then(function (response) {
|
||||
var setting = response.settings[0];
|
||||
|
||||
return when(setting.value);
|
||||
|
@ -108,10 +108,10 @@ db = {
|
|||
return when.reject(new errors.NoPermissionError('You do not have permission to export data. (no rights)'));
|
||||
});
|
||||
},
|
||||
'deleteAllContent': function () {
|
||||
var self = this;
|
||||
'deleteAllContent': function (options) {
|
||||
options = options || {};
|
||||
|
||||
return canThis(self.user).deleteAllContent.db().then(function () {
|
||||
return canThis(options.context).deleteAllContent.db().then(function () {
|
||||
return when(dataProvider.deleteAllContent())
|
||||
.then(function () {
|
||||
return when.resolve({ db: [] });
|
||||
|
|
|
@ -4,20 +4,46 @@
|
|||
var _ = require('lodash'),
|
||||
when = require('when'),
|
||||
config = require('../config'),
|
||||
// Include Endpoints
|
||||
db = require('./db'),
|
||||
settings = require('./settings'),
|
||||
mail = require('./mail'),
|
||||
notifications = require('./notifications'),
|
||||
posts = require('./posts'),
|
||||
users = require('./users'),
|
||||
settings = require('./settings'),
|
||||
tags = require('./tags'),
|
||||
themes = require('./themes'),
|
||||
mail = require('./mail'),
|
||||
requestHandler,
|
||||
init;
|
||||
users = require('./users'),
|
||||
|
||||
// ## Request Handlers
|
||||
http,
|
||||
formatHttpErrors,
|
||||
cacheInvalidationHeader,
|
||||
locationHeader,
|
||||
contentDispositionHeader,
|
||||
init,
|
||||
|
||||
function cacheInvalidationHeader(req, result) {
|
||||
/**
|
||||
* ### Init
|
||||
* Initialise the API - populate the settings cache
|
||||
* @return {Promise(Settings)} Resolves to Settings Collection
|
||||
*/
|
||||
init = function () {
|
||||
return settings.updateSettingsCache();
|
||||
};
|
||||
|
||||
/**
|
||||
* ### Cache Invalidation Header
|
||||
* Calculate the header string for the X-Cache-Invalidate: header.
|
||||
* The resulting string instructs any cache in front of the blog that request has occurred which invalidates any cached
|
||||
* versions of the listed URIs.
|
||||
*
|
||||
* `/*` is used to mean the entire cache is invalid
|
||||
*
|
||||
* @private
|
||||
* @param {Express.request} req Original HTTP Request
|
||||
* @param {Object} result API method result
|
||||
* @return {Promise(String)} Resolves to header string
|
||||
*/
|
||||
cacheInvalidationHeader = function (req, result) {
|
||||
var parsedUrl = req._parsedUrl.pathname.replace(/\/$/, '').split('/'),
|
||||
method = req.method,
|
||||
endpoint = parsedUrl[4],
|
||||
|
@ -54,15 +80,20 @@ function cacheInvalidationHeader(req, result) {
|
|||
}
|
||||
|
||||
return when(cacheInvalidate);
|
||||
}
|
||||
};
|
||||
|
||||
// if api request results in the creation of a new object, construct
|
||||
// a Location: header that points to the new resource.
|
||||
//
|
||||
// arguments: request object, result object from the api call
|
||||
// returns: a promise that will be fulfilled with the location of the
|
||||
// resource
|
||||
function locationHeader(req, result) {
|
||||
/**
|
||||
* ### Location Header
|
||||
*
|
||||
* If the API request results in the creation of a new object, construct a Location: header which points to the new
|
||||
* resource.
|
||||
*
|
||||
* @private
|
||||
* @param {Express.request} req Original HTTP Request
|
||||
* @param {Object} result API method result
|
||||
* @return {Promise(String)} Resolves to header string
|
||||
*/
|
||||
locationHeader = function (req, result) {
|
||||
var apiRoot = config.urlFor('api'),
|
||||
location,
|
||||
post,
|
||||
|
@ -81,98 +112,137 @@ function locationHeader(req, result) {
|
|||
}
|
||||
|
||||
return when(location);
|
||||
}
|
||||
};
|
||||
|
||||
// create a header that invokes the 'Save As' dialog
|
||||
// in the browser when exporting the database to file.
|
||||
// The 'filename' parameter is governed by [RFC6266](http://tools.ietf.org/html/rfc6266#section-4.3).
|
||||
//
|
||||
// for encoding whitespace and non-ISO-8859-1 characters, you MUST
|
||||
// use the "filename*=" attribute, NOT "filename=". Ideally, both.
|
||||
// see: http://tools.ietf.org/html/rfc598
|
||||
// examples: http://tools.ietf.org/html/rfc6266#section-5
|
||||
//
|
||||
// we'll use ISO-8859-1 characters here to keep it simple.
|
||||
function dbExportSaveAsHeader() {
|
||||
/**
|
||||
* ### Content Disposition Header
|
||||
* create a header that invokes the 'Save As' dialog in the browser when exporting the database to file. The 'filename'
|
||||
* parameter is governed by [RFC6266](http://tools.ietf.org/html/rfc6266#section-4.3).
|
||||
*
|
||||
* For encoding whitespace and non-ISO-8859-1 characters, you MUST use the "filename*=" attribute, NOT "filename=".
|
||||
* Ideally, both. Examples: http://tools.ietf.org/html/rfc6266#section-5
|
||||
*
|
||||
* We'll use ISO-8859-1 characters here to keep it simple.
|
||||
*
|
||||
* @private
|
||||
* @see http://tools.ietf.org/html/rfc598
|
||||
* @return {string}
|
||||
*/
|
||||
contentDispositionHeader = function () {
|
||||
// replace ':' with '_' for OS that don't support it
|
||||
var now = (new Date()).toJSON().replace(/:/g, '_');
|
||||
return 'Attachment; filename="ghost-' + now + '.json"';
|
||||
}
|
||||
};
|
||||
|
||||
// ### requestHandler
|
||||
// decorator for api functions which are called via an HTTP request
|
||||
// takes the API method and wraps it so that it gets data from the request and returns a sensible JSON response
|
||||
requestHandler = function (apiMethod) {
|
||||
|
||||
/**
|
||||
* ### Format HTTP Errors
|
||||
* Converts the error response from the API into a format which can be returned over HTTP
|
||||
*
|
||||
* @private
|
||||
* @param {Array} error
|
||||
* @return {{errors: Array, statusCode: number}}
|
||||
*/
|
||||
formatHttpErrors = function (error) {
|
||||
var statusCode = 500,
|
||||
errors = [];
|
||||
|
||||
if (!_.isArray(error)) {
|
||||
error = [].concat(error);
|
||||
}
|
||||
|
||||
_.each(error, function (errorItem) {
|
||||
var errorContent = {};
|
||||
|
||||
//TODO: add logic to set the correct status code
|
||||
statusCode = errorItem.code || 500;
|
||||
|
||||
errorContent.message = _.isString(errorItem) ? errorItem :
|
||||
(_.isObject(errorItem) ? errorItem.message : 'Unknown API Error');
|
||||
errorContent.type = errorItem.type || 'InternalServerError';
|
||||
errors.push(errorContent);
|
||||
});
|
||||
|
||||
return {errors: errors, statusCode: statusCode};
|
||||
};
|
||||
|
||||
/**
|
||||
* ### HTTP
|
||||
*
|
||||
* Decorator for API functions which are called via an HTTP request. Takes the API method and wraps it so that it gets
|
||||
* data from the request and returns a sensible JSON response.
|
||||
*
|
||||
* @public
|
||||
* @param {Function} apiMethod API method to call
|
||||
* @return {Function} middleware format function to be called by the route when a matching request is made
|
||||
*/
|
||||
http = function (apiMethod) {
|
||||
return function (req, res) {
|
||||
var options = _.extend(req.body, req.files, req.query, req.params),
|
||||
apiContext = {
|
||||
user: (req.session && req.session.user) ? req.session.user : null
|
||||
};
|
||||
|
||||
return apiMethod.call(apiContext, options).then(function (result) {
|
||||
return cacheInvalidationHeader(req, result).then(function (header) {
|
||||
if (header) {
|
||||
res.set({
|
||||
"X-Cache-Invalidate": header
|
||||
});
|
||||
// We define 2 properties for using as arguments in API calls:
|
||||
var object = req.body,
|
||||
options = _.extend({}, req.files, req.query, req.params, {
|
||||
context: {
|
||||
user: (req.session && req.session.user) ? req.session.user : null
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
if (apiMethod === db.exportContent) {
|
||||
res.set({
|
||||
"Content-Disposition": dbExportSaveAsHeader()
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
return locationHeader(req, result).then(function (header) {
|
||||
if (header) {
|
||||
res.set({
|
||||
'Location': header
|
||||
});
|
||||
}
|
||||
|
||||
res.json(result || {});
|
||||
});
|
||||
});
|
||||
}, function (error) {
|
||||
var errorCode,
|
||||
errors = [];
|
||||
|
||||
if (!_.isArray(error)) {
|
||||
error = [].concat(error);
|
||||
}
|
||||
|
||||
_.each(error, function (errorItem) {
|
||||
var errorContent = {};
|
||||
|
||||
//TODO: add logic to set the correct status code
|
||||
errorCode = errorItem.code || 500;
|
||||
|
||||
errorContent['message'] = _.isString(errorItem) ? errorItem : (_.isObject(errorItem) ? errorItem.message : 'Unknown API Error');
|
||||
errorContent['type'] = errorItem.type || 'InternalServerError';
|
||||
errors.push(errorContent);
|
||||
});
|
||||
|
||||
res.json(errorCode, {errors: errors});
|
||||
});
|
||||
// If this is a GET, or a DELETE, req.body should be null, so we only have options (route and query params)
|
||||
// If this is a PUT, POST, or PATCH, req.body is an object
|
||||
if (_.isEmpty(object)) {
|
||||
object = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
return apiMethod(object, options)
|
||||
// Handle adding headers
|
||||
.then(function onSuccess(result) {
|
||||
// Add X-Cache-Invalidate header
|
||||
return cacheInvalidationHeader(req, result)
|
||||
.then(function addCacheHeader(header) {
|
||||
if (header) {
|
||||
res.set({'X-Cache-Invalidate': header});
|
||||
}
|
||||
|
||||
// Add Location header
|
||||
return locationHeader(req, result);
|
||||
}).then(function addLocationHeader(header) {
|
||||
if (header) {
|
||||
res.set({'Location': header});
|
||||
}
|
||||
|
||||
// Add Content-Disposition Header
|
||||
if (apiMethod === db.exportContent) {
|
||||
res.set({
|
||||
'Content-Disposition': contentDispositionHeader()
|
||||
});
|
||||
}
|
||||
// #### Success
|
||||
// Send a properly formatting HTTP response containing the data with correct headers
|
||||
res.json(result || {});
|
||||
});
|
||||
}).catch(function onError(error) {
|
||||
// #### Error
|
||||
var httpErrors = formatHttpErrors(error);
|
||||
// Send a properly formatted HTTP response containing the errors
|
||||
res.json(httpErrors.statusCode, {errors: httpErrors.errors});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
init = function () {
|
||||
return settings.updateSettingsCache();
|
||||
};
|
||||
|
||||
// Public API
|
||||
/**
|
||||
* ## Public API
|
||||
*/
|
||||
module.exports = {
|
||||
posts: posts,
|
||||
users: users,
|
||||
tags: tags,
|
||||
themes: themes,
|
||||
notifications: notifications,
|
||||
settings: settings,
|
||||
// Extras
|
||||
init: init,
|
||||
http: http,
|
||||
// API Endpoints
|
||||
db: db,
|
||||
mail: mail,
|
||||
requestHandler: requestHandler,
|
||||
init: init
|
||||
notifications: notifications,
|
||||
posts: posts,
|
||||
settings: settings,
|
||||
tags: tags,
|
||||
themes: themes,
|
||||
users: users
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ var when = require('when'),
|
|||
|
||||
// Holds the persistent notifications
|
||||
notificationsStore = [],
|
||||
// Holds the last used id
|
||||
// Holds the last used id
|
||||
notificationCounter = 0,
|
||||
notifications;
|
||||
|
||||
|
@ -15,16 +15,15 @@ notifications = {
|
|||
return when({ 'notifications': notificationsStore });
|
||||
},
|
||||
|
||||
// #### Destroy
|
||||
|
||||
// **takes:** an identifier object ({id: id})
|
||||
destroy: function destroy(i) {
|
||||
destroy: function destroy(options) {
|
||||
var notification = _.find(notificationsStore, function (element) {
|
||||
return element.id === parseInt(i.id, 10);
|
||||
return element.id === parseInt(options.id, 10);
|
||||
});
|
||||
|
||||
if (notification && !notification.dismissable) {
|
||||
return when.reject(new errors.NoPermissionError('You do not have permission to dismiss this notification.'));
|
||||
return when.reject(
|
||||
new errors.NoPermissionError('You do not have permission to dismiss this notification.')
|
||||
);
|
||||
}
|
||||
|
||||
if (!notification) {
|
||||
|
@ -32,7 +31,7 @@ notifications = {
|
|||
}
|
||||
|
||||
notificationsStore = _.reject(notificationsStore, function (element) {
|
||||
return element.id === parseInt(i.id, 10);
|
||||
return element.id === parseInt(options.id, 10);
|
||||
});
|
||||
// **returns:** a promise for the deleted object
|
||||
return when({notifications: [notification]});
|
||||
|
@ -44,17 +43,20 @@ notifications = {
|
|||
return when(notificationsStore);
|
||||
},
|
||||
|
||||
// #### Add
|
||||
|
||||
// **takes:** a notification object of the form
|
||||
// ```
|
||||
// msg = {
|
||||
// type: 'error', // this can be 'error', 'success', 'warn' and 'info'
|
||||
// message: 'This is an error', // A string. Should fit in one line.
|
||||
// location: 'bottom', // A string where this notification should appear. can be 'bottom' or 'top'
|
||||
// dismissable: true // A Boolean. Whether the notification is dismissable or not.
|
||||
// };
|
||||
// ```
|
||||
/**
|
||||
* ### Add
|
||||
*
|
||||
*
|
||||
* **takes:** a notification object of the form
|
||||
* ```
|
||||
* msg = {
|
||||
* type: 'error', // this can be 'error', 'success', 'warn' and 'info'
|
||||
* message: 'This is an error', // A string. Should fit in one line.
|
||||
* location: 'bottom', // A string where this notification should appear. can be 'bottom' or 'top'
|
||||
* dismissable: true // A Boolean. Whether the notification is dismissable or not.
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
add: function add(notification) {
|
||||
|
||||
var defaults = {
|
||||
|
@ -64,7 +66,7 @@ notifications = {
|
|||
};
|
||||
|
||||
notificationCounter = notificationCounter + 1;
|
||||
|
||||
|
||||
notification = _.assign(defaults, notification, {
|
||||
id: notificationCounter
|
||||
//status: 'persistent'
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
var when = require('when'),
|
||||
_ = require('lodash'),
|
||||
dataProvider = require('../models'),
|
||||
canThis = require('../permissions').canThis,
|
||||
errors = require('../errors'),
|
||||
// # Posts API
|
||||
var when = require('when'),
|
||||
_ = require('lodash'),
|
||||
dataProvider = require('../models'),
|
||||
canThis = require('../permissions').canThis,
|
||||
errors = require('../errors'),
|
||||
utils = require('./utils'),
|
||||
|
||||
allowedIncludes = ['created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields'],
|
||||
docName = 'posts',
|
||||
allowedIncludes = ['created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields'],
|
||||
posts;
|
||||
|
||||
function checkPostData(postData) {
|
||||
if (_.isEmpty(postData) || _.isEmpty(postData.posts) || _.isEmpty(postData.posts[0])) {
|
||||
return when.reject(new errors.BadRequestError('No root key (\'posts\') provided.'));
|
||||
}
|
||||
return when.resolve(postData);
|
||||
}
|
||||
|
||||
// ## Helpers
|
||||
function prepareInclude(include) {
|
||||
var index;
|
||||
|
||||
include = _.intersection(include.split(","), allowedIncludes);
|
||||
include = _.intersection(include.split(','), allowedIncludes);
|
||||
index = include.indexOf('author');
|
||||
|
||||
if (index !== -1) {
|
||||
|
@ -27,16 +24,20 @@ function prepareInclude(include) {
|
|||
return include;
|
||||
}
|
||||
|
||||
// ## Posts
|
||||
// ## API Methods
|
||||
posts = {
|
||||
|
||||
// #### Browse
|
||||
// **takes:** filter / pagination parameters
|
||||
/**
|
||||
* ### Browse
|
||||
* Find a paginated set of posts
|
||||
* @param {{context, page, limit, status, staticPages, tag}} options (optional)
|
||||
* @returns {Promise(Posts)} Posts Collection with Meta
|
||||
*/
|
||||
browse: function browse(options) {
|
||||
options = options || {};
|
||||
|
||||
|
||||
// only published posts if no user is present
|
||||
if (!this.user) {
|
||||
if (!(options.context && options.context.user)) {
|
||||
options.status = 'published';
|
||||
}
|
||||
|
||||
|
@ -44,28 +45,30 @@ posts = {
|
|||
options.include = prepareInclude(options.include);
|
||||
}
|
||||
|
||||
// **returns:** a promise for a page of posts in a json object
|
||||
return dataProvider.Post.findPage(options);
|
||||
},
|
||||
|
||||
// #### Read
|
||||
// **takes:** an identifier (id or slug?)
|
||||
/**
|
||||
* ### Read
|
||||
* Find a post, by ID or Slug
|
||||
* @param {{id_or_slug (required), context, status, include, ...}} options
|
||||
* @return {Promise(Post)} Post
|
||||
*/
|
||||
read: function read(options) {
|
||||
var include;
|
||||
options = options || {};
|
||||
var attrs = ['id', 'slug', 'status'],
|
||||
data = _.pick(options, attrs);
|
||||
options = _.omit(options, attrs);
|
||||
|
||||
// only published posts if no user is present
|
||||
if (!this.user) {
|
||||
options.status = 'published';
|
||||
if (!(options.context && options.context.user)) {
|
||||
data.status = 'published';
|
||||
}
|
||||
|
||||
if (options.include) {
|
||||
include = prepareInclude(options.include);
|
||||
delete options.include;
|
||||
options.include = prepareInclude(options.include);
|
||||
}
|
||||
|
||||
// **returns:** a promise for a single post in a json object
|
||||
return dataProvider.Post.findOne(options, {include: include}).then(function (result) {
|
||||
return dataProvider.Post.findOne(data, options).then(function (result) {
|
||||
if (result) {
|
||||
return { posts: [ result.toJSON() ]};
|
||||
}
|
||||
|
@ -75,21 +78,21 @@ posts = {
|
|||
});
|
||||
},
|
||||
|
||||
// #### Edit
|
||||
// **takes:** a json object with all the properties which should be updated
|
||||
edit: function edit(postData) {
|
||||
// **returns:** a promise for the resulting post in a json object
|
||||
var self = this,
|
||||
include;
|
||||
|
||||
return canThis(this).edit.post(postData.id).then(function () {
|
||||
return checkPostData(postData).then(function (checkedPostData) {
|
||||
|
||||
if (postData.include) {
|
||||
include = prepareInclude(postData.include);
|
||||
/**
|
||||
* ### Edit
|
||||
* Update properties of a post
|
||||
* @param {Post} object Post or specific properties to update
|
||||
* @param {{id (required), context, include,...}} options
|
||||
* @return {Promise(Post)} Edited Post
|
||||
*/
|
||||
edit: function edit(object, options) {
|
||||
return canThis(options.context).edit.post(options.id).then(function () {
|
||||
return utils.checkObject(object, docName).then(function (checkedPostData) {
|
||||
if (options.include) {
|
||||
options.include = prepareInclude(options.include);
|
||||
}
|
||||
|
||||
return dataProvider.Post.edit(checkedPostData.posts[0], {user: self.user, include: include});
|
||||
return dataProvider.Post.edit(checkedPostData.posts[0], options);
|
||||
}).then(function (result) {
|
||||
if (result) {
|
||||
var post = result.toJSON();
|
||||
|
@ -108,20 +111,23 @@ posts = {
|
|||
});
|
||||
},
|
||||
|
||||
// #### Add
|
||||
// **takes:** a json object representing a post,
|
||||
add: function add(postData) {
|
||||
var self = this,
|
||||
include;
|
||||
/**
|
||||
* ### Add
|
||||
* Create a new post along with any tags
|
||||
* @param {Post} object
|
||||
* @param {{context, include,...}} options
|
||||
* @return {Promise(Post)} Created Post
|
||||
*/
|
||||
add: function add(object, options) {
|
||||
options = options || {};
|
||||
|
||||
// **returns:** a promise for the resulting post in a json object
|
||||
return canThis(this).create.post().then(function () {
|
||||
return checkPostData(postData).then(function (checkedPostData) {
|
||||
if (postData.include) {
|
||||
include = prepareInclude(postData.include);
|
||||
return canThis(options.context).create.post().then(function () {
|
||||
return utils.checkObject(object, docName).then(function (checkedPostData) {
|
||||
if (options.include) {
|
||||
options.include = prepareInclude(options.include);
|
||||
}
|
||||
|
||||
return dataProvider.Post.add(checkedPostData.posts[0], {user: self.user, include: include});
|
||||
return dataProvider.Post.add(checkedPostData.posts[0], options);
|
||||
}).then(function (result) {
|
||||
var post = result.toJSON();
|
||||
|
||||
|
@ -136,15 +142,18 @@ posts = {
|
|||
});
|
||||
},
|
||||
|
||||
// #### Destroy
|
||||
// **takes:** an identifier (id or slug?)
|
||||
destroy: function destroy(args) {
|
||||
var self = this;
|
||||
// **returns:** a promise for a json response with the id of the deleted post
|
||||
return canThis(this).remove.post(args.id).then(function () {
|
||||
// TODO: Would it be good to get rid of .call()?
|
||||
return posts.read.call({user: self.user}, {id : args.id, status: 'all'}).then(function (result) {
|
||||
return dataProvider.Post.destroy(args.id).then(function () {
|
||||
|
||||
/**
|
||||
* ### Destroy
|
||||
* Delete a post, cleans up tag relations, but not unused tags
|
||||
* @param {{id (required), context,...}} options
|
||||
* @return {Promise(Post)} Deleted Post
|
||||
*/
|
||||
destroy: function destroy(options) {
|
||||
return canThis(options.context).remove.post(options.id).then(function () {
|
||||
var readOptions = _.extend({}, options, {status: 'all'});
|
||||
return posts.read(readOptions).then(function (result) {
|
||||
return dataProvider.Post.destroy(options).then(function () {
|
||||
var deletedObj = result;
|
||||
|
||||
if (deletedObj.posts) {
|
||||
|
@ -161,17 +170,21 @@ posts = {
|
|||
});
|
||||
},
|
||||
|
||||
// #### Generate slug
|
||||
// **takes:** a string to generate the slug from
|
||||
generateSlug: function generateSlug(args) {
|
||||
|
||||
return canThis(this).slug.post().then(function () {
|
||||
return dataProvider.Base.Model.generateSlug(dataProvider.Post, args.title, {status: 'all'}).then(function (slug) {
|
||||
if (slug) {
|
||||
return slug;
|
||||
}
|
||||
return when.reject(new errors.InternalServerError('Could not generate slug'));
|
||||
});
|
||||
/**
|
||||
* ## Generate Slug
|
||||
* Create a unique slug for a given post title
|
||||
* @param {{title (required), transacting}} options
|
||||
* @returns {Promise(String)} Unique string
|
||||
*/
|
||||
generateSlug: function generateSlug(options) {
|
||||
return canThis(options.context).slug.post().then(function () {
|
||||
return dataProvider.Base.Model.generateSlug(dataProvider.Post, options.title, {status: 'all'})
|
||||
.then(function (slug) {
|
||||
if (slug) {
|
||||
return slug;
|
||||
}
|
||||
return when.reject(new errors.InternalServerError('Could not generate slug'));
|
||||
});
|
||||
}, function () {
|
||||
return when.reject(new errors.NoPermissionError('You do not have permission.'));
|
||||
});
|
||||
|
|
|
@ -1,33 +1,38 @@
|
|||
// # Settings API
|
||||
var _ = require('lodash'),
|
||||
dataProvider = require('../models'),
|
||||
when = require('when'),
|
||||
config = require('../config'),
|
||||
canThis = require('../permissions').canThis,
|
||||
errors = require('../errors'),
|
||||
utils = require('./utils'),
|
||||
|
||||
docName = 'settings',
|
||||
settings,
|
||||
settingsFilter,
|
||||
|
||||
updateSettingsCache,
|
||||
readSettingsResult,
|
||||
settingsFilter,
|
||||
filterPaths,
|
||||
readSettingsResult,
|
||||
settingsResult,
|
||||
// Holds cached settings
|
||||
canEditAllSettings,
|
||||
|
||||
/**
|
||||
* ## Cache
|
||||
* Holds cached settings
|
||||
* @private
|
||||
* @type {{}}
|
||||
*/
|
||||
settingsCache = {};
|
||||
|
||||
// ### Helpers
|
||||
|
||||
// Filters an object based on a given filter object
|
||||
settingsFilter = function (settings, filter) {
|
||||
return _.object(_.filter(_.pairs(settings), function (setting) {
|
||||
if (filter) {
|
||||
return _.some(filter.split(','), function (f) {
|
||||
return setting[1].type === f;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
};
|
||||
|
||||
// Maintain the internal cache of the settings object
|
||||
/**
|
||||
* ### Update Settings Cache
|
||||
* Maintain the internal cache of the settings object
|
||||
* @public
|
||||
* @param settings
|
||||
* @returns {Settings}
|
||||
*/
|
||||
updateSettingsCache = function (settings) {
|
||||
settings = settings || {};
|
||||
|
||||
|
@ -47,6 +52,78 @@ updateSettingsCache = function (settings) {
|
|||
});
|
||||
};
|
||||
|
||||
// ## Helpers
|
||||
|
||||
/**
|
||||
* ### Settings Filter
|
||||
* Filters an object based on a given filter object
|
||||
* @private
|
||||
* @param settings
|
||||
* @param filter
|
||||
* @returns {*}
|
||||
*/
|
||||
settingsFilter = function (settings, filter) {
|
||||
return _.object(_.filter(_.pairs(settings), function (setting) {
|
||||
if (filter) {
|
||||
return _.some(filter.split(','), function (f) {
|
||||
return setting[1].type === f;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* ### Filter Paths
|
||||
* Normalizes paths read by require-tree so that the apps and themes modules can use them. Creates an empty
|
||||
* array (res), and populates it with useful info about the read packages like name, whether they're active
|
||||
* (comparison with the second argument), and if they have a package.json, that, otherwise false
|
||||
* @private
|
||||
* @param {object} paths as returned by require-tree()
|
||||
* @param {array/string} active as read from the settings object
|
||||
* @returns {Array} of objects with useful info about apps / themes
|
||||
*/
|
||||
filterPaths = function (paths, active) {
|
||||
var pathKeys = Object.keys(paths),
|
||||
res = [],
|
||||
item;
|
||||
|
||||
// turn active into an array (so themes and apps can be checked the same)
|
||||
if (!Array.isArray(active)) {
|
||||
active = [active];
|
||||
}
|
||||
|
||||
_.each(pathKeys, function (key) {
|
||||
//do not include hidden files or _messages
|
||||
if (key.indexOf('.') !== 0 &&
|
||||
key !== '_messages' &&
|
||||
key !== 'README.md'
|
||||
) {
|
||||
item = {
|
||||
name: key
|
||||
};
|
||||
if (paths[key].hasOwnProperty('package.json')) {
|
||||
item.package = paths[key]['package.json'];
|
||||
} else {
|
||||
item.package = false;
|
||||
}
|
||||
|
||||
if (_.indexOf(active, key) !== -1) {
|
||||
item.active = true;
|
||||
}
|
||||
res.push(item);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* ### Read Settings Result
|
||||
* @private
|
||||
* @param settingsModels
|
||||
* @returns {Settings}
|
||||
*/
|
||||
readSettingsResult = function (settingsModels) {
|
||||
var settings = _.reduce(settingsModels, function (memo, member) {
|
||||
if (!memo.hasOwnProperty(member.attributes.key)) {
|
||||
|
@ -82,79 +159,78 @@ readSettingsResult = function (settingsModels) {
|
|||
return settings;
|
||||
};
|
||||
|
||||
|
||||
// Normalizes paths read by require-tree so that the apps and themes modules can use them.
|
||||
// Creates an empty array (res), and populates it with useful info about the read packages
|
||||
// like name, whether they're active (comparison with the second argument), and if they
|
||||
// have a package.json, that, otherwise false
|
||||
// @param {object} paths as returned by require-tree()
|
||||
// @param {array/string} active as read from the settings object
|
||||
// @return {array} of objects with useful info about apps / themes
|
||||
|
||||
filterPaths = function (paths, active) {
|
||||
var pathKeys = Object.keys(paths),
|
||||
res = [],
|
||||
item;
|
||||
|
||||
// turn active into an array (so themes and apps can be checked the same)
|
||||
if (!Array.isArray(active)) {
|
||||
active = [active];
|
||||
}
|
||||
|
||||
_.each(pathKeys, function (key) {
|
||||
//do not include hidden files or _messages
|
||||
if (key.indexOf('.') !== 0 &&
|
||||
key !== '_messages' &&
|
||||
key !== 'README.md'
|
||||
) {
|
||||
item = {
|
||||
name: key
|
||||
};
|
||||
if (paths[key].hasOwnProperty('package.json')) {
|
||||
item.package = paths[key]['package.json'];
|
||||
} else {
|
||||
item.package = false;
|
||||
}
|
||||
|
||||
if (_.indexOf(active, key) !== -1) {
|
||||
item.active = true;
|
||||
}
|
||||
res.push(item);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* ### Settings Result
|
||||
* @private
|
||||
* @param settings
|
||||
* @param type
|
||||
* @returns {{settings: *}}
|
||||
*/
|
||||
settingsResult = function (settings, type) {
|
||||
var filteredSettings = _.values(settingsFilter(settings, type)),
|
||||
result = {
|
||||
settings: filteredSettings
|
||||
settings: filteredSettings,
|
||||
meta: {}
|
||||
};
|
||||
|
||||
if (type) {
|
||||
result.meta = {
|
||||
filters: {
|
||||
type: type
|
||||
}
|
||||
result.meta.filters = {
|
||||
type: type
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* ### Can Edit All Settings
|
||||
* Check that this edit request is allowed for all settings requested to be updated
|
||||
* @private
|
||||
* @param settingsInfo
|
||||
* @returns {*}
|
||||
*/
|
||||
canEditAllSettings = function (settingsInfo, options) {
|
||||
var checks = _.map(settingsInfo, function (settingInfo) {
|
||||
var setting = settingsCache[settingInfo.key];
|
||||
|
||||
if (!setting) {
|
||||
return when.reject(new errors.NotFoundError('Unable to find setting: ' + settingInfo.key));
|
||||
}
|
||||
|
||||
if (setting.type === 'core' && !(options.context && options.context.internal)) {
|
||||
return when.reject(
|
||||
new errors.NoPermissionError('Attempted to access core setting from external request')
|
||||
);
|
||||
}
|
||||
|
||||
return canThis(options.context).edit.setting(settingInfo.key);
|
||||
});
|
||||
|
||||
return when.all(checks);
|
||||
};
|
||||
|
||||
// ## API Methods
|
||||
settings = {
|
||||
// #### Browse
|
||||
|
||||
// **takes:** options object
|
||||
/**
|
||||
* ### Browse
|
||||
* @param options
|
||||
* @returns {*}
|
||||
*/
|
||||
browse: function browse(options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
|
||||
// **returns:** a promise for a settings json object
|
||||
return canThis(this).browse.setting().then(function () {
|
||||
var result = settingsResult(settingsCache, options.type);
|
||||
var result = settingsResult(settingsCache, options.type);
|
||||
|
||||
// If there is no context, return only blog settings
|
||||
if (!options.context) {
|
||||
return when(_.filter(result.settings, function (setting) { return setting.type === 'blog'; }));
|
||||
}
|
||||
|
||||
// Otherwise return whatever this context is allowed to browse
|
||||
return canThis(options.context).browse.setting().then(function () {
|
||||
// Omit core settings unless internal request
|
||||
if (!self.internal) {
|
||||
if (!options.context.internal) {
|
||||
result.settings = _.filter(result.settings, function (setting) { return setting.type !== 'core'; });
|
||||
}
|
||||
|
||||
|
@ -162,90 +238,88 @@ settings = {
|
|||
});
|
||||
},
|
||||
|
||||
// #### Read
|
||||
|
||||
// **takes:** either a json object containing a key, or a single key string
|
||||
/**
|
||||
* ### Read
|
||||
* @param options
|
||||
* @returns {*}
|
||||
*/
|
||||
read: function read(options) {
|
||||
if (_.isString(options)) {
|
||||
options = { key: options };
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var setting = settingsCache[options.key],
|
||||
result = {};
|
||||
|
||||
return canThis(this).read.setting(options.key).then(function () {
|
||||
var setting = settingsCache[options.key],
|
||||
result = {};
|
||||
if (!setting) {
|
||||
return when.reject(new errors.NotFoundError('Unable to find setting: ' + options.key));
|
||||
}
|
||||
|
||||
if (!setting) {
|
||||
return when.reject(new errors.NotFoundError('Unable to find setting: ' + options.key));
|
||||
}
|
||||
result[options.key] = setting;
|
||||
|
||||
if (!self.internal && setting.type === 'core') {
|
||||
return when.reject(new errors.NoPermissionError('Attempted to access core setting on external request'));
|
||||
}
|
||||
if (setting.type === 'core' && !(options.context && options.context.internal)) {
|
||||
return when.reject(
|
||||
new errors.NoPermissionError('Attempted to access core setting from external request')
|
||||
);
|
||||
}
|
||||
|
||||
result[options.key] = setting;
|
||||
if (setting.type === 'blog') {
|
||||
return when(settingsResult(result));
|
||||
}
|
||||
|
||||
return canThis(options.context).read.setting(options.key).then(function () {
|
||||
return settingsResult(result);
|
||||
}, function () {
|
||||
return when.reject(new errors.NoPermissionError('You do not have permission to read settings.'));
|
||||
});
|
||||
},
|
||||
|
||||
// #### Edit
|
||||
|
||||
// **takes:** either a json object representing a collection of settings, or a key and value pair
|
||||
edit: function edit(key, value) {
|
||||
/**
|
||||
* ### Edit
|
||||
* Update properties of a post
|
||||
* @param {{settings: }} object Setting or a single string name
|
||||
* @param {{id (required), include,...}} options (optional) or a single string value
|
||||
* @return {Promise(Setting)} Edited Setting
|
||||
*/
|
||||
edit: function edit(object, options) {
|
||||
options = options || {};
|
||||
var self = this,
|
||||
type,
|
||||
canEditAllSettings = function (settingsInfo) {
|
||||
var checks = _.map(settingsInfo, function (settingInfo) {
|
||||
var setting = settingsCache[settingInfo.key];
|
||||
type;
|
||||
|
||||
if (!setting) {
|
||||
return when.reject(new errors.NotFoundError('Unable to find setting: ' + settingInfo.key));
|
||||
}
|
||||
|
||||
if (!self.internal && setting.type === 'core') {
|
||||
return when.reject(new errors.NoPermissionError('Attempted to access core setting on external request'));
|
||||
}
|
||||
|
||||
return canThis(self).edit.setting(settingInfo.key);
|
||||
});
|
||||
|
||||
return when.all(checks);
|
||||
};
|
||||
|
||||
if (!_.isString(value)) {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
|
||||
// Allow shorthand syntax
|
||||
if (_.isString(key)) {
|
||||
key = { settings: [{ key: key, value: value }]};
|
||||
// Allow shorthand syntax where a single key and value are passed to edit instead of object and options
|
||||
if (_.isString(object)) {
|
||||
object = { settings: [{ key: object, value: options }]};
|
||||
}
|
||||
|
||||
//clean data
|
||||
type = _.find(key.settings, function (setting) { return setting.key === 'type'; });
|
||||
_.each(object.settings, function (setting) {
|
||||
if (!_.isString(setting.value)) {
|
||||
setting.value = JSON.stringify(setting.value);
|
||||
}
|
||||
});
|
||||
|
||||
type = _.find(object.settings, function (setting) { return setting.key === 'type'; });
|
||||
if (_.isObject(type)) {
|
||||
type = type.value;
|
||||
}
|
||||
|
||||
key = _.reject(key.settings, function (setting) {
|
||||
object.settings = _.reject(object.settings, function (setting) {
|
||||
return setting.key === 'type' || setting.key === 'availableThemes' || setting.key === 'availableApps';
|
||||
});
|
||||
|
||||
return canEditAllSettings(key).then(function () {
|
||||
return dataProvider.Settings.edit(key, {user: self.user});
|
||||
}).then(function (result) {
|
||||
var readResult = readSettingsResult(result);
|
||||
return canEditAllSettings(object.settings, options).then(function () {
|
||||
return utils.checkObject(object, docName).then(function (checkedData) {
|
||||
options.user = self.user;
|
||||
return dataProvider.Settings.edit(checkedData.settings, options);
|
||||
}).then(function (result) {
|
||||
var readResult = readSettingsResult(result);
|
||||
|
||||
return updateSettingsCache(readResult).then(function () {
|
||||
return config.theme.update(settings, config().url);
|
||||
}).then(function () {
|
||||
return settingsResult(readResult, type);
|
||||
return updateSettingsCache(readResult).then(function () {
|
||||
return config.theme.update(settings, config().url);
|
||||
}).then(function () {
|
||||
return settingsResult(readResult, type);
|
||||
});
|
||||
});
|
||||
}).catch(function (error) {
|
||||
// Pass along API error
|
||||
return when.reject(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3,11 +3,8 @@ var dataProvider = require('../models'),
|
|||
|
||||
|
||||
tags = {
|
||||
// #### Browse
|
||||
// **takes:** Nothing yet
|
||||
browse: function browse() {
|
||||
// **returns:** a promise for all tags which have previously been used in a json object
|
||||
return dataProvider.Tag.findAll().then(function (result) {
|
||||
browse: function browse(options) {
|
||||
return dataProvider.Tag.findAll(options).then(function (result) {
|
||||
return { tags: result.toJSON() };
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,11 +10,12 @@ var when = require('when'),
|
|||
// ## Themes
|
||||
themes = {
|
||||
|
||||
browse: function browse() {
|
||||
// **returns:** a promise for a collection of themes in a json object
|
||||
return canThis(this).browse.theme().then(function () {
|
||||
browse: function browse(options) {
|
||||
options = options || {};
|
||||
|
||||
return canThis(options.context).browse.theme().then(function () {
|
||||
return when.all([
|
||||
settings.read.call({ internal: true }, 'activeTheme'),
|
||||
settings.read({key: 'activeTheme', context: {internal: true}}),
|
||||
config().paths.availableThemes
|
||||
]).then(function (result) {
|
||||
var activeTheme = result[0].settings[0].value,
|
||||
|
@ -49,19 +50,18 @@ themes = {
|
|||
});
|
||||
},
|
||||
|
||||
edit: function edit(themeData) {
|
||||
var self = this,
|
||||
themeName;
|
||||
edit: function edit(object, options) {
|
||||
var themeName;
|
||||
|
||||
// Check whether the request is properly formatted.
|
||||
if (!_.isArray(themeData.themes)) {
|
||||
if (!_.isArray(object.themes)) {
|
||||
return when.reject({type: 'BadRequest', message: 'Invalid request.'});
|
||||
}
|
||||
|
||||
themeName = themeData.themes[0].uuid;
|
||||
themeName = object.themes[0].uuid;
|
||||
|
||||
return canThis(this).edit.theme().then(function () {
|
||||
return themes.browse.call(self).then(function (availableThemes) {
|
||||
return canThis(options.context).edit.theme().then(function () {
|
||||
return themes.browse(options).then(function (availableThemes) {
|
||||
var theme;
|
||||
|
||||
// Check if the theme exists
|
||||
|
@ -73,8 +73,10 @@ themes = {
|
|||
return when.reject(new errors.BadRequestError('Theme does not exist.'));
|
||||
}
|
||||
|
||||
// Activate the theme
|
||||
return settings.edit.call({ internal: true }, 'activeTheme', themeName).then(function () {
|
||||
// Activate the theme
|
||||
return settings.edit(
|
||||
{settings: [{ key: 'activeTheme', value: themeName }]}, {context: {internal: true }}
|
||||
).then(function () {
|
||||
theme.active = true;
|
||||
return { themes: [theme]};
|
||||
});
|
||||
|
|
|
@ -1,27 +1,26 @@
|
|||
var when = require('when'),
|
||||
_ = require('lodash'),
|
||||
dataProvider = require('../models'),
|
||||
settings = require('./settings'),
|
||||
canThis = require('../permissions').canThis,
|
||||
errors = require('../errors'),
|
||||
ONE_DAY = 86400000,
|
||||
var when = require('when'),
|
||||
_ = require('lodash'),
|
||||
dataProvider = require('../models'),
|
||||
settings = require('./settings'),
|
||||
canThis = require('../permissions').canThis,
|
||||
errors = require('../errors'),
|
||||
utils = require('./utils'),
|
||||
|
||||
docName = 'users',
|
||||
ONE_DAY = 86400000,
|
||||
users;
|
||||
|
||||
|
||||
function checkUserData(userData) {
|
||||
if (_.isEmpty(userData) || _.isEmpty(userData.users) || _.isEmpty(userData.users[0])) {
|
||||
return when.reject(new errors.BadRequestError('No root key (\'users\') provided.'));
|
||||
}
|
||||
return when.resolve(userData);
|
||||
}
|
||||
// ## Users
|
||||
users = {
|
||||
|
||||
// #### Browse
|
||||
// **takes:** options object
|
||||
/**
|
||||
* ## Browse
|
||||
* Fetch all users
|
||||
* @param {object} options (optional)
|
||||
* @returns {Promise(Users)} Users Collection
|
||||
*/
|
||||
browse: function browse(options) {
|
||||
// **returns:** a promise for a collection of users in a json object
|
||||
return canThis(this).browse.user().then(function () {
|
||||
options = options || {};
|
||||
return canThis(options.context).browse.user().then(function () {
|
||||
return dataProvider.User.findAll(options).then(function (result) {
|
||||
return { users: result.toJSON() };
|
||||
});
|
||||
|
@ -30,15 +29,17 @@ users = {
|
|||
});
|
||||
},
|
||||
|
||||
// #### Read
|
||||
// **takes:** an identifier (id or slug?)
|
||||
read: function read(args) {
|
||||
// **returns:** a promise for a single user in a json object
|
||||
if (args.id === 'me') {
|
||||
args = {id: this.user};
|
||||
read: function read(options) {
|
||||
var attrs = ['id'],
|
||||
data = _.pick(options, attrs);
|
||||
|
||||
options = _.omit(options, attrs);
|
||||
|
||||
if (data.id === 'me' && options.context && options.context.user) {
|
||||
data.id = options.context.user;
|
||||
}
|
||||
|
||||
return dataProvider.User.findOne(args).then(function (result) {
|
||||
return dataProvider.User.findOne(data, options).then(function (result) {
|
||||
if (result) {
|
||||
return { users: [result.toJSON()] };
|
||||
}
|
||||
|
@ -47,14 +48,15 @@ users = {
|
|||
});
|
||||
},
|
||||
|
||||
// #### Edit
|
||||
// **takes:** a json object representing a user
|
||||
edit: function edit(userData) {
|
||||
// **returns:** a promise for the resulting user in a json object
|
||||
var self = this;
|
||||
return canThis(this).edit.user(userData.users[0].id).then(function () {
|
||||
return checkUserData(userData).then(function (checkedUserData) {
|
||||
return dataProvider.User.edit(checkedUserData.users[0], {user: self.user});
|
||||
edit: function edit(object, options) {
|
||||
if (options.id === 'me' && options.context && options.context.user) {
|
||||
options.id = options.context.user;
|
||||
}
|
||||
|
||||
return canThis(options.context).edit.user(options.id).then(function () {
|
||||
return utils.checkObject(object, docName).then(function (checkedUserData) {
|
||||
|
||||
return dataProvider.User.edit(checkedUserData.users[0], options);
|
||||
}).then(function (result) {
|
||||
if (result) {
|
||||
return { users: [result.toJSON()]};
|
||||
|
@ -66,19 +68,17 @@ users = {
|
|||
});
|
||||
},
|
||||
|
||||
// #### Add
|
||||
// **takes:** a json object representing a user
|
||||
add: function add(userData) {
|
||||
// **returns:** a promise for the resulting user in a json object
|
||||
var self = this;
|
||||
return canThis(this).add.user().then(function () {
|
||||
return checkUserData(userData).then(function (checkedUserData) {
|
||||
// if the user is created by users.register(), use id: 1
|
||||
// as the creator for now
|
||||
if (self.internal) {
|
||||
self.user = 1;
|
||||
add: function add(object, options) {
|
||||
options = options || {};
|
||||
|
||||
return canThis(options.context).add.user().then(function () {
|
||||
return utils.checkObject(object, docName).then(function (checkedUserData) {
|
||||
// if the user is created by users.register(), use id: 1 as the creator for now
|
||||
if (options.context.internal) {
|
||||
options.context.user = 1;
|
||||
}
|
||||
return dataProvider.User.add(checkedUserData.users[0], {user: self.user});
|
||||
|
||||
return dataProvider.User.add(checkedUserData.users[0], options);
|
||||
}).then(function (result) {
|
||||
if (result) {
|
||||
return { users: [result.toJSON()]};
|
||||
|
@ -89,47 +89,37 @@ users = {
|
|||
});
|
||||
},
|
||||
|
||||
// #### Register
|
||||
// **takes:** a json object representing a user
|
||||
register: function register(userData) {
|
||||
// TODO: if we want to prevent users from being created with the signup form
|
||||
// this is the right place to do it
|
||||
return users.add.call({internal: true}, userData);
|
||||
register: function register(object) {
|
||||
// TODO: if we want to prevent users from being created with the signup form this is the right place to do it
|
||||
return users.add(object, {context: {internal: true}});
|
||||
},
|
||||
|
||||
// #### Check
|
||||
// Checks a password matches the given email address
|
||||
|
||||
// **takes:** a json object representing a user
|
||||
check: function check(userData) {
|
||||
// **returns:** on success, returns a promise for the resulting user in a json object
|
||||
return dataProvider.User.check(userData);
|
||||
check: function check(object) {
|
||||
return dataProvider.User.check(object);
|
||||
},
|
||||
|
||||
// #### Change Password
|
||||
// **takes:** a json object representing a user
|
||||
changePassword: function changePassword(userData) {
|
||||
// **returns:** on success, returns a promise for the resulting user in a json object
|
||||
return dataProvider.User.changePassword(userData);
|
||||
changePassword: function changePassword(object) {
|
||||
return dataProvider.User.changePassword(object);
|
||||
},
|
||||
|
||||
generateResetToken: function generateResetToken(email) {
|
||||
var expires = Date.now() + ONE_DAY;
|
||||
return settings.read.call({ internal: true }, 'dbHash').then(function (response) {
|
||||
return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
|
||||
var dbHash = response.settings[0].value;
|
||||
return dataProvider.User.generateResetToken(email, expires, dbHash);
|
||||
});
|
||||
},
|
||||
|
||||
validateToken: function validateToken(token) {
|
||||
return settings.read.call({ internal: true }, 'dbHash').then(function (response) {
|
||||
return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
|
||||
var dbHash = response.settings[0].value;
|
||||
return dataProvider.User.validateToken(token, dbHash);
|
||||
});
|
||||
},
|
||||
|
||||
resetPassword: function resetPassword(token, newPassword, ne2Password) {
|
||||
return settings.read.call({ internal: true }, 'dbHash').then(function (response) {
|
||||
return settings.read({context: {internal: true}, key: 'dbHash'}).then(function (response) {
|
||||
var dbHash = response.settings[0].value;
|
||||
return dataProvider.User.resetPassword(token, newPassword, ne2Password, dbHash);
|
||||
});
|
||||
|
|
14
core/server/api/utils.js
Normal file
14
core/server/api/utils.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
var when = require('when'),
|
||||
_ = require('lodash'),
|
||||
utils;
|
||||
|
||||
utils = {
|
||||
checkObject: function (object, docName) {
|
||||
if (_.isEmpty(object) || _.isEmpty(object[docName]) || _.isEmpty(object[docName][0])) {
|
||||
return when.reject({type: 'BadRequest', message: 'No root key (\'' + docName + '\') provided.'});
|
||||
}
|
||||
return when.resolve(object);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = utils;
|
|
@ -9,7 +9,7 @@ var _ = require('lodash'),
|
|||
|
||||
|
||||
function getInstalledApps() {
|
||||
return api.settings.read.call({ internal: true }, 'installedApps').then(function (response) {
|
||||
return api.settings.read({context: {internal: true}, key: 'installedApps'}).then(function (response) {
|
||||
var installed = response.settings[0];
|
||||
|
||||
installed.value = installed.value || '[]';
|
||||
|
@ -28,7 +28,7 @@ function saveInstalledApps(installedApps) {
|
|||
return getInstalledApps().then(function (currentInstalledApps) {
|
||||
var updatedAppsInstalled = _.uniq(installedApps.concat(currentInstalledApps));
|
||||
|
||||
return api.settings.edit.call({internal: true}, 'installedApps', updatedAppsInstalled);
|
||||
return api.settings.edit({context: {internal: true}, key: 'installedApps'}, updatedAppsInstalled);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ module.exports = {
|
|||
|
||||
try {
|
||||
// We have to parse the value because it's a string
|
||||
api.settings.read.call({ internal: true }, 'activeApps').then(function (response) {
|
||||
api.settings.read({context: {internal: true}, key: 'activeApps'}).then(function (response) {
|
||||
var aApps = response.settings[0];
|
||||
|
||||
appsToLoad = JSON.parse(aApps.value) || [];
|
||||
|
|
|
@ -39,7 +39,13 @@ var generateProxyFunctions = function (name, permissions) {
|
|||
|
||||
return _.reduce(apiMethods, function (memo, apiMethod, methodName) {
|
||||
memo[methodName] = function () {
|
||||
return apiMethod.apply(_.clone(appContext), _.toArray(arguments));
|
||||
var args = _.toArray(arguments),
|
||||
options = args[args.length - 1];
|
||||
|
||||
if (_.isObject(options)) {
|
||||
options.context = _.clone(appContext);
|
||||
}
|
||||
return apiMethod.apply({}, args);
|
||||
};
|
||||
|
||||
return memo;
|
||||
|
@ -57,10 +63,18 @@ var generateProxyFunctions = function (name, permissions) {
|
|||
registerAsync: checkRegisterPermissions('helpers', helpers.registerAsyncThemeHelper.bind(helpers))
|
||||
},
|
||||
api: {
|
||||
posts: passThruAppContextToApi('posts', _.pick(api.posts, 'browse', 'read', 'edit', 'add', 'destroy')),
|
||||
tags: passThruAppContextToApi('tags', _.pick(api.tags, 'browse')),
|
||||
notifications: passThruAppContextToApi('notifications', _.pick(api.notifications, 'browse', 'add', 'destroy')),
|
||||
settings: passThruAppContextToApi('settings', _.pick(api.settings, 'browse', 'read', 'edit'))
|
||||
posts: passThruAppContextToApi('posts',
|
||||
_.pick(api.posts, 'browse', 'read', 'edit', 'add', 'destroy')
|
||||
),
|
||||
tags: passThruAppContextToApi('tags',
|
||||
_.pick(api.tags, 'browse')
|
||||
),
|
||||
notifications: passThruAppContextToApi('notifications',
|
||||
_.pick(api.notifications, 'browse', 'add', 'destroy')
|
||||
),
|
||||
settings: passThruAppContextToApi('settings',
|
||||
_.pick(api.settings, 'browse', 'read', 'edit')
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -19,10 +19,10 @@ function theme() {
|
|||
function update(settings, configUrl) {
|
||||
// TODO: Pass the context into this method instead of hard coding internal: true?
|
||||
return when.all([
|
||||
settings.read.call({ internal: true }, 'title'),
|
||||
settings.read.call({ internal: true }, 'description'),
|
||||
settings.read.call({ internal: true }, 'logo'),
|
||||
settings.read.call({ internal: true }, 'cover')
|
||||
settings.read('title'),
|
||||
settings.read('description'),
|
||||
settings.read('logo'),
|
||||
settings.read('cover')
|
||||
]).then(function (globals) {
|
||||
// normalise the URL by removing any trailing slash
|
||||
themeConfig.url = configUrl.replace(/\/$/, '');
|
||||
|
|
|
@ -148,9 +148,9 @@ function urlFor(context, data, absolute) {
|
|||
// - post - a json object representing a post
|
||||
// - absolute (optional, default:false) - boolean whether or not the url should be absolute
|
||||
function urlForPost(settings, post, absolute) {
|
||||
return settings.read.call({ internal: true }, 'permalinks').then(function (response) {
|
||||
return settings.read('permalinks').then(function (response) {
|
||||
var permalinks = response.settings[0];
|
||||
|
||||
|
||||
return urlFor('post', {post: post, permalinks: permalinks}, absolute);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ adminControllers = {
|
|||
},
|
||||
// frontend route for downloading a file
|
||||
exportContent: function (req, res) {
|
||||
api.db.exportContent.call({user: req.session.user}).then(function (exportData) {
|
||||
api.db.exportContent({context: {user: req.session.user}}).then(function (exportData) {
|
||||
// send a file to the client
|
||||
res.set('Content-Disposition', 'attachment; filename="GhostData.json"');
|
||||
res.json(exportData);
|
||||
|
@ -260,10 +260,9 @@ adminControllers = {
|
|||
password: password
|
||||
}];
|
||||
|
||||
api.users.register({users: users}).then(function (apiResp) {
|
||||
var user = apiResp.users[0];
|
||||
|
||||
api.settings.edit.call({user: 1}, 'email', email).then(function () {
|
||||
api.users.register({users: users}).then(function (response) {
|
||||
var user = response.users[0];
|
||||
api.settings.edit({settings: [{key: 'email', value: email}]}, {context: {user: 1}}).then(function () {
|
||||
var message = {
|
||||
to: email,
|
||||
subject: 'Your New Ghost Blog',
|
||||
|
|
|
@ -22,7 +22,7 @@ var moment = require('moment'),
|
|||
staticPostPermalink = new Route(null, '/:slug/:edit?');
|
||||
|
||||
function getPostPage(options) {
|
||||
return api.settings.read.call({ internal: true }, 'postsPerPage').then(function (response) {
|
||||
return api.settings.read('postsPerPage').then(function (response) {
|
||||
var postPP = response.settings[0],
|
||||
postsPerPage = parseInt(postPP.value, 10);
|
||||
|
||||
|
@ -123,7 +123,7 @@ frontendControllers = {
|
|||
|
||||
// Render the page of posts
|
||||
filters.doFilter('prePostsRender', page.posts).then(function (posts) {
|
||||
api.settings.read.call({ internal: true }, 'activeTheme').then(function (response) {
|
||||
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('tag.hbs') ? 'tag' : 'index',
|
||||
|
@ -148,7 +148,7 @@ frontendControllers = {
|
|||
editFormat,
|
||||
usingStaticPermalink = false;
|
||||
|
||||
api.settings.read.call({ internal: true }, 'permalinks').then(function (response) {
|
||||
api.settings.read('permalinks').then(function (response) {
|
||||
var permalink = response.settings[0],
|
||||
postLookup;
|
||||
|
||||
|
@ -203,7 +203,7 @@ frontendControllers = {
|
|||
setReqCtx(req, post);
|
||||
|
||||
filters.doFilter('prePostsRender', post).then(function (post) {
|
||||
api.settings.read.call({ internal: true }, 'activeTheme').then(function (response) {
|
||||
api.settings.read({key: 'activeTheme', context: {internal: true}}).then(function (response) {
|
||||
var activeTheme = response.settings[0],
|
||||
paths = config().paths.availableThemes[activeTheme.value],
|
||||
view = template.getThemeViewForPost(paths, post);
|
||||
|
@ -282,9 +282,9 @@ frontendControllers = {
|
|||
}
|
||||
|
||||
return when.settle([
|
||||
api.settings.read.call({ internal: true }, 'title'),
|
||||
api.settings.read.call({ internal: true }, 'description'),
|
||||
api.settings.read.call({ internal: true }, 'permalinks')
|
||||
api.settings.read('title'),
|
||||
api.settings.read('description'),
|
||||
api.settings.read('permalinks')
|
||||
]).then(function (result) {
|
||||
|
||||
var options = {};
|
||||
|
|
|
@ -112,7 +112,7 @@ function importUsers(ops, tableData, transaction) {
|
|||
// don't override the users credentials
|
||||
tableData = stripProperties(['id', 'email', 'password'], tableData);
|
||||
tableData[0].id = 1;
|
||||
ops.push(models.User.edit(tableData[0], {user: 1, transacting: transaction})
|
||||
ops.push(models.User.edit(tableData[0], {id: 1, user: 1, transacting: transaction})
|
||||
// add pass-through error handling so that bluebird doesn't think we've dropped it
|
||||
.otherwise(function (error) { return when.reject(error); }));
|
||||
}
|
||||
|
@ -157,9 +157,9 @@ function importApps(ops, tableData, transaction) {
|
|||
// var appsData = tableData.apps,
|
||||
// appSettingsData = tableData.app_settings,
|
||||
// appName;
|
||||
//
|
||||
//
|
||||
// appSettingsData = stripProperties(['id'], appSettingsData);
|
||||
//
|
||||
//
|
||||
// _.each(appSettingsData, function (appSetting) {
|
||||
// // Find app to attach settings to
|
||||
// appName = _.find(appsData, function (app) {
|
||||
|
|
|
@ -389,7 +389,7 @@ coreHelpers.body_class = function (options) {
|
|||
classes.push('page');
|
||||
}
|
||||
|
||||
return api.settings.read.call({ internal: true }, 'activeTheme').then(function (response) {
|
||||
return api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
|
||||
var activeTheme = response.settings[0],
|
||||
paths = config().paths.availableThemes[activeTheme.value],
|
||||
view;
|
||||
|
@ -532,8 +532,8 @@ coreHelpers.meta_description = function (options) {
|
|||
coreHelpers.e = function (key, defaultString, options) {
|
||||
var output;
|
||||
when.all([
|
||||
api.settings.read.call({ internal: true }, 'defaultLang'),
|
||||
api.settings.read.call({ internal: true }, 'forceI18n')
|
||||
api.settings.read('defaultLang'),
|
||||
api.settings.read('forceI18n')
|
||||
]).then(function (values) {
|
||||
if (values[0].settings.value === 'en' &&
|
||||
_.isEmpty(options.hash) &&
|
||||
|
|
|
@ -52,7 +52,7 @@ function doFirstRun() {
|
|||
}
|
||||
|
||||
function initDbHashAndFirstRun() {
|
||||
return api.settings.read.call({ internal: true }, 'dbHash').then(function (response) {
|
||||
return api.settings.read({key: 'dbHash', context: {internal: true}}).then(function (response) {
|
||||
var hash = response.settings[0].value,
|
||||
initHash;
|
||||
|
||||
|
@ -60,10 +60,11 @@ function initDbHashAndFirstRun() {
|
|||
|
||||
if (dbHash === null) {
|
||||
initHash = uuid.v4();
|
||||
return api.settings.edit.call({ internal: true }, 'dbHash', initHash).then(function (response) {
|
||||
dbHash = response.settings[0].value;
|
||||
return dbHash;
|
||||
}).then(doFirstRun);
|
||||
return api.settings.edit({settings: [{key: 'dbHash', value: initHash}]}, {context: {internal: true}})
|
||||
.then(function (response) {
|
||||
dbHash = response.settings[0].value;
|
||||
return dbHash;
|
||||
}).then(doFirstRun);
|
||||
}
|
||||
|
||||
return dbHash;
|
||||
|
|
|
@ -106,7 +106,7 @@ GhostMailer.prototype.send = function (payload) {
|
|||
return when.reject(new Error('Email Error: Incomplete message data.'));
|
||||
}
|
||||
|
||||
return api.settings.read.call({ internal: true }, 'email').then(function (response) {
|
||||
return api.settings.read('email').then(function (response) {
|
||||
|
||||
var email = response.settings[0],
|
||||
to = message.to || email.value;
|
||||
|
|
|
@ -39,7 +39,7 @@ function ghostLocals(req, res, next) {
|
|||
if (res.isAdmin) {
|
||||
res.locals.csrfToken = req.csrfToken();
|
||||
when.all([
|
||||
api.users.read.call({user: req.session.user}, {id: req.session.user}),
|
||||
api.users.read({id: req.session.user}, {context: {user: req.session.user}}),
|
||||
api.notifications.browse()
|
||||
]).then(function (values) {
|
||||
var currentUser = values[0].users[0],
|
||||
|
@ -150,9 +150,9 @@ function manageAdminAndTheme(req, res, next) {
|
|||
expressServer.enable(expressServer.get('activeTheme'));
|
||||
expressServer.disable('admin');
|
||||
}
|
||||
api.settings.read.call({ internal: true }, 'activeTheme').then(function (response) {
|
||||
api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
|
||||
var activeTheme = response.settings[0];
|
||||
|
||||
|
||||
// Check if the theme changed
|
||||
if (activeTheme.value !== expressServer.get('activeTheme')) {
|
||||
// Change theme
|
||||
|
|
|
@ -170,7 +170,7 @@ var middleware = {
|
|||
|
||||
// to allow unit testing
|
||||
forwardToExpressStatic: function (req, res, next) {
|
||||
api.settings.read.call({ internal: true }, 'activeTheme').then(function (response) {
|
||||
api.settings.read({context: {internal: true}, key: 'activeTheme'}).then(function (response) {
|
||||
var activeTheme = response.settings[0];
|
||||
// For some reason send divides the max age number by 1000
|
||||
express['static'](path.join(config().paths.themePath, activeTheme.value), {maxAge: ONE_HOUR_MS})(req, res, next);
|
||||
|
|
|
@ -63,18 +63,20 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
},
|
||||
|
||||
creating: function (newObj, attr, options) {
|
||||
var user = options.context && options.context.user ? options.context.user : 1;
|
||||
if (!this.get('created_by')) {
|
||||
this.set('created_by', options.user);
|
||||
this.set('created_by', user);
|
||||
}
|
||||
},
|
||||
|
||||
saving: function (newObj, attr, options) {
|
||||
var user = options.context && options.context.user ? options.context.user : 1;
|
||||
// Remove any properties which don't belong on the model
|
||||
this.attributes = this.pick(this.permittedAttributes());
|
||||
// Store the previous attributes so we can tell what was updated later
|
||||
this._updatedAttributes = newObj.previousAttributes();
|
||||
|
||||
this.set('updated_by', options.user);
|
||||
this.set('updated_by', user);
|
||||
},
|
||||
|
||||
// Base prototype properties will go here
|
||||
|
@ -153,8 +155,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
}
|
||||
|
||||
}, {
|
||||
|
||||
// ## Model Data Functions
|
||||
// ## Data Utility Functions
|
||||
|
||||
/**
|
||||
* Returns an array of keys permitted in every method's `options` hash.
|
||||
|
@ -191,6 +192,8 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
return filteredOptions;
|
||||
},
|
||||
|
||||
// ## Model Data Functions
|
||||
|
||||
/**
|
||||
* ### Find All
|
||||
* Naive find all fetches all the data for a particular model
|
||||
|
@ -219,6 +222,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
findOne: function (data, options) {
|
||||
data = this.filterData(data);
|
||||
options = this.filterOptions(options, 'findOne');
|
||||
// We pass include to forge so that toJSON has access
|
||||
return this.forge(data, {include: options.include}).fetch(options);
|
||||
},
|
||||
|
||||
|
@ -230,9 +234,11 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
* @return {Promise(ghostBookshelf.Model)} Edited Model
|
||||
*/
|
||||
edit: function (data, options) {
|
||||
var id = options.id;
|
||||
data = this.filterData(data);
|
||||
options = this.filterOptions(options, 'edit');
|
||||
return this.forge({id: data.id}).fetch(options).then(function (object) {
|
||||
|
||||
return this.forge({id: id}).fetch(options).then(function (object) {
|
||||
if (object) {
|
||||
return object.save(data, options);
|
||||
}
|
||||
|
@ -250,11 +256,8 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
data = this.filterData(data);
|
||||
options = this.filterOptions(options, 'add');
|
||||
var instance = this.forge(data);
|
||||
// We allow you to disable timestamps
|
||||
// when importing posts so that
|
||||
// the new posts `updated_at` value
|
||||
// is the same as the import json blob.
|
||||
// More details refer to https://github.com/TryGhost/Ghost/issues/1696
|
||||
// We allow you to disable timestamps when importing posts so that the new posts `updated_at` value is the same
|
||||
// as the import json blob. More details refer to https://github.com/TryGhost/Ghost/issues/1696
|
||||
if (options.importing) {
|
||||
instance.hasTimestamps = false;
|
||||
}
|
||||
|
@ -264,13 +267,13 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
/**
|
||||
* ### Destroy
|
||||
* Naive destroy
|
||||
* @param {Object} data
|
||||
* @param {Object} options (optional)
|
||||
* @return {Promise(ghostBookshelf.Model)} Empty Model
|
||||
*/
|
||||
destroy: function (data, options) {
|
||||
destroy: function (options) {
|
||||
var id = options.id;
|
||||
options = this.filterOptions(options, 'destroy');
|
||||
return this.forge({id: data}).destroy(options);
|
||||
return this.forge({id: id}).destroy(options);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,12 +25,12 @@ module.exports = {
|
|||
|
||||
return self.Post.findAll().then(function (posts) {
|
||||
return when.all(_.map(posts.toJSON(), function (post) {
|
||||
return self.Post.destroy(post.id);
|
||||
return self.Post.destroy({id: post.id});
|
||||
}));
|
||||
}).then(function () {
|
||||
return self.Tag.findAll().then(function (tags) {
|
||||
return when.all(_.map(tags.toJSON(), function (tag) {
|
||||
return self.Tag.destroy(tag.id);
|
||||
return self.Tag.destroy({id: tag.id});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -44,7 +44,8 @@ Post = ghostBookshelf.Model.extend({
|
|||
/*jshint unused:false*/
|
||||
var self = this,
|
||||
tagsToCheck,
|
||||
i;
|
||||
i,
|
||||
user = options.context && options.context.user ? options.context.user : 1;
|
||||
|
||||
options = options || {};
|
||||
// keep tags for 'saved' event and deduplicate upper/lowercase tags
|
||||
|
@ -75,7 +76,7 @@ Post = ghostBookshelf.Model.extend({
|
|||
this.set('published_at', new Date());
|
||||
}
|
||||
// This will need to go elsewhere in the API layer.
|
||||
this.set('published_by', options.user);
|
||||
this.set('published_by', user);
|
||||
}
|
||||
|
||||
if (this.hasChanged('slug') || !this.get('slug')) {
|
||||
|
@ -93,9 +94,11 @@ Post = ghostBookshelf.Model.extend({
|
|||
/*jshint unused:false*/
|
||||
options = options || {};
|
||||
|
||||
var user = options.context && options.context.user ? options.context.user : 1;
|
||||
|
||||
// set any dynamic default properties
|
||||
if (!this.get('author_id')) {
|
||||
this.set('author_id', options.user);
|
||||
this.set('author_id', user);
|
||||
}
|
||||
|
||||
ghostBookshelf.Model.prototype.creating.call(this, newPage, attr, options);
|
||||
|
@ -105,7 +108,7 @@ Post = ghostBookshelf.Model.extend({
|
|||
* ### updateTags
|
||||
* Update tags that are attached to a post. Create any tags that don't already exist.
|
||||
* @param {Object} newPost
|
||||
* @param {Object} attr
|
||||
* @param {Object} attr
|
||||
* @param {Object} options
|
||||
* @return {Promise(ghostBookshelf.Models.Post)} Updated Post model
|
||||
*/
|
||||
|
@ -243,8 +246,14 @@ Post = ghostBookshelf.Model.extend({
|
|||
return filteredData;
|
||||
},
|
||||
|
||||
// #### findAll
|
||||
// Extends base model findAll to eager-fetch author and user relationships.
|
||||
// ## Model Data Functions
|
||||
|
||||
/**
|
||||
* ### Find All
|
||||
*
|
||||
* @param options
|
||||
* @returns {*}
|
||||
*/
|
||||
findAll: function (options) {
|
||||
options = options || {};
|
||||
options.withRelated = _.union([ 'tags', 'fields' ], options.include);
|
||||
|
@ -252,24 +261,24 @@ Post = ghostBookshelf.Model.extend({
|
|||
},
|
||||
|
||||
|
||||
// #### findPage
|
||||
// Find results by page - returns an object containing the
|
||||
// information about the request (page, limit), along with the
|
||||
// info needed for pagination (pages, total).
|
||||
|
||||
// **response:**
|
||||
|
||||
// {
|
||||
// posts: [
|
||||
// {...}, {...}, {...}
|
||||
// ],
|
||||
// page: __,
|
||||
// limit: __,
|
||||
// pages: __,
|
||||
// total: __
|
||||
// }
|
||||
|
||||
/*
|
||||
/**
|
||||
* #### findPage
|
||||
* Find results by page - returns an object containing the
|
||||
* information about the request (page, limit), along with the
|
||||
* info needed for pagination (pages, total).
|
||||
*
|
||||
* **response:**
|
||||
*
|
||||
* {
|
||||
* posts: [
|
||||
* {...}, {...}, {...}
|
||||
* ],
|
||||
* page: __,
|
||||
* limit: __,
|
||||
* pages: __,
|
||||
* total: __
|
||||
* }
|
||||
*
|
||||
* @params {Object} options
|
||||
*/
|
||||
findPage: function (options) {
|
||||
|
@ -377,22 +386,23 @@ Post = ghostBookshelf.Model.extend({
|
|||
meta = {},
|
||||
data = {};
|
||||
|
||||
pagination['page'] = parseInt(options.page, 10);
|
||||
pagination['limit'] = options.limit;
|
||||
pagination['pages'] = calcPages === 0 ? 1 : calcPages;
|
||||
pagination['total'] = totalPosts;
|
||||
pagination['next'] = null;
|
||||
pagination['prev'] = null;
|
||||
pagination.page = parseInt(options.page, 10);
|
||||
pagination.limit = options.limit;
|
||||
pagination.pages = calcPages === 0 ? 1 : calcPages;
|
||||
pagination.total = totalPosts;
|
||||
pagination.next = null;
|
||||
pagination.prev = null;
|
||||
|
||||
// Pass include to each model so that toJSON works correctly
|
||||
if (options.include) {
|
||||
_.each(postCollection.models, function (item) {
|
||||
item.include = options.include;
|
||||
});
|
||||
}
|
||||
|
||||
data['posts'] = postCollection.toJSON();
|
||||
data['meta'] = meta;
|
||||
meta['pagination'] = pagination;
|
||||
data.posts = postCollection.toJSON();
|
||||
data.meta = meta;
|
||||
meta.pagination = pagination;
|
||||
|
||||
if (pagination.pages > 1) {
|
||||
if (pagination.page === 1) {
|
||||
|
@ -406,9 +416,9 @@ Post = ghostBookshelf.Model.extend({
|
|||
}
|
||||
|
||||
if (tagInstance) {
|
||||
meta['filters'] = {};
|
||||
meta.filters = {};
|
||||
if (!tagInstance.isNew()) {
|
||||
meta.filters['tags'] = [tagInstance.toJSON()];
|
||||
meta.filters.tags = [tagInstance.toJSON()];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,59 +427,76 @@ Post = ghostBookshelf.Model.extend({
|
|||
.catch(errors.logAndThrowError);
|
||||
},
|
||||
|
||||
// #### findOne
|
||||
// Extends base model read to eager-fetch author and user relationships.
|
||||
findOne: function (args, options) {
|
||||
/**
|
||||
* ### Find One
|
||||
* @extends ghostBookshelf.Model.findOne to handle post status
|
||||
* **See:** [ghostBookshelf.Model.findOne](base.js.html#Find%20One)
|
||||
*/
|
||||
findOne: function (data, options) {
|
||||
options = options || {};
|
||||
|
||||
args = _.extend({
|
||||
data = _.extend({
|
||||
status: 'published'
|
||||
}, args || {});
|
||||
}, data || {});
|
||||
|
||||
if (args.status === 'all') {
|
||||
delete args.status;
|
||||
if (data.status === 'all') {
|
||||
delete data.status;
|
||||
}
|
||||
|
||||
// Add related objects
|
||||
options.withRelated = _.union([ 'tags', 'fields' ], options.include);
|
||||
|
||||
return ghostBookshelf.Model.findOne.call(this, args, options);
|
||||
return ghostBookshelf.Model.findOne.call(this, data, options);
|
||||
},
|
||||
|
||||
add: function (newPostData, options) {
|
||||
/**
|
||||
* ### Edit
|
||||
* @extends ghostBookshelf.Model.edit to handle returning the full object and manage _updatedAttributes
|
||||
* **See:** [ghostBookshelf.Model.edit](base.js.html#edit)
|
||||
*/
|
||||
edit: function (data, options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
|
||||
return ghostBookshelf.Model.add.call(this, newPostData, options).then(function (post) {
|
||||
return self.findOne({status: 'all', id: post.id}, options);
|
||||
});
|
||||
},
|
||||
edit: function (editedPost, options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
return ghostBookshelf.Model.edit.call(this, editedPost, options).then(function (post) {
|
||||
if (post) {
|
||||
return self.findOne({status: 'all', id: post.id}, options)
|
||||
.then(function (found) {
|
||||
return ghostBookshelf.Model.edit.call(this, data, options).then(function (post) {
|
||||
return self.findOne({status: 'all', id: options.id}, options)
|
||||
.then(function (found) {
|
||||
if (found) {
|
||||
// Pass along the updated attributes for checking status changes
|
||||
found._updatedAttributes = post._updatedAttributes;
|
||||
return found;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
destroy: function (_identifier, options) {
|
||||
|
||||
/**
|
||||
* ### Add
|
||||
* @extends ghostBookshelf.Model.add to handle returning the full object
|
||||
* **See:** [ghostBookshelf.Model.add](base.js.html#add)
|
||||
*/
|
||||
add: function (data, options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
|
||||
return ghostBookshelf.Model.add.call(this, data, options).then(function (post) {
|
||||
return self.findOne({status: 'all', id: post.id}, options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* ### Destroy
|
||||
* @extends ghostBookshelf.Model.destroy to clean up tag relations
|
||||
* **See:** [ghostBookshelf.Model.destroy](base.js.html#destroy)
|
||||
*/
|
||||
destroy: function (options) {
|
||||
var id = options.id;
|
||||
options = this.filterOptions(options, 'destroy');
|
||||
|
||||
return this.forge({id: _identifier}).fetch({withRelated: ['tags']}).then(function destroyTags(post) {
|
||||
var tagIds = _.pluck(post.related('tags').toJSON(), 'id');
|
||||
if (tagIds) {
|
||||
return post.tags().detach(tagIds).then(function destroyPost() {
|
||||
return post.destroy(options);
|
||||
});
|
||||
}
|
||||
|
||||
return post.destroy(options);
|
||||
return this.forge({id: id}).fetch({withRelated: ['tags']}).then(function destroyTagsAndPost(post) {
|
||||
return post.related('tags').detach().then(function () {
|
||||
return post.destroy(options);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ Role = ghostBookshelf.Model.extend({
|
|||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
Roles = ghostBookshelf.Collection.extend({
|
||||
|
|
|
@ -19,7 +19,7 @@ Session = ghostBookshelf.Model.extend({
|
|||
/*jshint unused:false*/
|
||||
// Remove any properties which don't belong on the model
|
||||
this.attributes = this.pick(this.permittedAttributes());
|
||||
},
|
||||
}
|
||||
|
||||
}, {
|
||||
destroyAll: function (options) {
|
||||
|
|
|
@ -80,23 +80,23 @@ Settings = ghostBookshelf.Model.extend({
|
|||
return options;
|
||||
},
|
||||
|
||||
findOne: function (_key) {
|
||||
findOne: function (options) {
|
||||
// Allow for just passing the key instead of attributes
|
||||
if (!_.isObject(_key)) {
|
||||
_key = { key: _key };
|
||||
if (!_.isObject(options)) {
|
||||
options = { key: options };
|
||||
}
|
||||
return when(ghostBookshelf.Model.findOne.call(this, _key));
|
||||
return when(ghostBookshelf.Model.findOne.call(this, options));
|
||||
},
|
||||
|
||||
edit: function (_data, options) {
|
||||
edit: function (data, options) {
|
||||
var self = this;
|
||||
options = this.filterOptions(options, 'edit');
|
||||
|
||||
if (!Array.isArray(_data)) {
|
||||
_data = [_data];
|
||||
if (!Array.isArray(data)) {
|
||||
data = [data];
|
||||
}
|
||||
|
||||
return when.map(_data, function (item) {
|
||||
return when.map(data, function (item) {
|
||||
// Accept an array of models as input
|
||||
if (item.toJSON) { item = item.toJSON(); }
|
||||
if (!(_.isString(item.key) && item.key.length > 0)) {
|
||||
|
|
|
@ -57,7 +57,7 @@ Tag = ghostBookshelf.Model.extend({
|
|||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
Tags = ghostBookshelf.Collection.extend({
|
||||
|
|
|
@ -106,16 +106,20 @@ User = ghostBookshelf.Model.extend({
|
|||
},
|
||||
|
||||
/**
|
||||
* ## Add
|
||||
* Naive user add
|
||||
* @param {object} _user
|
||||
*
|
||||
* Hashes the password provided before saving to the database.
|
||||
*
|
||||
* @param {object} data
|
||||
* @param {object} options
|
||||
* @extends ghostBookshelf.Model.add to manage all aspects of user signup
|
||||
* **See:** [ghostBookshelf.Model.add](base.js.html#Add)
|
||||
*/
|
||||
add: function (_user, options) {
|
||||
add: function (data, options) {
|
||||
|
||||
var self = this,
|
||||
// Clone the _user so we don't expose the hashed password unnecessarily
|
||||
userData = this.filterData(_user);
|
||||
userData = this.filterData(data);
|
||||
|
||||
options = this.filterOptions(options, 'add');
|
||||
|
||||
|
@ -133,7 +137,7 @@ User = ghostBookshelf.Model.extend({
|
|||
}
|
||||
}).then(function () {
|
||||
// Generate a new password hash
|
||||
return generatePasswordHash(_user.password);
|
||||
return generatePasswordHash(data.password);
|
||||
}).then(function (hash) {
|
||||
// Assign the hashed password
|
||||
userData.password = hash;
|
||||
|
@ -143,6 +147,7 @@ User = ghostBookshelf.Model.extend({
|
|||
// Save the user with the hashed password
|
||||
return ghostBookshelf.Model.add.call(self, userData, options);
|
||||
}).then(function (addedUser) {
|
||||
|
||||
// Assign the userData to our created user so we can pass it back
|
||||
userData = addedUser;
|
||||
// Add this user to the admin role (assumes admin = role_id: 1)
|
||||
|
|
|
@ -35,16 +35,11 @@ function parseContext(context) {
|
|||
parsed.internal = true;
|
||||
}
|
||||
|
||||
// @TODO: Refactor canThis() references to pass { user: id } explicitly instead of primitives.
|
||||
if (context && context.id) {
|
||||
// Handle passing of just user.id string
|
||||
parsed.user = context.id;
|
||||
} else if (_.isNumber(context)) {
|
||||
// Handle passing of just user id number
|
||||
parsed.user = context;
|
||||
} else if (_.isObject(context)) {
|
||||
// Otherwise, use the new hotness { user: id, app: id } format
|
||||
if (context && context.user) {
|
||||
parsed.user = context.user;
|
||||
}
|
||||
|
||||
if (context && context.app) {
|
||||
parsed.app = context.app;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,11 @@ var admin = require('../controllers/admin'),
|
|||
middleware = require('../middleware').middleware,
|
||||
|
||||
ONE_HOUR_S = 60 * 60,
|
||||
ONE_YEAR_S = 365 * 24 * ONE_HOUR_S;
|
||||
ONE_YEAR_S = 365 * 24 * ONE_HOUR_S,
|
||||
|
||||
module.exports = function (server) {
|
||||
adminRoutes;
|
||||
|
||||
adminRoutes = function (server) {
|
||||
// Have ember route look for hits first
|
||||
// to prevent conflicts with pre-existing routes
|
||||
server.get('/ghost/ember/*', admin.index);
|
||||
|
@ -65,4 +67,6 @@ module.exports = function (server) {
|
|||
res.redirect(subdir + '/ghost/');
|
||||
});
|
||||
server.get('/ghost/', admin.indexold);
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = adminRoutes;
|
|
@ -1,37 +1,43 @@
|
|||
// # API routes
|
||||
var middleware = require('../middleware').middleware,
|
||||
api = require('../api');
|
||||
api = require('../api'),
|
||||
apiRoutes;
|
||||
|
||||
module.exports = function (server) {
|
||||
// ### API routes
|
||||
// #### Posts
|
||||
server.get('/ghost/api/v0.1/posts', api.requestHandler(api.posts.browse));
|
||||
server.post('/ghost/api/v0.1/posts', api.requestHandler(api.posts.add));
|
||||
server.get('/ghost/api/v0.1/posts/:id', api.requestHandler(api.posts.read));
|
||||
server.put('/ghost/api/v0.1/posts/:id', api.requestHandler(api.posts.edit));
|
||||
server.del('/ghost/api/v0.1/posts/:id', api.requestHandler(api.posts.destroy));
|
||||
server.get('/ghost/api/v0.1/posts/slug/:title', middleware.authAPI, api.requestHandler(api.posts.generateSlug));
|
||||
// #### Settings
|
||||
server.get('/ghost/api/v0.1/settings/', api.requestHandler(api.settings.browse));
|
||||
server.get('/ghost/api/v0.1/settings/:key/', api.requestHandler(api.settings.read));
|
||||
server.put('/ghost/api/v0.1/settings/', api.requestHandler(api.settings.edit));
|
||||
// #### Users
|
||||
server.get('/ghost/api/v0.1/users/', api.requestHandler(api.users.browse));
|
||||
server.get('/ghost/api/v0.1/users/:id/', api.requestHandler(api.users.read));
|
||||
server.put('/ghost/api/v0.1/users/:id/', api.requestHandler(api.users.edit));
|
||||
// #### Tags
|
||||
server.get('/ghost/api/v0.1/tags/', api.requestHandler(api.tags.browse));
|
||||
// #### Themes
|
||||
server.get('/ghost/api/v0.1/themes/', api.requestHandler(api.themes.browse));
|
||||
server.put('/ghost/api/v0.1/themes/:name', api.requestHandler(api.themes.edit));
|
||||
// #### Notifications
|
||||
server.del('/ghost/api/v0.1/notifications/:id', api.requestHandler(api.notifications.destroy));
|
||||
server.post('/ghost/api/v0.1/notifications/', api.requestHandler(api.notifications.add));
|
||||
server.get('/ghost/api/v0.1/notifications/', api.requestHandler(api.notifications.browse));
|
||||
// #### Import/Export
|
||||
server.get('/ghost/api/v0.1/db/', api.requestHandler(api.db.exportContent));
|
||||
server.post('/ghost/api/v0.1/db/', middleware.busboy, api.requestHandler(api.db.importContent));
|
||||
server.del('/ghost/api/v0.1/db/', api.requestHandler(api.db.deleteAllContent));
|
||||
// #### Mail
|
||||
server.post('/ghost/api/v0.1/mail', api.requestHandler(api.mail.send));
|
||||
server.post('/ghost/api/v0.1/mail/test', api.requestHandler(api.mail.sendTest));
|
||||
};
|
||||
apiRoutes = function (server) {
|
||||
// ## Posts
|
||||
server.get('/ghost/api/v0.1/posts', api.http(api.posts.browse));
|
||||
server.post('/ghost/api/v0.1/posts', api.http(api.posts.add));
|
||||
server.get('/ghost/api/v0.1/posts/:id(\\d+)', api.http(api.posts.read));
|
||||
server.get('/ghost/api/v0.1/posts/:slug([a-z-]+)', api.http(api.posts.read));
|
||||
server.put('/ghost/api/v0.1/posts/:id', api.http(api.posts.edit));
|
||||
server.del('/ghost/api/v0.1/posts/:id', api.http(api.posts.destroy));
|
||||
server.get('/ghost/api/v0.1/posts/slug/:title', api.http(api.posts.generateSlug));
|
||||
// ## Settings
|
||||
server.get('/ghost/api/v0.1/settings/', api.http(api.settings.browse));
|
||||
server.get('/ghost/api/v0.1/settings/:key/', api.http(api.settings.read));
|
||||
server.put('/ghost/api/v0.1/settings/', api.http(api.settings.edit));
|
||||
// ## Users
|
||||
server.get('/ghost/api/v0.1/users/', api.http(api.users.browse));
|
||||
server.get('/ghost/api/v0.1/users/:id/', api.http(api.users.read));
|
||||
server.put('/ghost/api/v0.1/users/:id/', api.http(api.users.edit));
|
||||
// ## Tags
|
||||
server.get('/ghost/api/v0.1/tags/', api.http(api.tags.browse));
|
||||
// ## Themes
|
||||
server.get('/ghost/api/v0.1/themes/', api.http(api.themes.browse));
|
||||
server.put('/ghost/api/v0.1/themes/:name', api.http(api.themes.edit));
|
||||
// ## Notifications
|
||||
server.del('/ghost/api/v0.1/notifications/:id', api.http(api.notifications.destroy));
|
||||
server.post('/ghost/api/v0.1/notifications/', api.http(api.notifications.add));
|
||||
server.get('/ghost/api/v0.1/notifications/', api.http(api.notifications.browse));
|
||||
server.post('/ghost/api/v0.1/notifications/', api.http(api.notifications.add));
|
||||
server.del('/ghost/api/v0.1/notifications/:id', api.http(api.notifications.destroy));
|
||||
// ## DB
|
||||
server.get('/ghost/api/v0.1/db/', api.http(api.db.exportContent));
|
||||
server.post('/ghost/api/v0.1/db/', middleware.busboy, api.http(api.db.importContent));
|
||||
server.del('/ghost/api/v0.1/db/', api.http(api.db.deleteAllContent));
|
||||
// ## Mail
|
||||
server.post('/ghost/api/v0.1/mail', api.http(api.mail.send));
|
||||
server.post('/ghost/api/v0.1/mail/test', api.http(api.mail.sendTest));
|
||||
};
|
||||
|
||||
module.exports = apiRoutes;
|
|
@ -2,9 +2,11 @@ var frontend = require('../controllers/frontend'),
|
|||
config = require('../config'),
|
||||
|
||||
ONE_HOUR_S = 60 * 60,
|
||||
ONE_YEAR_S = 365 * 24 * ONE_HOUR_S;
|
||||
ONE_YEAR_S = 365 * 24 * ONE_HOUR_S,
|
||||
|
||||
module.exports = function (server) {
|
||||
frontendRoutes;
|
||||
|
||||
frontendRoutes = function (server) {
|
||||
var subdir = config().paths.subdir;
|
||||
|
||||
// ### Frontend routes
|
||||
|
@ -24,6 +26,6 @@ module.exports = function (server) {
|
|||
server.get('/page/:page/', frontend.homepage);
|
||||
server.get('/', frontend.homepage);
|
||||
server.get('*', frontend.single);
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
module.exports = frontendRoutes;
|
|
@ -50,9 +50,9 @@ function updateCheckData() {
|
|||
ops = [],
|
||||
mailConfig = config().mail;
|
||||
|
||||
ops.push(api.settings.read.call({ internal: true }, 'dbHash').otherwise(errors.rejectError));
|
||||
ops.push(api.settings.read.call({ internal: true }, 'activeTheme').otherwise(errors.rejectError));
|
||||
ops.push(api.settings.read.call({ internal: true }, 'activeApps')
|
||||
ops.push(api.settings.read({context: {internal: true}, key: 'dbHash'}).otherwise(errors.rejectError));
|
||||
ops.push(api.settings.read({context: {internal: true}, key: 'activeTheme'}).otherwise(errors.rejectError));
|
||||
ops.push(api.settings.read({context: {internal: true}, key: 'activeApps'})
|
||||
.then(function (response) {
|
||||
var apps = response.settings[0];
|
||||
try {
|
||||
|
@ -64,7 +64,7 @@ function updateCheckData() {
|
|||
return _.reduce(apps, function (memo, item) { return memo === '' ? memo + item : memo + ', ' + item; }, '');
|
||||
}).otherwise(errors.rejectError));
|
||||
ops.push(api.posts.browse().otherwise(errors.rejectError));
|
||||
ops.push(api.users.browse.call({user: 1}).otherwise(errors.rejectError));
|
||||
ops.push(api.users.browse({context: {user: 1}}).otherwise(errors.rejectError));
|
||||
ops.push(nodefn.call(exec, 'npm -v').otherwise(errors.rejectError));
|
||||
|
||||
data.ghost_version = currentVersion;
|
||||
|
@ -142,13 +142,21 @@ function updateCheckRequest() {
|
|||
// 1. Updates the time we can next make a check
|
||||
// 2. Checks if the version in the response is new, and updates the notification setting
|
||||
function updateCheckResponse(response) {
|
||||
var ops = [];
|
||||
var ops = [],
|
||||
internalContext = {context: {internal: true}};
|
||||
|
||||
ops.push(api.settings.edit.call({internal: true}, 'nextUpdateCheck', response.next_check)
|
||||
.otherwise(errors.rejectError));
|
||||
|
||||
ops.push(api.settings.edit.call({internal: true}, 'displayUpdateNotification', response.version)
|
||||
.otherwise(errors.rejectError));
|
||||
ops.push(
|
||||
api.settings.edit(
|
||||
{settings: [{key: 'nextUpdateCheck', value: response.next_check}]},
|
||||
internalContext
|
||||
)
|
||||
.otherwise(errors.rejectError),
|
||||
api.settings.edit(
|
||||
{settings: [{key: 'displayUpdateNotification', value: response.version}]},
|
||||
internalContext
|
||||
)
|
||||
.otherwise(errors.rejectError)
|
||||
);
|
||||
|
||||
return when.settle(ops).then(function (descriptors) {
|
||||
descriptors.forEach(function (d) {
|
||||
|
@ -171,7 +179,7 @@ function updateCheck() {
|
|||
// No update check
|
||||
deferred.resolve();
|
||||
} else {
|
||||
api.settings.read.call({ internal: true }, 'nextUpdateCheck').then(function (result) {
|
||||
api.settings.read({context: {internal: true}, key: 'nextUpdateCheck'}).then(function (result) {
|
||||
var nextUpdateCheck = result.settings[0];
|
||||
|
||||
if (nextUpdateCheck && nextUpdateCheck.value && nextUpdateCheck.value > moment().unix()) {
|
||||
|
@ -191,7 +199,7 @@ function updateCheck() {
|
|||
}
|
||||
|
||||
function showUpdateNotification() {
|
||||
return api.settings.read.call({ internal: true }, 'displayUpdateNotification').then(function (response) {
|
||||
return api.settings.read({context: {internal: true}, key: 'displayUpdateNotification'}).then(function (response) {
|
||||
var display = response.settings[0];
|
||||
|
||||
// Version 0.4 used boolean to indicate the need for an update. This special case is
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
// Posts
|
||||
var blanket = require("blanket")({
|
||||
"pattern": ["/core/server/", "/core/clientold/", "/core/shared/"],
|
||||
"data-cover-only": ["/core/server/", "/core/clientold/", "/core/shared/"]
|
||||
}),
|
||||
requireDir = require("require-dir");
|
||||
|
||||
|
||||
requireDir("./unit");
|
||||
requireDir("./integration");
|
||||
requireDir("./functional/routes");
|
||||
|
|
|
@ -61,7 +61,7 @@ describe('Post API', function () {
|
|||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
|
||||
csrfToken = res.text.match(pattern_meta)[1];
|
||||
done();
|
||||
});
|
||||
|
@ -124,7 +124,7 @@ describe('Post API', function () {
|
|||
testUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
// Test bits of the API we don't use in the app yet to ensure the API behaves properly
|
||||
|
@ -193,7 +193,7 @@ describe('Post API', function () {
|
|||
|
||||
// ## Read
|
||||
describe('Read', function () {
|
||||
it('can retrieve a post', function (done) {
|
||||
it('can retrieve a post by id', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/1/'))
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
|
@ -207,6 +207,32 @@ describe('Post API', function () {
|
|||
jsonResponse.should.exist;
|
||||
jsonResponse.posts.should.exist;
|
||||
testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
|
||||
jsonResponse.posts[0].id.should.equal(1);
|
||||
jsonResponse.posts[0].page.should.eql(0);
|
||||
_.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
|
||||
_.isBoolean(jsonResponse.posts[0].page).should.eql(true);
|
||||
jsonResponse.posts[0].author.should.be.a.Number;
|
||||
jsonResponse.posts[0].created_by.should.be.a.Number;
|
||||
jsonResponse.posts[0].tags[0].should.be.a.Number;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can retrieve a post by slug', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/welcome-to-ghost/'))
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
res.should.have.status(200);
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
res.should.be.json;
|
||||
var jsonResponse = res.body;
|
||||
jsonResponse.should.exist;
|
||||
jsonResponse.posts.should.exist;
|
||||
testUtils.API.checkResponse(jsonResponse.posts[0], 'post');
|
||||
jsonResponse.posts[0].slug.should.equal('welcome-to-ghost');
|
||||
jsonResponse.posts[0].page.should.eql(0);
|
||||
_.isBoolean(jsonResponse.posts[0].featured).should.eql(true);
|
||||
_.isBoolean(jsonResponse.posts[0].page).should.eql(true);
|
||||
|
@ -477,7 +503,6 @@ describe('Post API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
it('can change a static page to a post', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/7/'))
|
||||
.end(function (err, res) {
|
||||
|
@ -501,11 +526,11 @@ describe('Post API', function () {
|
|||
}
|
||||
|
||||
var putBody = res.body;
|
||||
|
||||
_.has(res.headers, 'x-cache-invalidate').should.equal(false);
|
||||
res.should.be.json;
|
||||
putBody.should.exist;
|
||||
putBody.posts[0].page.should.eql(changedValue);
|
||||
|
||||
testUtils.API.checkResponse(putBody.posts[0], 'post');
|
||||
done();
|
||||
});
|
||||
|
@ -605,10 +630,6 @@ describe('Post API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// ## delete
|
||||
describe('Delete', function () {
|
||||
it('can\'t edit non existent post', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('posts/1/'))
|
||||
.end(function (err, res) {
|
||||
|
@ -630,7 +651,6 @@ describe('Post API', function () {
|
|||
return done(err);
|
||||
}
|
||||
|
||||
var putBody = res.body;
|
||||
_.has(res.headers, 'x-cache-invalidate').should.equal(false);
|
||||
res.should.be.json;
|
||||
jsonResponse = res.body;
|
||||
|
@ -641,6 +661,10 @@ describe('Post API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// ## delete
|
||||
describe('Delete', function () {
|
||||
it('can delete a post', function (done) {
|
||||
var deletePostId = 1;
|
||||
request.del(testUtils.API.getApiQuery('posts/' + deletePostId + '/'))
|
||||
|
@ -829,9 +853,5 @@ describe('Post API', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
|
|
@ -214,7 +214,7 @@ describe('Settings API', function () {
|
|||
newValue = 'new value';
|
||||
jsonResponse.should.exist;
|
||||
should.exist(jsonResponse.settings);
|
||||
jsonResponse.settings.push({ key: 'testvalue', value: newValue });
|
||||
jsonResponse.settings = [{ key: 'testvalue', value: newValue }];
|
||||
|
||||
request.put(testUtils.API.getApiQuery('settings/'))
|
||||
.set('X-CSRF-Token', csrfToken)
|
||||
|
|
|
@ -42,7 +42,7 @@ describe('User API', function () {
|
|||
pattern_meta.should.exist;
|
||||
csrfToken = res.text.match(pattern_meta)[1];
|
||||
|
||||
process.nextTick(function() {
|
||||
process.nextTick(function () {
|
||||
request.post('/ghost/signin/')
|
||||
.set('X-CSRF-Token', csrfToken)
|
||||
.send({email: user.email, password: user.password})
|
||||
|
@ -144,13 +144,16 @@ describe('User API', function () {
|
|||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
changedValue = 'joe-bloggs.ghost.org';
|
||||
changedValue = 'joe-bloggs.ghost.org',
|
||||
dataToSend;
|
||||
jsonResponse.users[0].should.exist;
|
||||
jsonResponse.users[0].website = changedValue;
|
||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user');
|
||||
|
||||
dataToSend = { users: [{website: changedValue}]};
|
||||
|
||||
request.put(testUtils.API.getApiQuery('users/me/'))
|
||||
.set('X-CSRF-Token', csrfToken)
|
||||
.send(jsonResponse)
|
||||
.send(dataToSend)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
|
@ -162,7 +165,7 @@ describe('User API', function () {
|
|||
res.should.be.json;
|
||||
putBody.users[0].should.exist;
|
||||
putBody.users[0].website.should.eql(changedValue);
|
||||
|
||||
putBody.users[0].email.should.eql(jsonResponse.users[0].email);
|
||||
testUtils.API.checkResponse(putBody.users[0], 'user');
|
||||
done();
|
||||
});
|
||||
|
@ -195,6 +198,4 @@ describe('User API', function () {
|
|||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
|
@ -37,7 +37,7 @@ describe('DB API', function () {
|
|||
|
||||
it('delete all content', function (done) {
|
||||
permissions.init().then(function () {
|
||||
return dbAPI.deleteAllContent.call({user: 1});
|
||||
return dbAPI.deleteAllContent({context: {user: 1}});
|
||||
}).then(function (result) {
|
||||
should.exist(result.db);
|
||||
result.db.should.be.instanceof(Array);
|
||||
|
@ -61,12 +61,12 @@ describe('DB API', function () {
|
|||
|
||||
it('delete all content is denied', function (done) {
|
||||
permissions.init().then(function () {
|
||||
return dbAPI.deleteAllContent.call({user: 2});
|
||||
return dbAPI.deleteAllContent({context: {user: 2}});
|
||||
}).then(function (){
|
||||
done(new Error("Delete all content is not denied for editor."));
|
||||
}, function (error) {
|
||||
error.type.should.eql('NoPermissionError');
|
||||
return dbAPI.deleteAllContent.call({user: 3});
|
||||
return dbAPI.deleteAllContent({context: {user: 3}});
|
||||
}).then(function (){
|
||||
done(new Error("Delete all content is not denied for author."));
|
||||
}, function (error) {
|
||||
|
@ -82,12 +82,12 @@ describe('DB API', function () {
|
|||
|
||||
it('export content is denied', function (done) {
|
||||
permissions.init().then(function () {
|
||||
return dbAPI.exportContent.call({user: 2});
|
||||
return dbAPI.exportContent({context: {user: 2}});
|
||||
}).then(function (){
|
||||
done(new Error("Export content is not denied for editor."));
|
||||
}, function (error) {
|
||||
error.type.should.eql('NoPermissionError');
|
||||
return dbAPI.exportContent.call({user: 3});
|
||||
return dbAPI.exportContent({context: {user: 3}});
|
||||
}).then(function (){
|
||||
done(new Error("Export content is not denied for author."));
|
||||
}, function (error) {
|
||||
|
@ -103,12 +103,12 @@ describe('DB API', function () {
|
|||
|
||||
it('import content is denied', function (done) {
|
||||
permissions.init().then(function () {
|
||||
return dbAPI.importContent.call({user: 2});
|
||||
return dbAPI.importContent({context: {user: 2}});
|
||||
}).then(function (result){
|
||||
done(new Error("Import content is not denied for editor."));
|
||||
}, function (error) {
|
||||
error.type.should.eql('NoPermissionError');
|
||||
return dbAPI.importContent.call({user: 3});
|
||||
return dbAPI.importContent({context: {user: 3}});
|
||||
}).then(function (result){
|
||||
done(new Error("Import content is not denied for author."));
|
||||
}, function (error) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*globals describe, before, beforeEach, afterEach, it */
|
||||
/*globals describe, before, beforeEach, afterEach, it */
|
||||
var testUtils = require('../../utils'),
|
||||
should = require('should'),
|
||||
|
||||
|
|
|
@ -17,7 +17,14 @@ describe('Settings API', function () {
|
|||
internal: true
|
||||
},
|
||||
callApiWithContext = function (context, method) {
|
||||
return SettingsAPI[method].apply(context, _.toArray(arguments).slice(2));
|
||||
var args = _.toArray(arguments),
|
||||
options = args[args.length - 1];
|
||||
|
||||
if (_.isObject(options)) {
|
||||
options.context = _.clone(context);
|
||||
}
|
||||
|
||||
return SettingsAPI[method].apply({}, args.slice(2));
|
||||
},
|
||||
getErrorDetails = function (done) {
|
||||
return function (err) {
|
||||
|
@ -58,7 +65,7 @@ describe('Settings API', function () {
|
|||
});
|
||||
|
||||
it('can browse', function (done) {
|
||||
return callApiWithContext(defaultContext, 'browse', 'blog').then(function (results) {
|
||||
return callApiWithContext(defaultContext, 'browse', {}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'settings');
|
||||
results.settings.length.should.be.above(0);
|
||||
|
@ -71,8 +78,23 @@ describe('Settings API', function () {
|
|||
}).catch(getErrorDetails(done));
|
||||
});
|
||||
|
||||
it('returns core settings for internal requests when browsing', function (done){
|
||||
return callApiWithContext(internalContext, 'browse', 'blog').then(function (results) {
|
||||
|
||||
it('can browse by type', function (done) {
|
||||
return callApiWithContext(defaultContext, 'browse', {type: 'blog'}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'settings');
|
||||
results.settings.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(results.settings[0], 'setting');
|
||||
|
||||
// Check for a core setting
|
||||
should.not.exist(_.find(results.settings, function (setting) { return setting.type === 'core'; }));
|
||||
|
||||
done();
|
||||
}).catch(getErrorDetails(done));
|
||||
});
|
||||
|
||||
it('returns core settings for internal requests when browsing', function (done) {
|
||||
return callApiWithContext(internalContext, 'browse', {}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'settings');
|
||||
results.settings.length.should.be.above(0);
|
||||
|
@ -82,11 +104,11 @@ describe('Settings API', function () {
|
|||
should.exist(_.find(results.settings, function (setting) { return setting.type === 'core'; }));
|
||||
|
||||
done();
|
||||
}).catch(getErrorDetails(done));
|
||||
}).catch(getErrorDetails(done));
|
||||
});
|
||||
|
||||
it('can read by string', function (done) {
|
||||
return callApiWithContext(defaultContext, 'read', 'title').then(function (response) {
|
||||
it('can read blog settings by string', function (done) {
|
||||
return SettingsAPI.read('title').then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
|
@ -97,19 +119,17 @@ describe('Settings API', function () {
|
|||
});
|
||||
|
||||
it('cannot read core settings if not an internal request', function (done) {
|
||||
return callApiWithContext(defaultContext, 'read', 'databaseVersion').then(function (response) {
|
||||
return callApiWithContext(defaultContext, 'read', {key: 'databaseVersion'}).then(function (response) {
|
||||
done(new Error('Allowed to read databaseVersion with external request'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.message.should.equal('Attempted to access core setting on external request');
|
||||
|
||||
}).catch(function (error) {
|
||||
should.exist(error);
|
||||
error.type.should.eql('NoPermissionError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can read core settings if an internal request', function (done) {
|
||||
return callApiWithContext(internalContext, 'read', 'databaseVersion').then(function (response) {
|
||||
return callApiWithContext(internalContext, 'read', {key: 'databaseVersion'}).then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
|
@ -131,37 +151,40 @@ describe('Settings API', function () {
|
|||
});
|
||||
|
||||
it('can edit', function (done) {
|
||||
return callApiWithContext(defaultContext, 'edit', { settings: [{ key: 'title', value: 'UpdatedGhost'}]}).then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
testUtils.API.checkResponse(response.settings[0], 'setting');
|
||||
return callApiWithContext(defaultContext, 'edit', {settings: [{ key: 'title', value: 'UpdatedGhost'}]}, {})
|
||||
.then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
testUtils.API.checkResponse(response.settings[0], 'setting');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can edit, by key/value', function (done) {
|
||||
return callApiWithContext(defaultContext, 'edit', 'title', 'UpdatedGhost').then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
testUtils.API.checkResponse(response.settings[0], 'setting');
|
||||
|
||||
done();
|
||||
}).catch(getErrorDetails(done));
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('cannot edit a core setting if not an internal request', function (done) {
|
||||
return callApiWithContext(defaultContext, 'edit', 'databaseVersion', '999').then(function (response) {
|
||||
done(new Error('Allowed to edit a core setting as external request'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
return callApiWithContext(defaultContext, 'edit', {settings: [{ key: 'databaseVersion', value: '999'}]}, {})
|
||||
.then(function () {
|
||||
done(new Error('Allowed to edit a core setting as external request'));
|
||||
}).catch(function (err) {
|
||||
should.exist(err);
|
||||
|
||||
err.message.should.equal('Attempted to access core setting on external request');
|
||||
err.type.should.eql('NoPermissionError');
|
||||
|
||||
done();
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can edit a core setting with an internal request', function (done) {
|
||||
return callApiWithContext(internalContext, 'edit', {settings: [{ key: 'databaseVersion', value: '999'}]}, {})
|
||||
.then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'settings');
|
||||
response.settings.length.should.equal(1);
|
||||
testUtils.API.checkResponse(response.settings[0], 'setting');
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('ensures values are stringified before saving to database', function (done) {
|
||||
|
|
|
@ -8,7 +8,7 @@ var _ = require('lodash'),
|
|||
|
||||
// Stuff we are testing
|
||||
permissions = require('../../../server/permissions'),
|
||||
settings = require('../../../server/api/settings'),
|
||||
SettingsAPI = require('../../../server/api/settings'),
|
||||
ThemeAPI = rewire('../../../server/api/themes');
|
||||
|
||||
describe('Themes API', function () {
|
||||
|
@ -26,12 +26,15 @@ describe('Themes API', function () {
|
|||
testUtils.initData().then(function () {
|
||||
return testUtils.insertDefaultFixtures();
|
||||
}).then(function () {
|
||||
return SettingsAPI.updateSettingsCache();
|
||||
}).then(function () {
|
||||
|
||||
return permissions.init();
|
||||
}).then(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
// Override settings.read for activeTheme
|
||||
settingsReadStub = sandbox.stub(settings, 'read', function () {
|
||||
settingsReadStub = sandbox.stub(SettingsAPI, 'read', function () {
|
||||
return when({ settings: [{value: 'casper'}] });
|
||||
});
|
||||
|
||||
|
@ -67,14 +70,14 @@ describe('Themes API', function () {
|
|||
_.extend(configStub, config);
|
||||
ThemeAPI.__set__('config', configStub);
|
||||
|
||||
ThemeAPI.browse.call({user: 1}).then(function (result) {
|
||||
ThemeAPI.browse({context: {user: 1}}).then(function (result) {
|
||||
should.exist(result);
|
||||
result.themes.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(result.themes[0], 'theme');
|
||||
done();
|
||||
}, function (error) {
|
||||
}).catch(function (error) {
|
||||
done(new Error(JSON.stringify(error)));
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('can edit', function (done) {
|
||||
|
@ -84,15 +87,15 @@ describe('Themes API', function () {
|
|||
_.extend(configStub, config);
|
||||
ThemeAPI.__set__('config', configStub);
|
||||
|
||||
ThemeAPI.edit.call({user: 1}, {themes: [{uuid: 'rasper', active: true }]}).then(function (result) {
|
||||
ThemeAPI.edit({themes: [{uuid: 'rasper', active: true }]}, {context: {user: 1}}).then(function (result) {
|
||||
should.exist(result);
|
||||
should.exist(result.themes);
|
||||
result.themes.length.should.be.above(0);
|
||||
testUtils.API.checkResponse(result.themes[0], 'theme');
|
||||
result.themes[0].uuid.should.equal('rasper');
|
||||
done();
|
||||
}, function (error) {
|
||||
}).catch(function (error) {
|
||||
done(new Error(JSON.stringify(error)));
|
||||
})
|
||||
});
|
||||
})
|
||||
});
|
|
@ -2,6 +2,8 @@
|
|||
var testUtils = require('../../utils'),
|
||||
should = require('should'),
|
||||
|
||||
permissions = require('../../../server/permissions'),
|
||||
|
||||
// Stuff we are testing
|
||||
UsersAPI = require('../../../server/api/users');
|
||||
|
||||
|
@ -22,11 +24,12 @@ describe('Users API', function () {
|
|||
describe('No User', function () {
|
||||
beforeEach(function (done) {
|
||||
testUtils.initData().then(function () {
|
||||
return permissions.init();
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
|
||||
it('can add with internal user', function (done) {
|
||||
UsersAPI.register({ users: [{
|
||||
'name': 'Hello World',
|
||||
|
@ -52,13 +55,15 @@ describe('Users API', function () {
|
|||
return testUtils.insertEditorUser();
|
||||
}).then(function () {
|
||||
return testUtils.insertAuthorUser();
|
||||
}).then(function () {
|
||||
return permissions.init();
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('admin can browse', function (done) {
|
||||
UsersAPI.browse.call({user: 1}).then(function (results) {
|
||||
UsersAPI.browse({context: {user: 1}}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'users');
|
||||
should.exist(results.users);
|
||||
|
@ -71,7 +76,7 @@ describe('Users API', function () {
|
|||
});
|
||||
|
||||
it('editor can browse', function (done) {
|
||||
UsersAPI.browse.call({user: 2}).then(function (results) {
|
||||
UsersAPI.browse({context: {user: 2}}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'users');
|
||||
should.exist(results.users);
|
||||
|
@ -84,7 +89,7 @@ describe('Users API', function () {
|
|||
});
|
||||
|
||||
it('author can browse', function (done) {
|
||||
UsersAPI.browse.call({user: 3}).then(function (results) {
|
||||
UsersAPI.browse({context: {user: 3}}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'users');
|
||||
should.exist(results.users);
|
||||
|
@ -105,7 +110,7 @@ describe('Users API', function () {
|
|||
});
|
||||
|
||||
it('admin can read', function (done) {
|
||||
UsersAPI.read.call({user: 1}, {id: 1}).then(function (results) {
|
||||
UsersAPI.read({id: 1, context: {user: 1}}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'users');
|
||||
results.users[0].id.should.eql(1);
|
||||
|
@ -115,7 +120,7 @@ describe('Users API', function () {
|
|||
});
|
||||
|
||||
it('editor can read', function (done) {
|
||||
UsersAPI.read.call({user: 2}, {id: 1}).then(function (results) {
|
||||
UsersAPI.read({id: 1, context: {user: 2}}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'users');
|
||||
results.users[0].id.should.eql(1);
|
||||
|
@ -125,7 +130,7 @@ describe('Users API', function () {
|
|||
});
|
||||
|
||||
it('author can read', function (done) {
|
||||
UsersAPI.read.call({user: 3}, {id: 1}).then(function (results) {
|
||||
UsersAPI.read({id: 1, context: {user: 3}}).then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'users');
|
||||
results.users[0].id.should.eql(1);
|
||||
|
@ -145,7 +150,7 @@ describe('Users API', function () {
|
|||
});
|
||||
|
||||
it('admin can edit', function (done) {
|
||||
UsersAPI.edit.call({user: 1}, {users: [{id: 1, name: 'Joe Blogger'}]}).then(function (response) {
|
||||
UsersAPI.edit({users: [{name: 'Joe Blogger'}]}, {id: 1, context: {user: 1}}).then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'users');
|
||||
response.users.should.have.length(1);
|
||||
|
@ -157,7 +162,7 @@ describe('Users API', function () {
|
|||
});
|
||||
|
||||
it('editor can edit', function (done) {
|
||||
UsersAPI.edit.call({user: 2}, {users: [{id: 1, name: 'Joe Blogger'}]}).then(function (response) {
|
||||
UsersAPI.edit({users: [{name: 'Joe Blogger'}]}, {id: 1, context: {user: 2}}).then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'users');
|
||||
response.users.should.have.length(1);
|
||||
|
@ -170,13 +175,13 @@ describe('Users API', function () {
|
|||
|
||||
it('author can edit only self', function (done) {
|
||||
// Test author cannot edit admin user
|
||||
UsersAPI.edit.call({user: 3}, {users: [{id: 1, name: 'Joe Blogger'}]}).then(function () {
|
||||
UsersAPI.edit({users: [{name: 'Joe Blogger'}]}, {id: 1, context: {user: 3}}).then(function () {
|
||||
done(new Error('Author should not be able to edit account which is not their own'));
|
||||
}).catch(function (error) {
|
||||
error.code.should.eql(403);
|
||||
error.type.should.eql('NoPermissionError');
|
||||
}).finally(function () {
|
||||
// Next test that author CAN edit self
|
||||
return UsersAPI.edit.call({user: 3}, {users: [{id: 3, name: 'Timothy Bogendath'}]})
|
||||
return UsersAPI.edit({users: [{name: 'Timothy Bogendath'}]}, {id: 3, context: {user: 3}})
|
||||
.then(function (response) {
|
||||
should.exist(response);
|
||||
testUtils.API.checkResponse(response, 'users');
|
||||
|
|
|
@ -86,19 +86,20 @@ describe('App Model', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it("can delete", function (done) {
|
||||
AppModel.findOne({id: 1}).then(function (foundApp) {
|
||||
it("can destroy", function (done) {
|
||||
var firstApp = {id: 1};
|
||||
|
||||
AppModel.findOne(firstApp).then(function (foundApp) {
|
||||
should.exist(foundApp);
|
||||
foundApp.attributes.id.should.equal(firstApp.id);
|
||||
|
||||
return AppModel.destroy(1);
|
||||
}).then(function () {
|
||||
return AppModel.findAll();
|
||||
}).then(function (foundApp) {
|
||||
var hasRemovedId = foundApp.any(function (foundApp) {
|
||||
return foundApp.id === 1;
|
||||
});
|
||||
return AppModel.destroy(firstApp);
|
||||
}).then(function (response) {
|
||||
response.toJSON().should.be.empty;
|
||||
|
||||
hasRemovedId.should.equal(false);
|
||||
return AppModel.findOne(firstApp);
|
||||
}).then(function (newResults) {
|
||||
should.equal(newResults, null);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
|
|
|
@ -80,19 +80,19 @@ describe('Permission Model', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can delete', function (done) {
|
||||
PermissionModel.findOne({id: 1}).then(function (foundPermission) {
|
||||
it('can destroy', function (done) {
|
||||
var firstPermission = {id: 1};
|
||||
|
||||
PermissionModel.findOne(firstPermission).then(function (foundPermission) {
|
||||
should.exist(foundPermission);
|
||||
foundPermission.attributes.id.should.equal(firstPermission.id);
|
||||
|
||||
return PermissionModel.destroy(1);
|
||||
}).then(function () {
|
||||
return PermissionModel.findAll();
|
||||
}).then(function (foundPermissions) {
|
||||
var hasRemovedId = foundPermissions.any(function (permission) {
|
||||
return permission.id === 1;
|
||||
});
|
||||
|
||||
hasRemovedId.should.equal(false);
|
||||
return PermissionModel.destroy(firstPermission);
|
||||
}).then(function (response) {
|
||||
response.toJSON().should.be.empty;
|
||||
return PermissionModel.findOne(firstPermission);
|
||||
}).then(function (newResults) {
|
||||
should.equal(newResults, null);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
|
|
|
@ -35,19 +35,80 @@ describe('Post Model', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
function checkFirstPostData(firstPost) {
|
||||
should.not.exist(firstPost.author_id);
|
||||
firstPost.author.should.be.an.Object;
|
||||
firstPost.fields.should.be.an.Array;
|
||||
firstPost.tags.should.be.an.Array;
|
||||
firstPost.author.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.fields[0].key.should.equal(DataGenerator.Content.app_fields[0].key);
|
||||
firstPost.created_by.should.be.an.Object;
|
||||
firstPost.updated_by.should.be.an.Object;
|
||||
firstPost.published_by.should.be.an.Object;
|
||||
firstPost.created_by.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.updated_by.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.published_by.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.tags[0].name.should.equal('Getting Started');
|
||||
}
|
||||
|
||||
it('can findAll', function (done) {
|
||||
PostModel.findAll().then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.be.above(1);
|
||||
|
||||
// should be in published_at, DESC order
|
||||
// model and API differ here - need to fix
|
||||
//results.models[0].attributes.published_at.should.be.above(results.models[1].attributes.published_at);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can findAll, returning all related data', function (done) {
|
||||
var firstPost;
|
||||
|
||||
PostModel.findAll({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.be.above(0);
|
||||
firstPost = results.models[0].toJSON();
|
||||
|
||||
checkFirstPostData(firstPost);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can findPage (default)', function (done) {
|
||||
PostModel.findPage().then(function (results) {
|
||||
should.exist(results);
|
||||
|
||||
results.meta.pagination.page.should.equal(1);
|
||||
results.meta.pagination.limit.should.equal(15);
|
||||
results.meta.pagination.pages.should.equal(1);
|
||||
results.posts.length.should.equal(5);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can findPage, returning all related data', function (done) {
|
||||
var firstPost;
|
||||
|
||||
PostModel.findPage({include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
|
||||
results.meta.pagination.page.should.equal(1);
|
||||
results.meta.pagination.limit.should.equal(15);
|
||||
results.meta.pagination.pages.should.equal(1);
|
||||
results.posts.length.should.equal(5);
|
||||
|
||||
firstPost = results.posts[0];
|
||||
|
||||
checkFirstPostData(firstPost);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
|
||||
it('can findOne', function (done) {
|
||||
var firstPost;
|
||||
|
||||
|
@ -66,50 +127,31 @@ describe('Post Model', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can findAll, returning author and field data', function (done) {
|
||||
var firstPost;
|
||||
|
||||
PostModel.findAll({include: ['author_id', 'fields']}).then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.be.above(0);
|
||||
firstPost = results.models[0].toJSON();
|
||||
|
||||
should.not.exist(firstPost.author_id);
|
||||
firstPost.author.should.be.an.Object;
|
||||
firstPost.fields.should.be.an.Array;
|
||||
firstPost.author.name.should.equal(DataGenerator.Content.users[0].name);
|
||||
firstPost.fields[0].key.should.equal(DataGenerator.Content.app_fields[0].key);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can findOne, returning author and field data', function (done) {
|
||||
it('can findOne, returning all related data', function (done) {
|
||||
var firstPost;
|
||||
// TODO: should take author :-/
|
||||
PostModel.findOne({}, {include: ['author_id', 'fields']}).then(function (result) {
|
||||
should.exist(result);
|
||||
firstPost = result.toJSON();
|
||||
PostModel.findOne({}, {include: ['author_id', 'fields', 'tags', 'created_by', 'updated_by', 'published_by']})
|
||||
.then(function (result) {
|
||||
should.exist(result);
|
||||
firstPost = result.toJSON();
|
||||
|
||||
should.not.exist(firstPost.author_id);
|
||||
firstPost.author.should.be.an.Object;
|
||||
firstPost.fields.should.be.an.Array;
|
||||
firstPost.author.name.should.equal(testUtils.DataGenerator.Content.users[0].name);
|
||||
firstPost.fields[0].key.should.equal(DataGenerator.Content.app_fields[0].key);
|
||||
checkFirstPostData(firstPost);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can edit', function (done) {
|
||||
var firstPost;
|
||||
var firstPost = 1;
|
||||
|
||||
PostModel.findAll().then(function (results) {
|
||||
PostModel.findOne({id: firstPost}).then(function (results) {
|
||||
var post;
|
||||
should.exist(results);
|
||||
results.length.should.be.above(0);
|
||||
firstPost = results.models[0];
|
||||
post = results.toJSON();
|
||||
post.id.should.equal(firstPost);
|
||||
post.title.should.not.equal('new title');
|
||||
|
||||
return PostModel.edit({id: firstPost.id, title: 'new title'});
|
||||
return PostModel.edit({title: 'new title'}, {id: firstPost});
|
||||
}).then(function (edited) {
|
||||
should.exist(edited);
|
||||
edited.attributes.title.should.equal('new title');
|
||||
|
@ -118,6 +160,7 @@ describe('Post Model', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
|
||||
it('can add, defaults are all correct', function (done) {
|
||||
var createdPostUpdatedDate,
|
||||
newPost = testUtils.DataGenerator.forModel.posts[2],
|
||||
|
@ -330,18 +373,32 @@ describe('Post Model', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can delete', function (done) {
|
||||
var firstPostId;
|
||||
PostModel.findAll().then(function (results) {
|
||||
should.exist(results);
|
||||
results.length.should.be.above(0);
|
||||
firstPostId = results.models[0].id;
|
||||
it('can destroy', function (done) {
|
||||
// We're going to try deleting post id 1 which also has tag id 1
|
||||
var firstItemData = {id: 1};
|
||||
|
||||
return PostModel.destroy(firstPostId);
|
||||
}).then(function () {
|
||||
return PostModel.findOne({id: firstPostId});
|
||||
// Test that we have the post we expect, with exactly one tag
|
||||
PostModel.findOne(firstItemData).then(function (results) {
|
||||
var post;
|
||||
should.exist(results);
|
||||
post = results.toJSON();
|
||||
post.id.should.equal(firstItemData.id);
|
||||
post.tags.should.have.length(1);
|
||||
post.tags[0].should.equal(firstItemData.id);
|
||||
|
||||
// Destroy the post
|
||||
return PostModel.destroy(firstItemData);
|
||||
}).then(function (response) {
|
||||
var deleted = response.toJSON();
|
||||
|
||||
deleted.tags.should.be.empty;
|
||||
should.equal(deleted.author, undefined);
|
||||
|
||||
// Double check we can't find the post again
|
||||
return PostModel.findOne(firstItemData);
|
||||
}).then(function (newResults) {
|
||||
should.equal(newResults, null);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
@ -372,6 +429,7 @@ describe('Post Model', function () {
|
|||
paginationResult.meta.pagination.pages.should.equal(2);
|
||||
paginationResult.posts.length.should.equal(30);
|
||||
|
||||
// Test both boolean formats
|
||||
return PostModel.findPage({limit: 10, staticPages: true});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
|
@ -379,10 +437,26 @@ describe('Post Model', function () {
|
|||
paginationResult.meta.pagination.pages.should.equal(1);
|
||||
paginationResult.posts.length.should.equal(1);
|
||||
|
||||
// Test both boolean formats
|
||||
return PostModel.findPage({limit: 10, staticPages: '1'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.page.should.equal(1);
|
||||
paginationResult.meta.pagination.limit.should.equal(10);
|
||||
paginationResult.meta.pagination.pages.should.equal(1);
|
||||
paginationResult.posts.length.should.equal(1);
|
||||
|
||||
return PostModel.findPage({limit: 10, page: 2, status: 'all'});
|
||||
}).then(function (paginationResult) {
|
||||
paginationResult.meta.pagination.pages.should.equal(11);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
it('can findPage for tag, with various options', function (done) {
|
||||
testUtils.insertMorePosts().then(function () {
|
||||
|
||||
return testUtils.insertMorePostsTags();
|
||||
}).then(function () {
|
||||
// Test tag filter
|
||||
return PostModel.findPage({page: 1, tag: 'bacon'});
|
||||
}).then(function (paginationResult) {
|
||||
|
|
|
@ -79,19 +79,19 @@ describe('Role Model', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can delete', function (done) {
|
||||
RoleModel.findOne({id: 1}).then(function (foundRole) {
|
||||
it('can destroy', function (done) {
|
||||
var firstRole = {id: 1};
|
||||
|
||||
RoleModel.findOne(firstRole).then(function (foundRole) {
|
||||
should.exist(foundRole);
|
||||
foundRole.attributes.id.should.equal(firstRole.id);
|
||||
|
||||
return RoleModel.destroy(1);
|
||||
}).then(function (destResp) {
|
||||
return RoleModel.findAll();
|
||||
}).then(function (foundRoles) {
|
||||
var hasRemovedId = foundRoles.any(function (role) {
|
||||
return role.id === 1;
|
||||
});
|
||||
|
||||
hasRemovedId.should.equal(false);
|
||||
return RoleModel.destroy(firstRole);
|
||||
}).then(function (response) {
|
||||
response.toJSON().should.be.empty;
|
||||
return RoleModel.findOne(firstRole);
|
||||
}).then(function (newResults) {
|
||||
should.equal(newResults, null);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
|
|
|
@ -153,39 +153,23 @@ describe('Settings Model', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can delete', function (done) {
|
||||
var settingId;
|
||||
|
||||
SettingsModel.findAll().then(function (results) {
|
||||
it('can destroy', function (done) {
|
||||
// dont't use id 1, since it will delete databaseversion
|
||||
var settingToDestroy = {id: 2};
|
||||
|
||||
SettingsModel.findOne(settingToDestroy).then(function (results) {
|
||||
should.exist(results);
|
||||
results.attributes.id.should.equal(settingToDestroy.id);
|
||||
|
||||
results.length.should.be.above(0);
|
||||
|
||||
// dont't use results.models[0], since it will delete databaseversion
|
||||
// which is used for testUtils.reset()
|
||||
settingId = results.models[1].id;
|
||||
|
||||
return SettingsModel.destroy(settingId);
|
||||
|
||||
}).then(function () {
|
||||
|
||||
return SettingsModel.findAll();
|
||||
return SettingsModel.destroy(settingToDestroy);
|
||||
}).then(function (response) {
|
||||
response.toJSON().should.be.empty;
|
||||
|
||||
return SettingsModel.findOne(settingToDestroy);
|
||||
}).then(function (newResults) {
|
||||
|
||||
var ids, hasDeletedId;
|
||||
|
||||
ids = _.pluck(newResults.models, 'id');
|
||||
|
||||
hasDeletedId = _.any(ids, function (id) {
|
||||
return id === settingId;
|
||||
});
|
||||
|
||||
hasDeletedId.should.equal(false);
|
||||
should.equal(newResults, null);
|
||||
|
||||
done();
|
||||
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -144,7 +144,7 @@ describe('User Model', function run() {
|
|||
it('sets last login time on successful login', function (done) {
|
||||
var userData = testUtils.DataGenerator.forModel.users[0];
|
||||
|
||||
UserModel.check({email: userData.email, pw:userData.password}).then(function (activeUser) {
|
||||
UserModel.check({email: userData.email, pw: userData.password}).then(function (activeUser) {
|
||||
should.exist(activeUser.get('last_login'));
|
||||
done();
|
||||
}).catch(done);
|
||||
|
@ -175,19 +175,13 @@ describe('User Model', function run() {
|
|||
var firstUser;
|
||||
|
||||
UserModel.findAll().then(function (results) {
|
||||
|
||||
should.exist(results);
|
||||
|
||||
results.length.should.be.above(0);
|
||||
|
||||
firstUser = results.models[0];
|
||||
|
||||
return UserModel.findOne({email: firstUser.attributes.email});
|
||||
|
||||
}).then(function (found) {
|
||||
|
||||
should.exist(found);
|
||||
|
||||
found.attributes.name.should.equal(firstUser.attributes.name);
|
||||
|
||||
done();
|
||||
|
@ -197,22 +191,18 @@ describe('User Model', function run() {
|
|||
});
|
||||
|
||||
it('can edit', function (done) {
|
||||
var firstUser;
|
||||
|
||||
UserModel.findAll().then(function (results) {
|
||||
var firstUser = 1;
|
||||
|
||||
UserModel.findOne({id: firstUser}).then(function (results) {
|
||||
var user;
|
||||
should.exist(results);
|
||||
user = results.toJSON();
|
||||
user.id.should.equal(firstUser);
|
||||
should.equal(user.website, null);
|
||||
|
||||
results.length.should.be.above(0);
|
||||
|
||||
firstUser = results.models[0];
|
||||
|
||||
return UserModel.edit({id: firstUser.id, website: "some.newurl.com"});
|
||||
|
||||
return UserModel.edit({website: 'some.newurl.com'}, {id: firstUser});
|
||||
}).then(function (edited) {
|
||||
|
||||
should.exist(edited);
|
||||
|
||||
edited.attributes.website.should.equal('some.newurl.com');
|
||||
|
||||
done();
|
||||
|
@ -220,41 +210,43 @@ describe('User Model', function run() {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can delete', function (done) {
|
||||
var firstUserId;
|
||||
it('can destroy', function (done) {
|
||||
var firstUser = {id: 1};
|
||||
|
||||
UserModel.findAll().then(function (results) {
|
||||
// Test that we have the user we expect
|
||||
UserModel.findOne(firstUser).then(function (results) {
|
||||
|
||||
var user;
|
||||
should.exist(results);
|
||||
user = results.toJSON();
|
||||
user.id.should.equal(firstUser.id);
|
||||
|
||||
results.length.should.be.above(0);
|
||||
|
||||
firstUserId = results.models[0].id;
|
||||
|
||||
return UserModel.destroy(firstUserId);
|
||||
|
||||
}).then(function () {
|
||||
|
||||
return UserModel.findAll();
|
||||
// Destroy the user
|
||||
return UserModel.destroy(firstUser);
|
||||
}).then(function (response) {
|
||||
response.toJSON().should.be.empty;
|
||||
|
||||
// Double check we can't find the user again
|
||||
return UserModel.findOne(firstUser);
|
||||
}).then(function (newResults) {
|
||||
var ids, hasDeletedId;
|
||||
should.equal(newResults, null);
|
||||
|
||||
if (newResults.length < 1) {
|
||||
// Bug out if we only had one user and deleted it.
|
||||
return done();
|
||||
}
|
||||
|
||||
ids = _.pluck(newResults.models, "id");
|
||||
hasDeletedId = _.any(ids, function (id) {
|
||||
return id === firstUserId;
|
||||
});
|
||||
|
||||
hasDeletedId.should.equal(false);
|
||||
done();
|
||||
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Password Reset', function () {
|
||||
|
||||
beforeEach(function (done) {
|
||||
testUtils.initData()
|
||||
.then(function () {
|
||||
return when(testUtils.insertDefaultUser());
|
||||
})
|
||||
.then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can generate reset token', function (done) {
|
||||
// Expires in one minute
|
||||
|
|
|
@ -44,7 +44,7 @@ describe('Frontend Controller', function () {
|
|||
});
|
||||
|
||||
apiSettingsStub = sandbox.stub(api.settings, 'read');
|
||||
apiSettingsStub.withArgs('postsPerPage').returns(when({
|
||||
apiSettingsStub.withArgs('postsPerPage').returns(when({
|
||||
settings: [{
|
||||
'key': 'postsPerPage',
|
||||
'value': 6
|
||||
|
@ -181,7 +181,7 @@ describe('Frontend Controller', function () {
|
|||
done(new Error(msg));
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
beforeEach(function () {
|
||||
sandbox.stub(api.posts, 'browse', function (args) {
|
||||
return when({
|
||||
|
@ -197,23 +197,23 @@ describe('Frontend Controller', function () {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
apiSettingsStub = sandbox.stub(api.settings, 'read');
|
||||
|
||||
apiSettingsStub.withArgs('activeTheme').returns(when({
|
||||
|
||||
apiSettingsStub.withArgs(sinon.match.has('key', 'activeTheme')).returns(when({
|
||||
settings: [{
|
||||
'key': 'activeTheme',
|
||||
'value': 'casper'
|
||||
}]
|
||||
}));
|
||||
|
||||
|
||||
apiSettingsStub.withArgs('postsPerPage').returns(when({
|
||||
settings: [{
|
||||
'key': 'postsPerPage',
|
||||
'value': '10'
|
||||
}]
|
||||
}));
|
||||
|
||||
|
||||
frontend.__set__('config', sandbox.stub().returns({
|
||||
'paths': {
|
||||
'subdir': '',
|
||||
|
@ -229,15 +229,18 @@ describe('Frontend Controller', function () {
|
|||
}
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
describe('custom tag template', function () {
|
||||
|
||||
|
||||
beforeEach(function () {
|
||||
apiSettingsStub.withArgs('permalinks').returns(when({
|
||||
value: '/tag/:slug/'
|
||||
settings: [{
|
||||
key: 'permalinks',
|
||||
value: '/tag/:slug/'
|
||||
}]
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it('it will render custom tag template if it exists', function (done) {
|
||||
var req = {
|
||||
path: '/tag/' + mockTags[0].slug,
|
||||
|
@ -250,7 +253,7 @@ describe('Frontend Controller', function () {
|
|||
done();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
frontend.tag(req, res, failTest(done));
|
||||
});
|
||||
});
|
||||
|
@ -422,7 +425,7 @@ describe('Frontend Controller', function () {
|
|||
|
||||
apiSettingsStub = sandbox.stub(api.settings, 'read');
|
||||
|
||||
apiSettingsStub.withArgs('activeTheme').returns(when({
|
||||
apiSettingsStub.withArgs(sinon.match.has('key', 'activeTheme')).returns(when({
|
||||
settings: [{
|
||||
'key': 'activeTheme',
|
||||
'value': 'casper'
|
||||
|
@ -451,7 +454,7 @@ describe('Frontend Controller', function () {
|
|||
describe('custom page templates', function () {
|
||||
beforeEach(function () {
|
||||
apiSettingsStub.withArgs('permalinks').returns(when({
|
||||
settings: [{
|
||||
settings: [{
|
||||
value: '/:slug/'
|
||||
}]
|
||||
}));
|
||||
|
@ -547,8 +550,8 @@ describe('Frontend Controller', function () {
|
|||
beforeEach(function () {
|
||||
apiSettingsStub.withArgs('permalinks').returns(when({
|
||||
settings: [{
|
||||
value: '/:year/:month/:day/:slug/'
|
||||
}]
|
||||
value: '/:year/:month/:day/:slug/'
|
||||
}]
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -621,7 +624,7 @@ describe('Frontend Controller', function () {
|
|||
apiSettingsStub.withArgs('permalinks').returns(when({
|
||||
settings: [{
|
||||
value: '/:slug'
|
||||
}]
|
||||
}]
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -694,7 +697,7 @@ describe('Frontend Controller', function () {
|
|||
apiSettingsStub.withArgs('permalinks').returns(when({
|
||||
settings: [{
|
||||
value: '/:year/:month/:day/:slug'
|
||||
}]
|
||||
}]
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -784,7 +787,7 @@ describe('Frontend Controller', function () {
|
|||
apiSettingsStub.withArgs('permalinks').returns(when({
|
||||
settings: [{
|
||||
value: '/:year/:slug'
|
||||
}]
|
||||
}]
|
||||
}));
|
||||
});
|
||||
|
||||
|
|
|
@ -24,16 +24,6 @@ describe('Permissions', function () {
|
|||
}).catch(done);
|
||||
});
|
||||
|
||||
beforeEach(function (done) {
|
||||
sandbox = sinon.sandbox.create();
|
||||
testUtils.initData()
|
||||
.then(testUtils.insertDefaultUser)
|
||||
.then(testUtils.insertDefaultApp)
|
||||
.then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
afterEach(function (done) {
|
||||
sandbox.restore();
|
||||
testUtils.clearData()
|
||||
|
@ -60,21 +50,7 @@ describe('Permissions', function () {
|
|||
{ act: "remove", obj: "user" }
|
||||
],
|
||||
currTestPermId = 1,
|
||||
// currTestUserId = 1,
|
||||
// createTestUser = function (email) {
|
||||
// if (!email) {
|
||||
// currTestUserId += 1;
|
||||
// email = "test" + currTestPermId + "@test.com";
|
||||
// }
|
||||
|
||||
// var newUser = {
|
||||
// id: currTestUserId,
|
||||
// email: email,
|
||||
// password: "testing123"
|
||||
// };
|
||||
|
||||
// return UserProvider.add(newUser);
|
||||
// },
|
||||
createPermission = function (name, act, obj) {
|
||||
if (!name) {
|
||||
currTestPermId += 1;
|
||||
|
@ -97,347 +73,374 @@ describe('Permissions', function () {
|
|||
return when.all(createActions);
|
||||
};
|
||||
|
||||
it('can load an actions map from existing permissions', function (done) {
|
||||
describe('Init Permissions', function () {
|
||||
|
||||
createTestPermissions()
|
||||
.then(permissions.init)
|
||||
.then(function (actionsMap) {
|
||||
should.exist(actionsMap);
|
||||
beforeEach(function (done) {
|
||||
sandbox = sinon.sandbox.create();
|
||||
testUtils.initData()
|
||||
.then(testUtils.insertDefaultUser)
|
||||
.then(testUtils.insertDefaultApp)
|
||||
.then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
actionsMap.edit.sort().should.eql(['post', 'tag', 'user', 'page', 'theme', 'setting'].sort());
|
||||
it('can load an actions map from existing permissions', function (done) {
|
||||
createTestPermissions()
|
||||
.then(permissions.init)
|
||||
.then(function (actionsMap) {
|
||||
should.exist(actionsMap);
|
||||
|
||||
actionsMap.should.equal(permissions.actionsMap);
|
||||
actionsMap.edit.sort().should.eql(['post', 'tag', 'user', 'page', 'theme', 'setting'].sort());
|
||||
|
||||
actionsMap.should.equal(permissions.actionsMap);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add user to role', function (done) {
|
||||
var existingUserRoles;
|
||||
|
||||
UserProvider.findOne({id: 1}, { withRelated: ['roles'] }).then(function (foundUser) {
|
||||
var testRole = new Models.Role({
|
||||
name: 'testrole1',
|
||||
description: 'testrole1 description'
|
||||
});
|
||||
|
||||
should.exist(foundUser);
|
||||
|
||||
should.exist(foundUser.roles());
|
||||
|
||||
existingUserRoles = foundUser.related('roles').length;
|
||||
|
||||
return testRole.save(null, {user: 1}).then(function () {
|
||||
return foundUser.roles().attach(testRole);
|
||||
});
|
||||
}).then(function () {
|
||||
return UserProvider.findOne({id: 1}, { withRelated: ['roles'] });
|
||||
}).then(function (updatedUser) {
|
||||
should.exist(updatedUser);
|
||||
|
||||
updatedUser.related('roles').length.should.equal(existingUserRoles + 1);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add user to role', function (done) {
|
||||
var existingUserRoles;
|
||||
|
||||
UserProvider.findOne({id: 1}, { withRelated: ['roles'] }).then(function (foundUser) {
|
||||
var testRole = new Models.Role({
|
||||
name: 'testrole1',
|
||||
description: 'testrole1 description'
|
||||
});
|
||||
|
||||
should.exist(foundUser);
|
||||
|
||||
should.exist(foundUser.roles());
|
||||
|
||||
existingUserRoles = foundUser.related('roles').length;
|
||||
|
||||
return testRole.save(null, {user: 1}).then(function () {
|
||||
return foundUser.roles().attach(testRole);
|
||||
});
|
||||
}).then(function () {
|
||||
return UserProvider.findOne({id: 1}, { withRelated: ['roles'] });
|
||||
}).then(function (updatedUser) {
|
||||
should.exist(updatedUser);
|
||||
|
||||
updatedUser.related('roles').length.should.equal(existingUserRoles + 1);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add user permissions', function (done) {
|
||||
UserProvider.findOne({id: 1}, { withRelated: ['permissions']}).then(function (testUser) {
|
||||
var testPermission = new Models.Permission({
|
||||
name: "test edit posts",
|
||||
action_type: 'edit',
|
||||
object_type: 'post'
|
||||
});
|
||||
|
||||
testUser.related('permissions').length.should.equal(0);
|
||||
|
||||
return testPermission.save(null, {user: 1}).then(function () {
|
||||
return testUser.permissions().attach(testPermission);
|
||||
});
|
||||
}).then(function () {
|
||||
return UserProvider.findOne({id: 1}, { withRelated: ['permissions']});
|
||||
}).then(function (updatedUser) {
|
||||
should.exist(updatedUser);
|
||||
|
||||
updatedUser.related('permissions').length.should.equal(1);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can add role permissions', function (done) {
|
||||
var testRole = new Models.Role({
|
||||
name: "test2",
|
||||
description: "test2 description"
|
||||
});
|
||||
|
||||
testRole.save(null, {user: 1})
|
||||
.then(function () {
|
||||
return testRole.load('permissions');
|
||||
})
|
||||
.then(function () {
|
||||
var rolePermission = new Models.Permission({
|
||||
it('can add user permissions', function (done) {
|
||||
UserProvider.findOne({id: 1}, { withRelated: ['permissions']}).then(function (testUser) {
|
||||
var testPermission = new Models.Permission({
|
||||
name: "test edit posts",
|
||||
action_type: 'edit',
|
||||
object_type: 'post'
|
||||
});
|
||||
|
||||
testRole.related('permissions').length.should.equal(0);
|
||||
testUser.related('permissions').length.should.equal(0);
|
||||
|
||||
return rolePermission.save(null, {user: 1}).then(function () {
|
||||
return testRole.permissions().attach(rolePermission);
|
||||
return testPermission.save(null, {user: 1}).then(function () {
|
||||
return testUser.permissions().attach(testPermission);
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return Models.Role.findOne({id: testRole.id}, { withRelated: ['permissions']});
|
||||
})
|
||||
.then(function (updatedRole) {
|
||||
should.exist(updatedRole);
|
||||
|
||||
updatedRole.related('permissions').length.should.equal(1);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('does not allow edit post without permission', function (done) {
|
||||
var fakePage = {
|
||||
id: 1
|
||||
};
|
||||
|
||||
createTestPermissions()
|
||||
.then(permissions.init)
|
||||
.then(function () {
|
||||
return UserProvider.findOne({id: 1});
|
||||
})
|
||||
.then(function (foundUser) {
|
||||
var canThisResult = permissions.canThis(foundUser);
|
||||
|
||||
should.exist(canThisResult.edit);
|
||||
should.exist(canThisResult.edit.post);
|
||||
|
||||
return canThisResult.edit.page(fakePage);
|
||||
})
|
||||
.then(function () {
|
||||
errors.logError(new Error("Allowed edit post without permission"));
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('allows edit post with permission', function (done) {
|
||||
var fakePost = {
|
||||
id: "1"
|
||||
};
|
||||
|
||||
createTestPermissions()
|
||||
.then(permissions.init)
|
||||
.then(function () {
|
||||
return UserProvider.findOne({id: 1});
|
||||
})
|
||||
.then(function (foundUser) {
|
||||
var newPerm = new Models.Permission({
|
||||
name: "test3 edit post",
|
||||
action_type: "edit",
|
||||
object_type: "post"
|
||||
});
|
||||
|
||||
return newPerm.save(null, {user: 1}).then(function () {
|
||||
return foundUser.permissions().attach(newPerm);
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
}).then(function () {
|
||||
return UserProvider.findOne({id: 1}, { withRelated: ['permissions']});
|
||||
})
|
||||
.then(function (updatedUser) {
|
||||
}).then(function (updatedUser) {
|
||||
should.exist(updatedUser);
|
||||
|
||||
// TODO: Verify updatedUser.related('permissions') has the permission?
|
||||
var canThisResult = permissions.canThis(updatedUser.id);
|
||||
updatedUser.related('permissions').length.should.equal(1);
|
||||
|
||||
should.exist(canThisResult.edit);
|
||||
should.exist(canThisResult.edit.post);
|
||||
|
||||
return canThisResult.edit.post(fakePost);
|
||||
})
|
||||
.then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('can use permissable function on Model to allow something', function (done) {
|
||||
var testUser,
|
||||
permissableStub = sandbox.stub(PostProvider, 'permissable', function () {
|
||||
return when.resolve();
|
||||
it('can add role permissions', function (done) {
|
||||
var testRole = new Models.Role({
|
||||
name: "test2",
|
||||
description: "test2 description"
|
||||
});
|
||||
|
||||
testUtils.insertAuthorUser()
|
||||
.then(function () {
|
||||
return UserProvider.findAll();
|
||||
})
|
||||
.then(function (foundUser) {
|
||||
testUser = foundUser.models[1];
|
||||
testRole.save(null, {user: 1})
|
||||
.then(function () {
|
||||
return testRole.load('permissions');
|
||||
})
|
||||
.then(function () {
|
||||
var rolePermission = new Models.Permission({
|
||||
name: "test edit posts",
|
||||
action_type: 'edit',
|
||||
object_type: 'post'
|
||||
});
|
||||
|
||||
return permissions.canThis(testUser).edit.post(123);
|
||||
})
|
||||
.then(function () {
|
||||
permissableStub.restore();
|
||||
permissableStub.calledWith(123, { user: testUser.id, app: null, internal: false }).should.equal(true);
|
||||
testRole.related('permissions').length.should.equal(0);
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(function () {
|
||||
permissableStub.restore();
|
||||
errors.logError(new Error("Did not allow testUser"));
|
||||
return rolePermission.save(null, {user: 1}).then(function () {
|
||||
return testRole.permissions().attach(rolePermission);
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return Models.Role.findOne({id: testRole.id}, { withRelated: ['permissions']});
|
||||
})
|
||||
.then(function (updatedRole) {
|
||||
should.exist(updatedRole);
|
||||
|
||||
updatedRole.related('permissions').length.should.equal(1);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can use permissable function on Model to forbid something', function (done) {
|
||||
var testUser,
|
||||
permissableStub = sandbox.stub(PostProvider, 'permissable', function () {
|
||||
return when.reject();
|
||||
});
|
||||
describe('With Permissions', function () {
|
||||
|
||||
testUtils.insertAuthorUser()
|
||||
.then(function () {
|
||||
return UserProvider.findAll();
|
||||
})
|
||||
.then(function (foundUser) {
|
||||
testUser = foundUser.models[1];
|
||||
beforeEach(function (done) {
|
||||
sandbox = sinon.sandbox.create();
|
||||
testUtils.initData()
|
||||
.then(testUtils.insertDefaultUser)
|
||||
.then(testUtils.insertDefaultApp)
|
||||
.then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
return permissions.canThis(testUser).edit.post(123);
|
||||
})
|
||||
.then(function () {
|
||||
permissableStub.restore();
|
||||
|
||||
done(new Error("Allowed testUser to edit post"));
|
||||
})
|
||||
.catch(function () {
|
||||
permissableStub.restore();
|
||||
permissableStub.calledWith(123, { user: testUser.id, app: null, internal: false }).should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('does not allow edit post without permission', function (done) {
|
||||
var fakePage = {
|
||||
id: 1
|
||||
};
|
||||
|
||||
it("can get effective user permissions", function (done) {
|
||||
effectivePerms.user(1).then(function (effectivePermissions) {
|
||||
should.exist(effectivePermissions);
|
||||
createTestPermissions()
|
||||
.then(permissions.init)
|
||||
.then(function () {
|
||||
return UserProvider.findOne({id: 1});
|
||||
})
|
||||
.then(function (foundUser) {
|
||||
var canThisResult = permissions.canThis(foundUser);
|
||||
|
||||
effectivePermissions.length.should.be.above(0);
|
||||
should.exist(canThisResult.edit);
|
||||
should.exist(canThisResult.edit.post);
|
||||
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
return canThisResult.edit.page(fakePage);
|
||||
})
|
||||
.then(function () {
|
||||
errors.logError(new Error("Allowed edit post without permission"));
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can check an apps effective permissions', function (done) {
|
||||
effectivePerms.app('Kudos')
|
||||
.then(function (effectivePermissions) {
|
||||
it('allows edit post with permission', function (done) {
|
||||
var fakePost = {
|
||||
id: "1"
|
||||
};
|
||||
|
||||
createTestPermissions()
|
||||
.then(permissions.init)
|
||||
.then(function () {
|
||||
return UserProvider.findOne({id: 1});
|
||||
})
|
||||
.then(function (foundUser) {
|
||||
var newPerm = new Models.Permission({
|
||||
name: "test3 edit post",
|
||||
action_type: "edit",
|
||||
object_type: "post"
|
||||
});
|
||||
|
||||
return newPerm.save(null, {user: 1}).then(function () {
|
||||
return foundUser.permissions().attach(newPerm);
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
return UserProvider.findOne({id: 1}, { withRelated: ['permissions']});
|
||||
})
|
||||
.then(function (updatedUser) {
|
||||
|
||||
// TODO: Verify updatedUser.related('permissions') has the permission?
|
||||
var canThisResult = permissions.canThis(updatedUser.id);
|
||||
|
||||
should.exist(canThisResult.edit);
|
||||
should.exist(canThisResult.edit.post);
|
||||
|
||||
return canThisResult.edit.post(fakePost);
|
||||
})
|
||||
.then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can use permissable function on Model to allow something', function (done) {
|
||||
var testUser,
|
||||
permissableStub = sandbox.stub(PostProvider, 'permissable', function () {
|
||||
return when.resolve();
|
||||
});
|
||||
|
||||
testUtils.insertAuthorUser()
|
||||
.then(function () {
|
||||
return UserProvider.findAll();
|
||||
})
|
||||
.then(function (foundUser) {
|
||||
testUser = foundUser.models[1];
|
||||
|
||||
return permissions.canThis({user: testUser.id}).edit.post(123);
|
||||
})
|
||||
.then(function () {
|
||||
permissableStub.restore();
|
||||
permissableStub.calledWith(123, { user: testUser.id, app: null, internal: false }).should.equal(true);
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(function () {
|
||||
permissableStub.restore();
|
||||
errors.logError(new Error("Did not allow testUser"));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can use permissable function on Model to forbid something', function (done) {
|
||||
var testUser,
|
||||
permissableStub = sandbox.stub(PostProvider, 'permissable', function () {
|
||||
return when.reject();
|
||||
});
|
||||
|
||||
testUtils.insertAuthorUser()
|
||||
.then(function () {
|
||||
return UserProvider.findAll();
|
||||
})
|
||||
.then(function (foundUser) {
|
||||
testUser = foundUser.models[1];
|
||||
|
||||
return permissions.canThis({user: testUser.id}).edit.post(123);
|
||||
})
|
||||
.then(function () {
|
||||
|
||||
permissableStub.restore();
|
||||
done(new Error("Allowed testUser to edit post"));
|
||||
})
|
||||
.catch(function () {
|
||||
permissableStub.calledWith(123, { user: testUser.id, app: null, internal: false }).should.equal(true);
|
||||
permissableStub.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("can get effective user permissions", function (done) {
|
||||
effectivePerms.user(1).then(function (effectivePermissions) {
|
||||
should.exist(effectivePermissions);
|
||||
|
||||
effectivePermissions.length.should.be.above(0);
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('does not allow an app to edit a post without permission', function (done) {
|
||||
// Change the author of the post so the author override doesn't affect the test
|
||||
PostProvider.edit({id: 1, 'author_id': 2})
|
||||
.then(function (updatedPost) {
|
||||
// Add user permissions
|
||||
return UserProvider.findOne({id: 1})
|
||||
.then(function (foundUser) {
|
||||
var newPerm = new Models.Permission({
|
||||
name: "app test edit post",
|
||||
action_type: "edit",
|
||||
object_type: "post"
|
||||
});
|
||||
it('can check an apps effective permissions', function (done) {
|
||||
effectivePerms.app('Kudos')
|
||||
.then(function (effectivePermissions) {
|
||||
should.exist(effectivePermissions);
|
||||
|
||||
return newPerm.save(null, {user: 1}).then(function () {
|
||||
return foundUser.permissions().attach(newPerm).then(function () {
|
||||
return when.all([updatedPost, foundUser]);
|
||||
effectivePermissions.length.should.be.above(0);
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('does not allow an app to edit a post without permission', function (done) {
|
||||
// Change the author of the post so the author override doesn't affect the test
|
||||
PostProvider.edit({'author_id': 2}, {id: 1})
|
||||
.then(function (updatedPost) {
|
||||
// Add user permissions
|
||||
return UserProvider.findOne({id: 1})
|
||||
.then(function (foundUser) {
|
||||
var newPerm = new Models.Permission({
|
||||
name: "app test edit post",
|
||||
action_type: "edit",
|
||||
object_type: "post"
|
||||
});
|
||||
|
||||
return newPerm.save(null, {user: 1}).then(function () {
|
||||
return foundUser.permissions().attach(newPerm).then(function () {
|
||||
return when.all([updatedPost, foundUser]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function (results) {
|
||||
var updatedPost = results[0],
|
||||
updatedUser = results[1];
|
||||
})
|
||||
.then(function (results) {
|
||||
var updatedPost = results[0],
|
||||
updatedUser = results[1];
|
||||
|
||||
return permissions.canThis({ user: updatedUser.id })
|
||||
.edit
|
||||
.post(updatedPost.id)
|
||||
.then(function () {
|
||||
return results;
|
||||
})
|
||||
.catch(function (err) {
|
||||
done(new Error("Did not allow user 1 to edit post 1"));
|
||||
});
|
||||
})
|
||||
.then(function (results) {
|
||||
var updatedPost = results[0],
|
||||
updatedUser = results[1];
|
||||
return permissions.canThis({ user: updatedUser.id })
|
||||
.edit
|
||||
.post(updatedPost.id)
|
||||
.then(function () {
|
||||
return results;
|
||||
})
|
||||
.catch(function (err) {
|
||||
done(new Error("Did not allow user 1 to edit post 1"));
|
||||
});
|
||||
})
|
||||
.then(function (results) {
|
||||
var updatedPost = results[0],
|
||||
updatedUser = results[1];
|
||||
|
||||
// Confirm app cannot edit it.
|
||||
return permissions.canThis({ app: 'Hemingway', user: updatedUser.id })
|
||||
.edit
|
||||
.post(updatedPost.id)
|
||||
.then(function () {
|
||||
done(new Error("Allowed an edit of post 1"));
|
||||
})
|
||||
.catch(function () {
|
||||
done();
|
||||
});
|
||||
}).catch(done);
|
||||
});
|
||||
// Confirm app cannot edit it.
|
||||
return permissions.canThis({ app: 'Hemingway', user: updatedUser.id })
|
||||
.edit
|
||||
.post(updatedPost.id)
|
||||
.then(function () {
|
||||
done(new Error("Allowed an edit of post 1"));
|
||||
})
|
||||
.catch(function () {
|
||||
done();
|
||||
});
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('allows an app to edit a post with permission', function (done) {
|
||||
permissions.canThis({ app: 'Kudos', user: 1 })
|
||||
.edit
|
||||
.post(1)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(function () {
|
||||
done(new Error("Allowed an edit of post 1"));
|
||||
});
|
||||
});
|
||||
it('allows an app to edit a post with permission', function (done) {
|
||||
permissions.canThis({ app: 'Kudos', user: 1 })
|
||||
.edit
|
||||
.post(1)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(function () {
|
||||
done(new Error("Allowed an edit of post 1"));
|
||||
});
|
||||
});
|
||||
|
||||
it('checks for null context passed and rejects', function (done) {
|
||||
permissions.canThis(undefined)
|
||||
.edit
|
||||
.post(1)
|
||||
.then(function () {
|
||||
done(new Error("Should not allow editing post"));
|
||||
})
|
||||
.catch(function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('checks for null context passed and rejects', function (done) {
|
||||
permissions.canThis(undefined)
|
||||
.edit
|
||||
.post(1)
|
||||
.then(function () {
|
||||
done(new Error("Should not allow editing post"));
|
||||
})
|
||||
.catch(function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('allows \'internal\' to be passed for internal requests', function (done) {
|
||||
// Using tag here because post implements the custom permissable interface
|
||||
permissions.canThis('internal')
|
||||
.edit
|
||||
.tag(1)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(function () {
|
||||
done(new Error("Should allow editing post with 'internal'"));
|
||||
});
|
||||
});
|
||||
it('allows \'internal\' to be passed for internal requests', function (done) {
|
||||
// Using tag here because post implements the custom permissable interface
|
||||
permissions.canThis('internal')
|
||||
.edit
|
||||
.tag(1)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(function () {
|
||||
done(new Error("Should allow editing post with 'internal'"));
|
||||
});
|
||||
});
|
||||
|
||||
it('allows { internal: true } to be passed for internal requests', function (done) {
|
||||
// Using tag here because post implements the custom permissable interface
|
||||
permissions.canThis({ internal: true })
|
||||
.edit
|
||||
.tag(1)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(function () {
|
||||
done(new Error("Should allow editing post with { internal: true }"));
|
||||
});
|
||||
it('allows { internal: true } to be passed for internal requests', function (done) {
|
||||
// Using tag here because post implements the custom permissable interface
|
||||
permissions.canThis({ internal: true })
|
||||
.edit
|
||||
.tag(1)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(function () {
|
||||
done(new Error("Should allow editing post with { internal: true }"));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -10,7 +10,7 @@ var url = require('url'),
|
|||
post: ['id', 'uuid', 'title', 'slug', 'markdown', 'html', 'meta_title', 'meta_description',
|
||||
'featured', 'image', 'status', 'language', 'created_at', 'created_by', 'updated_at',
|
||||
'updated_by', 'published_at', 'published_by', 'page', 'author', 'tags', 'fields'],
|
||||
settings: ['settings'],
|
||||
settings: ['settings', 'meta'],
|
||||
setting: ['id', 'uuid', 'key', 'value', 'type', 'created_at', 'created_by', 'updated_at', 'updated_by'],
|
||||
tag: ['id', 'uuid', 'name', 'slug', 'description', 'parent',
|
||||
'meta_title', 'meta_description', 'created_at', 'created_by', 'updated_at', 'updated_by'],
|
||||
|
|
Loading…
Reference in a new issue