mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-04 08:54:36 +03:00
Merge pull request #3337 from morficus/issue-3222
Pagination on the Users Management screen
This commit is contained in:
commit
90880469f6
@ -1,6 +1,6 @@
|
|||||||
import { getRequestErrorMessage } from 'ghost/utils/ajax';
|
import PaginationControllerMixin from 'ghost/mixins/pagination-controller';
|
||||||
|
|
||||||
var PostsController = Ember.ArrayController.extend({
|
var PostsController = Ember.ArrayController.extend(PaginationControllerMixin, {
|
||||||
// this will cause the list to re-sort when any of these properties change on any of the models
|
// this will cause the list to re-sort when any of these properties change on any of the models
|
||||||
sortProperties: ['status', 'published_at', 'updated_at'],
|
sortProperties: ['status', 'published_at', 'updated_at'],
|
||||||
|
|
||||||
@ -66,64 +66,11 @@ var PostsController = Ember.ArrayController.extend({
|
|||||||
return statusResult;
|
return statusResult;
|
||||||
},
|
},
|
||||||
|
|
||||||
// set from PostsRoute
|
|
||||||
paginationSettings: null,
|
|
||||||
|
|
||||||
// holds the next page to load during infinite scroll
|
|
||||||
nextPage: null,
|
|
||||||
|
|
||||||
// indicates whether we're currently loading the next page
|
|
||||||
isLoading: null,
|
|
||||||
|
|
||||||
init: function () {
|
init: function () {
|
||||||
this._super();
|
//let the PaginationControllerMixin know what type of model we will be paginating
|
||||||
|
//this is necesariy because we do not have access to the model inside the Controller::init method
|
||||||
|
this._super({'modelType': 'post'});
|
||||||
|
|
||||||
var metadata = this.store.metadataFor('post');
|
|
||||||
this.set('nextPage', metadata.pagination.next);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes an ajax response, concatenates any error messages, then generates an error notification.
|
|
||||||
* @param {jqXHR} response The jQuery ajax reponse object.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
reportLoadError: function (response) {
|
|
||||||
var message = 'A problem was encountered while loading more posts';
|
|
||||||
|
|
||||||
if (response) {
|
|
||||||
// Get message from response
|
|
||||||
message += ': ' + getRequestErrorMessage(response, true);
|
|
||||||
} else {
|
|
||||||
message += '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.notifications.showError(message);
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
/**
|
|
||||||
* Loads the next paginated page of posts into the ember-data store. Will cause the posts list UI to update.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
loadNextPage: function () {
|
|
||||||
var self = this,
|
|
||||||
store = this.get('store'),
|
|
||||||
nextPage = this.get('nextPage'),
|
|
||||||
paginationSettings = this.get('paginationSettings');
|
|
||||||
|
|
||||||
if (nextPage) {
|
|
||||||
this.set('isLoading', true);
|
|
||||||
this.set('paginationSettings.page', nextPage);
|
|
||||||
store.find('post', paginationSettings).then(function () {
|
|
||||||
var metadata = store.metadataFor('post');
|
|
||||||
|
|
||||||
self.set('nextPage', metadata.pagination.next);
|
|
||||||
self.set('isLoading', false);
|
|
||||||
}, function (response) {
|
|
||||||
self.reportLoadError(response);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
var UsersIndexController = Ember.ArrayController.extend({
|
import PaginationControllerMixin from 'ghost/mixins/pagination-controller';
|
||||||
|
|
||||||
|
var UsersIndexController = Ember.ArrayController.extend(PaginationControllerMixin, {
|
||||||
|
init: function () {
|
||||||
|
//let the PaginationControllerMixin know what type of model we will be paginating
|
||||||
|
//this is necesariy because we do not have access to the model inside the Controller::init method
|
||||||
|
this._super({'modelType': 'user'});
|
||||||
|
},
|
||||||
|
|
||||||
users: Ember.computed.alias('model'),
|
users: Ember.computed.alias('model'),
|
||||||
|
|
||||||
activeUsers: Ember.computed.filterBy('users', 'active', true).property('users'),
|
activeUsers: Ember.computed.filterBy('users', 'active', true).property('users'),
|
||||||
|
76
core/client/mixins/pagination-controller.js
Normal file
76
core/client/mixins/pagination-controller.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { getRequestErrorMessage } from 'ghost/utils/ajax';
|
||||||
|
|
||||||
|
var PaginationControllerMixin = Ember.Mixin.create({
|
||||||
|
|
||||||
|
// set from PaginationRouteMixin
|
||||||
|
paginationSettings: null,
|
||||||
|
|
||||||
|
// holds the next page to load during infinite scroll
|
||||||
|
nextPage: null,
|
||||||
|
|
||||||
|
// indicates whether we're currently loading the next page
|
||||||
|
isLoading: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param options: {
|
||||||
|
* modelType: <String> name of the model that will be paginated
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
init: function (options) {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
var metadata = this.store.metadataFor(options.modelType);
|
||||||
|
this.set('nextPage', metadata.pagination.next);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an ajax response, concatenates any error messages, then generates an error notification.
|
||||||
|
* @param {jqXHR} response The jQuery ajax reponse object.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
reportLoadError: function (response) {
|
||||||
|
var message = 'A problem was encountered while loading more records';
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
// Get message from response
|
||||||
|
message += ': ' + getRequestErrorMessage(response, true);
|
||||||
|
} else {
|
||||||
|
message += '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notifications.showError(message);
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
/**
|
||||||
|
* Loads the next paginated page of posts into the ember-data store. Will cause the posts list UI to update.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
loadNextPage: function () {
|
||||||
|
|
||||||
|
var self = this,
|
||||||
|
store = this.get('store'),
|
||||||
|
recordType = this.get('model').get('type'),
|
||||||
|
nextPage = this.get('nextPage'),
|
||||||
|
paginationSettings = this.get('paginationSettings');
|
||||||
|
|
||||||
|
if (nextPage) {
|
||||||
|
this.set('isLoading', true);
|
||||||
|
this.set('paginationSettings.page', nextPage);
|
||||||
|
store.find(recordType, paginationSettings).then(function () {
|
||||||
|
var metadata = store.metadataFor(recordType);
|
||||||
|
|
||||||
|
self.set('nextPage', metadata.pagination.next);
|
||||||
|
self.set('isLoading', false);
|
||||||
|
}, function (response) {
|
||||||
|
self.reportLoadError(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PaginationControllerMixin;
|
23
core/client/mixins/pagination-route.js
Normal file
23
core/client/mixins/pagination-route.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
var defaultPaginationSettings = {
|
||||||
|
page: 1,
|
||||||
|
limit: 15
|
||||||
|
};
|
||||||
|
|
||||||
|
var PaginationRoute = Ember.Mixin.create({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up pagination details
|
||||||
|
* @param {settings}: object that specifies additional pagination details
|
||||||
|
*/
|
||||||
|
setupPagination: function (settings) {
|
||||||
|
|
||||||
|
settings = settings || {};
|
||||||
|
settings = _.defaults(settings, defaultPaginationSettings);
|
||||||
|
|
||||||
|
this.set('paginationSettings', settings);
|
||||||
|
this.controller.set('paginationSettings', settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PaginationRoute;
|
39
core/client/mixins/pagination-view-infinite-scroll.js
Normal file
39
core/client/mixins/pagination-view-infinite-scroll.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
var PaginationViewInfiniteScrollMixin = Ember.Mixin.create({
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if we are past a scroll point where we need to fetch the next page
|
||||||
|
* @param event The scroll event
|
||||||
|
*/
|
||||||
|
checkScroll: function (event) {
|
||||||
|
var element = event.target,
|
||||||
|
triggerPoint = 100,
|
||||||
|
controller = this.get('controller'),
|
||||||
|
isLoading = controller.get('isLoading');
|
||||||
|
|
||||||
|
// If we haven't passed our threshold or we are already fetching content, exit
|
||||||
|
if (isLoading || (element.scrollTop + element.clientHeight + triggerPoint <= element.scrollHeight)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.send('loadNextPage');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind to the scroll event once the element is in the DOM
|
||||||
|
*/
|
||||||
|
didInsertElement: function () {
|
||||||
|
var el = this.$();
|
||||||
|
|
||||||
|
el.on('scroll', Ember.run.bind(this, this.checkScroll));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unbind from the scroll event when the element is no longer in the DOM
|
||||||
|
*/
|
||||||
|
willDestroyElement: function () {
|
||||||
|
var el = this.$();
|
||||||
|
el.off('scroll');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PaginationViewInfiniteScrollMixin;
|
@ -1,6 +1,7 @@
|
|||||||
import styleBody from 'ghost/mixins/style-body';
|
import styleBody from 'ghost/mixins/style-body';
|
||||||
import ShortcutsRoute from 'ghost/mixins/shortcuts-route';
|
import ShortcutsRoute from 'ghost/mixins/shortcuts-route';
|
||||||
import loadingIndicator from 'ghost/mixins/loading-indicator';
|
import loadingIndicator from 'ghost/mixins/loading-indicator';
|
||||||
|
import PaginationRouteMixin from 'ghost/mixins/pagination-route';
|
||||||
|
|
||||||
var paginationSettings = {
|
var paginationSettings = {
|
||||||
status: 'all',
|
status: 'all',
|
||||||
@ -9,7 +10,7 @@ var paginationSettings = {
|
|||||||
page: 1
|
page: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
var PostsRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, ShortcutsRoute, styleBody, loadingIndicator, {
|
var PostsRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, ShortcutsRoute, styleBody, loadingIndicator, PaginationRouteMixin, {
|
||||||
classNames: ['manage'],
|
classNames: ['manage'],
|
||||||
|
|
||||||
model: function () {
|
model: function () {
|
||||||
@ -22,7 +23,7 @@ var PostsRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, Sh
|
|||||||
|
|
||||||
setupController: function (controller, model) {
|
setupController: function (controller, model) {
|
||||||
this._super(controller, model);
|
this._super(controller, model);
|
||||||
controller.set('paginationSettings', paginationSettings);
|
this.setupPagination(paginationSettings);
|
||||||
},
|
},
|
||||||
|
|
||||||
shortcuts: {
|
shortcuts: {
|
||||||
|
@ -1,7 +1,34 @@
|
|||||||
var UsersIndexRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, {
|
import PaginationRouteMixin from 'ghost/mixins/pagination-route';
|
||||||
|
|
||||||
|
var activeUsersPaginationSettings = {
|
||||||
|
include: 'roles',
|
||||||
|
page: 1,
|
||||||
|
limit: 20
|
||||||
|
};
|
||||||
|
|
||||||
|
var invitedUsersPaginationSettings = {
|
||||||
|
include: 'roles',
|
||||||
|
limit: 'all',
|
||||||
|
status: 'invited'
|
||||||
|
};
|
||||||
|
|
||||||
|
var UsersIndexRoute = Ember.Route.extend(Ember.SimpleAuth.AuthenticatedRouteMixin, PaginationRouteMixin, {
|
||||||
|
setupController: function (controller, model) {
|
||||||
|
this._super(controller, model.active);
|
||||||
|
this.setupPagination(activeUsersPaginationSettings);
|
||||||
|
},
|
||||||
|
|
||||||
model: function () {
|
model: function () {
|
||||||
return this.store.find('user');
|
// using `.filter` allows the template to auto-update when new models are pulled in from the server.
|
||||||
|
// we just need to 'return true' to allow all models by default.
|
||||||
|
return Ember.RSVP.hash({
|
||||||
|
inactive: this.store.filter('user', invitedUsersPaginationSettings, function () {
|
||||||
|
return true;
|
||||||
|
}),
|
||||||
|
active: this.store.filter('user', activeUsersPaginationSettings, function () {
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
</section>
|
</section>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section class="content settings-users">
|
{{#view "settings/users/users-list-view" tagName="section" class="content settings-users" }}
|
||||||
{{#if invitedUsers}}
|
{{#if invitedUsers}}
|
||||||
|
|
||||||
<section class="object-list invited-users">
|
<section class="object-list invited-users">
|
||||||
@ -62,4 +62,4 @@
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</section>
|
{{/view}}
|
||||||
|
@ -1,34 +1,17 @@
|
|||||||
import setScrollClassName from 'ghost/utils/set-scroll-classname';
|
import setScrollClassName from 'ghost/utils/set-scroll-classname';
|
||||||
|
import PaginationViewMixin from 'ghost/mixins/pagination-view-infinite-scroll';
|
||||||
|
|
||||||
var PostsListView = Ember.View.extend({
|
|
||||||
|
var PostsListView = Ember.View.extend(PaginationViewMixin, {
|
||||||
classNames: ['content-list-content'],
|
classNames: ['content-list-content'],
|
||||||
|
|
||||||
checkScroll: function (event) {
|
|
||||||
var element = event.target,
|
|
||||||
triggerPoint = 100,
|
|
||||||
controller = this.get('controller'),
|
|
||||||
isLoading = controller.get('isLoading');
|
|
||||||
|
|
||||||
// If we haven't passed our threshold, exit
|
|
||||||
if (isLoading || (element.scrollTop + element.clientHeight + triggerPoint <= element.scrollHeight)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
controller.send('loadNextPage');
|
|
||||||
},
|
|
||||||
|
|
||||||
didInsertElement: function () {
|
didInsertElement: function () {
|
||||||
|
this._super();
|
||||||
var el = this.$();
|
var el = this.$();
|
||||||
el.on('scroll', Ember.run.bind(this, this.checkScroll));
|
|
||||||
el.on('scroll', Ember.run.bind(el, setScrollClassName, {
|
el.on('scroll', Ember.run.bind(el, setScrollClassName, {
|
||||||
target: el.closest('.content-list'),
|
target: el.closest('.content-list'),
|
||||||
offset: 10
|
offset: 10
|
||||||
}));
|
}));
|
||||||
},
|
|
||||||
|
|
||||||
willDestroyElement: function () {
|
|
||||||
var el = this.$();
|
|
||||||
el.off('scroll');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
8
core/client/views/settings/users/users-list-view.js
Normal file
8
core/client/views/settings/users/users-list-view.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
//import setScrollClassName from 'ghost/utils/set-scroll-classname';
|
||||||
|
import PaginationViewMixin from 'ghost/mixins/pagination-view-infinite-scroll';
|
||||||
|
|
||||||
|
var UsersListView = Ember.View.extend(PaginationViewMixin, {
|
||||||
|
classNames: ['settings-users']
|
||||||
|
});
|
||||||
|
|
||||||
|
export default UsersListView;
|
@ -42,9 +42,7 @@ users = {
|
|||||||
if (options.include) {
|
if (options.include) {
|
||||||
options.include = prepareInclude(options.include);
|
options.include = prepareInclude(options.include);
|
||||||
}
|
}
|
||||||
return dataProvider.User.findAll(options).then(function (result) {
|
return dataProvider.User.findPage(options);
|
||||||
return { users: result.toJSON() };
|
|
||||||
});
|
|
||||||
}, function () {
|
}, function () {
|
||||||
return when.reject(new errors.NoPermissionError('You do not have permission to browse users.'));
|
return when.reject(new errors.NoPermissionError('You do not have permission to browse users.'));
|
||||||
});
|
});
|
||||||
|
@ -109,7 +109,8 @@ User = ghostBookshelf.Model.extend({
|
|||||||
findOne: ['withRelated'],
|
findOne: ['withRelated'],
|
||||||
findAll: ['withRelated'],
|
findAll: ['withRelated'],
|
||||||
setup: ['id'],
|
setup: ['id'],
|
||||||
edit: ['withRelated', 'id']
|
edit: ['withRelated', 'id'],
|
||||||
|
findPage: ['page', 'limit', 'status']
|
||||||
};
|
};
|
||||||
|
|
||||||
if (validOptions[methodName]) {
|
if (validOptions[methodName]) {
|
||||||
@ -131,6 +132,144 @@ User = ghostBookshelf.Model.extend({
|
|||||||
return ghostBookshelf.Model.findAll.call(this, options);
|
return ghostBookshelf.Model.findAll.call(this, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #### 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:**
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* users: [
|
||||||
|
* {...}, {...}, {...}
|
||||||
|
* ],
|
||||||
|
* meta: {
|
||||||
|
* page: __,
|
||||||
|
* limit: __,
|
||||||
|
* pages: __,
|
||||||
|
* total: __
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @params {Object} options
|
||||||
|
*/
|
||||||
|
findPage: function (options) {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
var userCollection = Users.forge(),
|
||||||
|
userQuery;
|
||||||
|
|
||||||
|
if (options.limit && options.limit !== 'all') {
|
||||||
|
options.limit = parseInt(options.limit) || 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
options = this.filterOptions(options, 'findPage');
|
||||||
|
|
||||||
|
// Set default settings for options
|
||||||
|
options = _.extend({
|
||||||
|
page: 1, // pagination page
|
||||||
|
limit: 15,
|
||||||
|
status: 'active',
|
||||||
|
where: {}
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
//TODO: there are multiple statuses that make a user "active" or "invited" - we a way to translate/map them:
|
||||||
|
//TODO (cont'd from above): * valid "active" statuses: active, warn-1, warn-2, warn-3, warn-4, locked
|
||||||
|
//TODO (cont'd from above): * valid "invited" statuses" invited, invited-pending
|
||||||
|
|
||||||
|
// the status provided.
|
||||||
|
if (options.status) {
|
||||||
|
// make sure that status is valid
|
||||||
|
//TODO: need a better way of getting a list of statuses other than hard-coding them...
|
||||||
|
options.status = _.indexOf(['active', 'warn-1', 'warn-2', 'warn-3', 'locked', 'invited'], options.status) !== -1 ? options.status : 'active';
|
||||||
|
options.where.status = options.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are where conditionals specified, add those
|
||||||
|
// to the query.
|
||||||
|
if (options.where) {
|
||||||
|
userCollection.query('where', options.where);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add related objects
|
||||||
|
options.withRelated = _.union([ 'roles' ], options.include);
|
||||||
|
|
||||||
|
//only include a limit-query if a numeric limit is provided
|
||||||
|
if (_.isNumber(options.limit)) {
|
||||||
|
userCollection
|
||||||
|
.query('limit', options.limit)
|
||||||
|
.query('offset', options.limit * (options.page - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
userQuery = userCollection
|
||||||
|
.query('orderBy', 'last_login', 'DESC')
|
||||||
|
.query('orderBy', 'name', 'ASC')
|
||||||
|
.query('orderBy', 'created_at', 'DESC')
|
||||||
|
.fetch(_.omit(options, 'page', 'limit'));
|
||||||
|
|
||||||
|
|
||||||
|
return when(userQuery)
|
||||||
|
|
||||||
|
// Fetch pagination information
|
||||||
|
.then(function () {
|
||||||
|
var qb,
|
||||||
|
tableName = _.result(userCollection, 'tableName'),
|
||||||
|
idAttribute = _.result(userCollection, 'idAttribute');
|
||||||
|
|
||||||
|
// After we're done, we need to figure out what
|
||||||
|
// the limits are for the pagination values.
|
||||||
|
qb = ghostBookshelf.knex(tableName);
|
||||||
|
|
||||||
|
if (options.where) {
|
||||||
|
qb.where(options.where);
|
||||||
|
}
|
||||||
|
|
||||||
|
return qb.count(tableName + '.' + idAttribute + ' as aggregate');
|
||||||
|
})
|
||||||
|
|
||||||
|
// Format response of data
|
||||||
|
.then(function (resp) {
|
||||||
|
var totalUsers = parseInt(resp[0].aggregate, 10),
|
||||||
|
calcPages = Math.ceil(totalUsers / options.limit),
|
||||||
|
pagination = {},
|
||||||
|
meta = {},
|
||||||
|
data = {};
|
||||||
|
|
||||||
|
pagination.page = parseInt(options.page, 10);
|
||||||
|
pagination.limit = options.limit;
|
||||||
|
pagination.pages = calcPages === 0 ? 1 : calcPages;
|
||||||
|
pagination.total = totalUsers;
|
||||||
|
pagination.next = null;
|
||||||
|
pagination.prev = null;
|
||||||
|
|
||||||
|
// Pass include to each model so that toJSON works correctly
|
||||||
|
if (options.include) {
|
||||||
|
_.each(userCollection.models, function (item) {
|
||||||
|
item.include = options.include;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
data.users = userCollection.toJSON();
|
||||||
|
data.meta = meta;
|
||||||
|
meta.pagination = pagination;
|
||||||
|
|
||||||
|
if (pagination.pages > 1) {
|
||||||
|
if (pagination.page === 1) {
|
||||||
|
pagination.next = pagination.page + 1;
|
||||||
|
} else if (pagination.page === pagination.pages) {
|
||||||
|
pagination.prev = pagination.page - 1;
|
||||||
|
} else {
|
||||||
|
pagination.next = pagination.page + 1;
|
||||||
|
pagination.prev = pagination.page - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
.catch(errors.logAndThrowError);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ### Find One
|
* ### Find One
|
||||||
* @extends ghostBookshelf.Model.findOne to include roles
|
* @extends ghostBookshelf.Model.findOne to include roles
|
||||||
|
@ -117,7 +117,7 @@ describe('User API', function () {
|
|||||||
should.not.exist(res.headers['x-cache-invalidate']);
|
should.not.exist(res.headers['x-cache-invalidate']);
|
||||||
var jsonResponse = res.body;
|
var jsonResponse = res.body;
|
||||||
jsonResponse.users.should.exist;
|
jsonResponse.users.should.exist;
|
||||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
should.not.exist(jsonResponse.meta);
|
||||||
|
|
||||||
jsonResponse.users.should.have.length(1);
|
jsonResponse.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
||||||
@ -138,7 +138,7 @@ describe('User API', function () {
|
|||||||
should.not.exist(res.headers['x-cache-invalidate']);
|
should.not.exist(res.headers['x-cache-invalidate']);
|
||||||
var jsonResponse = res.body;
|
var jsonResponse = res.body;
|
||||||
jsonResponse.users.should.exist;
|
jsonResponse.users.should.exist;
|
||||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
should.not.exist(jsonResponse.meta);
|
||||||
|
|
||||||
jsonResponse.users.should.have.length(1);
|
jsonResponse.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
||||||
@ -159,7 +159,7 @@ describe('User API', function () {
|
|||||||
should.not.exist(res.headers['x-cache-invalidate']);
|
should.not.exist(res.headers['x-cache-invalidate']);
|
||||||
var jsonResponse = res.body;
|
var jsonResponse = res.body;
|
||||||
jsonResponse.users.should.exist;
|
jsonResponse.users.should.exist;
|
||||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
should.not.exist(jsonResponse.meta);
|
||||||
|
|
||||||
jsonResponse.users.should.have.length(1);
|
jsonResponse.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
||||||
@ -180,7 +180,7 @@ describe('User API', function () {
|
|||||||
should.not.exist(res.headers['x-cache-invalidate']);
|
should.not.exist(res.headers['x-cache-invalidate']);
|
||||||
var jsonResponse = res.body;
|
var jsonResponse = res.body;
|
||||||
jsonResponse.users.should.exist;
|
jsonResponse.users.should.exist;
|
||||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
should.not.exist(jsonResponse.meta);
|
||||||
|
|
||||||
jsonResponse.users.should.have.length(1);
|
jsonResponse.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
||||||
@ -201,7 +201,7 @@ describe('User API', function () {
|
|||||||
should.not.exist(res.headers['x-cache-invalidate']);
|
should.not.exist(res.headers['x-cache-invalidate']);
|
||||||
var jsonResponse = res.body;
|
var jsonResponse = res.body;
|
||||||
jsonResponse.users.should.exist;
|
jsonResponse.users.should.exist;
|
||||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
should.not.exist(jsonResponse.meta);
|
||||||
|
|
||||||
jsonResponse.users.should.have.length(1);
|
jsonResponse.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
||||||
@ -223,7 +223,7 @@ describe('User API', function () {
|
|||||||
should.not.exist(res.headers['x-cache-invalidate']);
|
should.not.exist(res.headers['x-cache-invalidate']);
|
||||||
var jsonResponse = res.body;
|
var jsonResponse = res.body;
|
||||||
jsonResponse.users.should.exist;
|
jsonResponse.users.should.exist;
|
||||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
should.not.exist(jsonResponse.meta);
|
||||||
|
|
||||||
jsonResponse.users.should.have.length(1);
|
jsonResponse.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
||||||
@ -247,7 +247,7 @@ describe('User API', function () {
|
|||||||
should.not.exist(res.headers['x-cache-invalidate']);
|
should.not.exist(res.headers['x-cache-invalidate']);
|
||||||
var jsonResponse = res.body;
|
var jsonResponse = res.body;
|
||||||
jsonResponse.users.should.exist;
|
jsonResponse.users.should.exist;
|
||||||
testUtils.API.checkResponse(jsonResponse, 'users');
|
should.not.exist(jsonResponse.meta);
|
||||||
|
|
||||||
jsonResponse.users.should.have.length(1);
|
jsonResponse.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(jsonResponse.users[0], 'user', ['roles']);
|
||||||
@ -366,4 +366,4 @@ describe('User API', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -58,8 +58,8 @@ describe('Authentication API', function () {
|
|||||||
AuthAPI.setup({ setup: [setupData] }).then(function (result) {
|
AuthAPI.setup({ setup: [setupData] }).then(function (result) {
|
||||||
should.exist(result);
|
should.exist(result);
|
||||||
should.exist(result.users);
|
should.exist(result.users);
|
||||||
|
should.not.exist(result.meta);
|
||||||
result.users.should.have.length(1);
|
result.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(result, 'users');
|
|
||||||
testUtils.API.checkResponse(result.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(result.users[0], 'user', ['roles']);
|
||||||
|
|
||||||
var newUser = result.users[0];
|
var newUser = result.users[0];
|
||||||
|
@ -1,19 +1,53 @@
|
|||||||
/*globals describe, before, beforeEach, afterEach, it */
|
/*globals describe, before, beforeEach, afterEach, it */
|
||||||
/*jshint expr:true*/
|
var testUtils = require('../../utils'),
|
||||||
var testUtils = require('../../utils'),
|
should = require('should'),
|
||||||
should = require('should'),
|
|
||||||
|
permissions = require('../../../server/permissions'),
|
||||||
|
UserModel = require('../../../server/models').User;
|
||||||
|
|
||||||
// Stuff we are testing
|
// Stuff we are testing
|
||||||
permissions = require('../../../server/permissions'),
|
UsersAPI = require('../../../server/api/users');
|
||||||
UserModel = require('../../../server/models').User,
|
AuthAPI = require('../../../server/api/authentication');
|
||||||
UsersAPI = require('../../../server/api/users');
|
|
||||||
|
|
||||||
describe('Users API', function () {
|
describe('Users API', function () {
|
||||||
// Keep the DB clean
|
|
||||||
before(testUtils.teardown);
|
|
||||||
afterEach(testUtils.teardown);
|
|
||||||
|
|
||||||
should.exist(UsersAPI);
|
before(function (done) {
|
||||||
|
testUtils.clearData().then(function () {
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function (done) {
|
||||||
|
testUtils.clearData().then(function () {
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
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) {
|
||||||
|
AuthAPI.setup({ setup: [{
|
||||||
|
'name': 'Hello World',
|
||||||
|
'email': 'hello@world.com',
|
||||||
|
'password': 'password'
|
||||||
|
}]}).then(function (results) {
|
||||||
|
should.exist(results);
|
||||||
|
should.not.exist(results.meta);
|
||||||
|
should.exist(results.users);
|
||||||
|
results.users.should.have.length(1);
|
||||||
|
testUtils.API.checkResponse(results.users[0], 'user', ['roles']);
|
||||||
|
results.users[0].name.should.equal('Hello World');
|
||||||
|
done();
|
||||||
|
}).catch(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('With Users', function () {
|
describe('With Users', function () {
|
||||||
beforeEach(function (done) {
|
beforeEach(function (done) {
|
||||||
@ -95,7 +129,8 @@ describe('Users API', function () {
|
|||||||
it('admin can read', function (done) {
|
it('admin can read', function (done) {
|
||||||
UsersAPI.read({id: 1, context: {user: 1}}).then(function (results) {
|
UsersAPI.read({id: 1, context: {user: 1}}).then(function (results) {
|
||||||
should.exist(results);
|
should.exist(results);
|
||||||
testUtils.API.checkResponse(results, 'users');
|
should.not.exist(results.meta);
|
||||||
|
should.exist(results.users);
|
||||||
results.users[0].id.should.eql(1);
|
results.users[0].id.should.eql(1);
|
||||||
testUtils.API.checkResponse(results.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(results.users[0], 'user', ['roles']);
|
||||||
|
|
||||||
@ -108,7 +143,8 @@ describe('Users API', function () {
|
|||||||
it('editor can read', function (done) {
|
it('editor can read', function (done) {
|
||||||
UsersAPI.read({id: 1, context: {user: 2}}).then(function (results) {
|
UsersAPI.read({id: 1, context: {user: 2}}).then(function (results) {
|
||||||
should.exist(results);
|
should.exist(results);
|
||||||
testUtils.API.checkResponse(results, 'users');
|
should.not.exist(results.meta);
|
||||||
|
should.exist(results.users);
|
||||||
results.users[0].id.should.eql(1);
|
results.users[0].id.should.eql(1);
|
||||||
testUtils.API.checkResponse(results.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(results.users[0], 'user', ['roles']);
|
||||||
done();
|
done();
|
||||||
@ -118,7 +154,8 @@ describe('Users API', function () {
|
|||||||
it('author can read', function (done) {
|
it('author can read', function (done) {
|
||||||
UsersAPI.read({id: 1, context: {user: 3}}).then(function (results) {
|
UsersAPI.read({id: 1, context: {user: 3}}).then(function (results) {
|
||||||
should.exist(results);
|
should.exist(results);
|
||||||
testUtils.API.checkResponse(results, 'users');
|
should.not.exist(results.meta);
|
||||||
|
should.exist(results.users);
|
||||||
results.users[0].id.should.eql(1);
|
results.users[0].id.should.eql(1);
|
||||||
testUtils.API.checkResponse(results.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(results.users[0], 'user', ['roles']);
|
||||||
done();
|
done();
|
||||||
@ -128,7 +165,8 @@ describe('Users API', function () {
|
|||||||
it('no-auth can read', function (done) {
|
it('no-auth can read', function (done) {
|
||||||
UsersAPI.read({id: 1}).then(function (results) {
|
UsersAPI.read({id: 1}).then(function (results) {
|
||||||
should.exist(results);
|
should.exist(results);
|
||||||
testUtils.API.checkResponse(results, 'users');
|
should.not.exist(results.meta);
|
||||||
|
should.exist(results.users);
|
||||||
results.users[0].id.should.eql(1);
|
results.users[0].id.should.eql(1);
|
||||||
testUtils.API.checkResponse(results.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(results.users[0], 'user', ['roles']);
|
||||||
done();
|
done();
|
||||||
@ -138,7 +176,8 @@ describe('Users API', function () {
|
|||||||
it('admin can edit', function (done) {
|
it('admin can edit', function (done) {
|
||||||
UsersAPI.edit({users: [{name: 'Joe Blogger'}]}, {id: 1, context: {user: 1}}).then(function (response) {
|
UsersAPI.edit({users: [{name: 'Joe Blogger'}]}, {id: 1, context: {user: 1}}).then(function (response) {
|
||||||
should.exist(response);
|
should.exist(response);
|
||||||
testUtils.API.checkResponse(response, 'users');
|
should.not.exist(response.meta);
|
||||||
|
should.exist(response.users);
|
||||||
response.users.should.have.length(1);
|
response.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(response.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(response.users[0], 'user', ['roles']);
|
||||||
response.users[0].name.should.equal('Joe Blogger');
|
response.users[0].name.should.equal('Joe Blogger');
|
||||||
@ -150,7 +189,8 @@ describe('Users API', function () {
|
|||||||
it('editor can edit', function (done) {
|
it('editor can edit', function (done) {
|
||||||
UsersAPI.edit({users: [{name: 'Joe Blogger'}]}, {id: 1, context: {user: 2}}).then(function (response) {
|
UsersAPI.edit({users: [{name: 'Joe Blogger'}]}, {id: 1, context: {user: 2}}).then(function (response) {
|
||||||
should.exist(response);
|
should.exist(response);
|
||||||
testUtils.API.checkResponse(response, 'users');
|
should.not.exist(response.meta);
|
||||||
|
should.exist(response.users);
|
||||||
response.users.should.have.length(1);
|
response.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(response.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(response.users[0], 'user', ['roles']);
|
||||||
response.users[0].name.should.eql('Joe Blogger');
|
response.users[0].name.should.eql('Joe Blogger');
|
||||||
@ -170,7 +210,8 @@ describe('Users API', function () {
|
|||||||
return UsersAPI.edit({users: [{name: 'Timothy Bogendath'}]}, {id: 3, context: {user: 3}})
|
return UsersAPI.edit({users: [{name: 'Timothy Bogendath'}]}, {id: 3, context: {user: 3}})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
should.exist(response);
|
should.exist(response);
|
||||||
testUtils.API.checkResponse(response, 'users');
|
should.not.exist(response.meta);
|
||||||
|
should.exist(response.users);
|
||||||
response.users.should.have.length(1);
|
response.users.should.have.length(1);
|
||||||
testUtils.API.checkResponse(response.users[0], 'user', ['roles']);
|
testUtils.API.checkResponse(response.users[0], 'user', ['roles']);
|
||||||
response.users[0].name.should.eql('Timothy Bogendath');
|
response.users[0].name.should.eql('Timothy Bogendath');
|
||||||
@ -179,4 +220,4 @@ describe('Users API', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@ var url = require('url'),
|
|||||||
schema = 'http://',
|
schema = 'http://',
|
||||||
expectedProperties = {
|
expectedProperties = {
|
||||||
posts: ['posts', 'meta'],
|
posts: ['posts', 'meta'],
|
||||||
users: ['users'],
|
users: ['users', 'meta'],
|
||||||
pagination: ['page', 'limit', 'pages', 'total', 'next', 'prev'],
|
pagination: ['page', 'limit', 'pages', 'total', 'next', 'prev'],
|
||||||
post: ['id', 'uuid', 'title', 'slug', 'markdown', 'html', 'meta_title', 'meta_description',
|
post: ['id', 'uuid', 'title', 'slug', 'markdown', 'html', 'meta_title', 'meta_description',
|
||||||
'featured', 'image', 'status', 'language', 'created_at', 'created_by', 'updated_at',
|
'featured', 'image', 'status', 'language', 'created_at', 'created_by', 'updated_at',
|
||||||
|
Loading…
Reference in New Issue
Block a user