diff --git a/client/coral-admin/src/actions/comments.js b/client/coral-admin/src/actions/comments.js
index e8276a9da..f37c79b4f 100644
--- a/client/coral-admin/src/actions/comments.js
+++ b/client/coral-admin/src/actions/comments.js
@@ -2,14 +2,21 @@ import coralApi from '../../../coral-framework/helpers/response';
import * as commentTypes from '../constants/comments';
import * as actionTypes from '../constants/actions';
+function addUsersCommentsActions (dispatch, {comments, users, actions}) {
+ dispatch({type: commentTypes.USERS_MODERATION_QUEUE_FETCH_SUCCESS, users});
+ dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS, comments});
+ dispatch({type: actionTypes.ACTIONS_MODERATION_QUEUE_FETCH_SUCCESS, actions});
+}
+
// Get comments to fill each of the three lists on the mod queue
export const fetchModerationQueueComments = () => {
return dispatch => {
dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST});
+
return Promise.all([
coralApi('/queue/comments/pending'),
- coralApi('/comments?status=rejected'),
- coralApi('/comments?action_type=flag')
+ coralApi('/queue/comments/rejected'),
+ coralApi('/queue/comments/flagged')
])
.then(([pending, rejected, flagged]) => {
@@ -21,14 +28,38 @@ export const fetchModerationQueueComments = () => {
actions: [...pending.actions, ...rejected.actions, ...flagged.actions]
};
})
- .then(({comments, users, actions}) => {
+ .then(addUsersCommentsActions.bind(this, dispatch));
+ };
+};
- /* Post comments and users to redux store. Actions will be posted when they are needed. */
- dispatch({type: commentTypes.USERS_MODERATION_QUEUE_FETCH_SUCCESS, users});
- dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_SUCCESS, comments});
- dispatch({type: actionTypes.ACTIONS_MODERATION_QUEUE_FETCH_SUCCESS, actions});
+export const fetchPendingQueue = () => {
+ return dispatch => {
+ dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST});
- });
+ return coralApi('/queue/comments/pending')
+ .then(addUsersCommentsActions.bind(this, dispatch));
+ };
+};
+
+export const fetchRejectedQueue = () => {
+ return dispatch => {
+ dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST});
+
+ return coralApi('/queue/comments/rejected')
+ .then(addUsersCommentsActions.bind(this, dispatch));
+ };
+};
+
+export const fetchFlaggedQueue = () => {
+ return dispatch => {
+ dispatch({type: commentTypes.COMMENTS_MODERATION_QUEUE_FETCH_REQUEST});
+
+ return coralApi('/queue/comments/flagged')
+ .then(comments => {
+ comments.forEach(comment => comment.flagged = true);
+ return comments;
+ })
+ .then(addUsersCommentsActions.bind(this, dispatch));
};
};
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
index 8a684184a..d2c888e9e 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
@@ -6,7 +6,10 @@ import {
updateStatus,
showBanUserDialog,
hideBanUserDialog,
- fetchModerationQueueComments
+ fetchPendingQueue,
+ fetchRejectedQueue,
+ fetchFlaggedQueue,
+ fetchModerationQueueComments,
} from 'actions/comments';
import {userStatusUpdate} from 'actions/users';
import {fetchSettings} from 'actions/settings';
@@ -54,6 +57,16 @@ class ModerationContainer extends React.Component {
onTabClick(activeTab) {
this.setState({activeTab});
+
+ if (activeTab === 'pending') {
+ this.props.fetchPendingQueue();
+ } else if (activeTab === 'rejected') {
+ this.props.fetchRejectedQueue();
+ } else if (activeTab === 'flagged') {
+ this.props.fetchFlaggedQueue();
+ } else {
+ this.props.fetchModerationQueueComments();
+ }
}
onClose() {
@@ -90,6 +103,9 @@ const mapDispatchToProps = dispatch => {
return {
fetchSettings: () => dispatch(fetchSettings()),
fetchModerationQueueComments: () => dispatch(fetchModerationQueueComments()),
+ fetchPendingQueue: () => dispatch(fetchPendingQueue()),
+ fetchRejectedQueue: () => dispatch(fetchRejectedQueue()),
+ fetchFlaggedQueue: () => dispatch(fetchFlaggedQueue()),
showBanUserDialog: (userId, userName, commentId) => dispatch(showBanUserDialog(userId, userName, commentId)),
hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
banUser: (userId, commentId) => dispatch(userStatusUpdate('banned', userId, commentId)).then(() => {
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
index 55a2e705e..d46a9b35d 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
@@ -56,7 +56,7 @@ export default ({onTabClick, ...props}) => (
0) {
- return this.status_history[this.status_history.length - 1].type;
- }
-
- return null;
-});
-
/**
* Creates a new Comment that came from a public source.
* @param {Mixed} comment either a single comment or an array of comments.
@@ -118,7 +101,7 @@ CommentSchema.statics.publicCreate = (comment) => {
body,
asset_id,
parent_id,
- status = false,
+ status = null,
author_id
} = comment;
@@ -130,6 +113,7 @@ CommentSchema.statics.publicCreate = (comment) => {
type: status,
created_at: new Date()
}] : [],
+ status,
author_id
});
@@ -160,7 +144,7 @@ CommentSchema.statics.findByAssetId = (asset_id) => Comment.find({
*/
CommentSchema.statics.findAcceptedByAssetId = (asset_id) => Comment.find({
asset_id,
- 'status_history.type': 'accepted'
+ status: 'accepted'
});
/**
@@ -171,14 +155,8 @@ CommentSchema.statics.findAcceptedByAssetId = (asset_id) => Comment.find({
CommentSchema.statics.findAcceptedAndNewByAssetId = (asset_id) => Comment.find({
asset_id,
$or: [
- {
- 'status_history.type': 'accepted'
- },
- {
- status_history: {
- $size: 0
- }
- }
+ {status: 'accepted'},
+ {status: null}
]
});
@@ -205,28 +183,20 @@ CommentSchema.statics.findIdsByActionType = (action_type) => Action
.then((actions) => actions.map(a => a.item_id));
/**
- * Find comments by their status_history.
- * @param {String} status the status of the comment to search for
- * @return {Promise}
+ * Find comments by current status
+ * @param {String} status status of the comment to search for
+ * @return {Promise} resovles to comment array
*/
-CommentSchema.statics.findByStatus = (status = false) => {
- let q = {};
-
- if (status) {
- q['status_history.type'] = status;
- } else {
- q.status_history = {$size: 0};
- }
-
- return Comment.find(q);
+CommentSchema.statics.findByStatus = (status = null) => {
+ return Comment.find({status});
};
/**
* Find comments that need to be moderated (aka moderation queue).
- * @param {String} moderationValue pre or post moderation setting. If it is undefined then look at the settings.
+ * @param {String} asset_id
* @return {Promise}
*/
-CommentSchema.statics.moderationQueue = (moderation, asset_id = false) => {
+CommentSchema.statics.moderationQueue = (status, asset_id = null) => {
/**
* This adds the asset_id requirement to the query if the asset_id is defined.
@@ -239,25 +209,8 @@ CommentSchema.statics.moderationQueue = (moderation, asset_id = false) => {
return query;
};
- // Decide on whether or not we need to load extended options for the
- // moderation based on the moderation options.
- let comments;
-
- if (moderation === 'pre') {
-
- // Pre-moderation: New comments are shown in the moderator queues immediately.
- comments = assetIDWrap(CommentSchema.statics.findByStatus('premod'));
-
- } else {
-
- // Post-moderation: New comments do not appear in moderation queues unless they are flagged by other users.
- comments = CommentSchema.statics.findIdsByActionType('flag')
- .then((ids) => assetIDWrap(Comment.find({
- id: {
- $in: ids
- }
- })));
- }
+ // Pre-moderation: New comments are shown in the moderator queues immediately.
+ let comments = assetIDWrap(Comment.findByStatus(status));
return comments;
};
@@ -277,7 +230,8 @@ CommentSchema.statics.pushStatus = (id, status, assigned_by = null) => Comment.u
created_at: new Date(),
assigned_by
}
- }
+ },
+ $set: {status}
});
/**
diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js
index 226f772be..8bf3616eb 100644
--- a/routes/api/comments/index.js
+++ b/routes/api/comments/index.js
@@ -57,7 +57,7 @@ router.get('/', (req, res, next) => {
.then((ids) => assetIDWrap(Comment.find({
id: {
$in: ids
- },
+ }
})));
} else {
query = assetIDWrap(Comment.all());
@@ -124,7 +124,7 @@ router.post('/', wordlist.filter('body'), (req, res, next) => {
if (charCountEnable && body.length > charCount) {
return 'rejected';
}
- return moderation === 'pre' ? 'premod' : '';
+ return moderation === 'pre' ? 'premod' : null;
});
}
diff --git a/routes/api/queue/index.js b/routes/api/queue/index.js
index 9c85fbe94..34902557c 100644
--- a/routes/api/queue/index.js
+++ b/routes/api/queue/index.js
@@ -2,12 +2,22 @@ const express = require('express');
const Comment = require('../../../models/comment');
const User = require('../../../models/user');
const Action = require('../../../models/action');
-const Setting = require('../../../models/setting');
-const Asset = require('../../../models/asset');
+const authorization = require('../../../middleware/authorization');
const _ = require('lodash');
const router = express.Router();
+function gatherActionsAndUsers (comments) {
+ return Promise.all([
+ comments,
+ User.findByIdArray(_.uniq(comments.map((comment) => comment.author_id))),
+ Action.getActionSummaries(_.uniq([
+ ...comments.map((comment) => comment.id),
+ ...comments.map((comment) => comment.author_id)
+ ]))
+ ]);
+}
+
//==============================================================================
// Get Routes
//==============================================================================
@@ -16,49 +26,51 @@ const router = express.Router();
// depending on the settings. The :moderation overwrites this settings.
// Pre-moderation: New comments are shown in the moderator queues immediately.
// Post-moderation: New comments do not appear in moderation queues unless they are flagged by other users.
-router.get('/comments/pending', (req, res, next) => {
+router.get('/comments/pending', authorization.needed('admin'), (req, res, next) => {
- const {
- asset_id
- } = req.query;
+ const {asset_id} = req.query;
- let settings = Setting.retrieve();
-
- if (asset_id) {
-
- // In the event that we have an asset_id, we should fetch the asset settings
- // in order to actually determine if there is additional comments to parse.
- settings = Promise.all([
- settings,
- Asset.findById(asset_id).select('settings')
- ]).then(([{moderation}, asset]) => {
- if (asset.settings && asset.settings.moderation) {
- return {moderation: asset.settings.moderation};
- }
-
- return {moderation};
- });
- }
-
- settings
- .then(({moderation}) => {
- return Comment.moderationQueue(moderation);
- }).then((comments) => {
- return Promise.all([
- comments,
- User.findByIdArray(_.uniq(comments.map((comment) => comment.author_id))),
- Action.getActionSummaries(_.uniq([
- ...comments.map((comment) => comment.id),
- ...comments.map((comment) => comment.author_id)
- ]))
- ]);
- })
+ Comment.moderationQueue('premod', asset_id)
+ .then(gatherActionsAndUsers)
.then(([comments, users, actions]) => {
- res.json({
- comments,
- users,
- actions
- });
+ res.json({comments, users, actions});
+ })
+ .catch(error => {
+ next(error);
+ });
+});
+
+router.get('/comments/rejected', authorization.needed('admin'), (req, res, next) => {
+ const {asset_id} = req.query;
+
+ Comment.moderationQueue('rejected', asset_id)
+ .then(gatherActionsAndUsers)
+ .then(([comments, users, actions]) => {
+ res.json({comments, users, actions});
+ })
+ .catch(error => {
+ next(error);
+ });
+});
+
+router.get('/comments/flagged', authorization.needed('admin'), (req, res, next) => {
+ const {asset_id} = req.query;
+
+ const assetIDWrap = (query) => {
+ if (asset_id) {
+ query = query.where('asset_id', asset_id);
+ }
+
+ return query;
+ };
+
+ Comment.findIdsByActionType('flag')
+ .then(ids => assetIDWrap(Comment.find({
+ id: {$in: ids}
+ })))
+ .then(gatherActionsAndUsers)
+ .then(([comments, users, actions]) => {
+ res.json({comments, users, actions});
})
.catch(error => {
next(error);
diff --git a/tests/models/comment.js b/tests/models/comment.js
index 91f51ccb8..20590dc3d 100644
--- a/tests/models/comment.js
+++ b/tests/models/comment.js
@@ -21,6 +21,7 @@ describe('models.Comment', () => {
status_history: [{
type: 'accepted'
}],
+ status: 'accepted',
parent_id: '',
author_id: '123',
id: '2'
@@ -37,6 +38,7 @@ describe('models.Comment', () => {
status_history: [{
type: 'rejected'
}],
+ status: 'rejected',
parent_id: '',
author_id: '456',
id: '4'
@@ -46,6 +48,7 @@ describe('models.Comment', () => {
status_history: [{
type: 'premod'
}],
+ status: 'premod',
parent_id: '',
author_id: '456',
id: '5'
@@ -55,6 +58,7 @@ describe('models.Comment', () => {
status_history: [{
type: 'premod'
}],
+ status: 'premod',
parent_id: '',
author_id: '456',
id: '6'
@@ -184,20 +188,12 @@ describe('models.Comment', () => {
describe('#moderationQueue()', () => {
it('should find an array of new comments to moderate when pre-moderation', () => {
- return Comment.moderationQueue('pre').then((result) => {
+ return Comment.moderationQueue('premod').then((result) => {
expect(result).to.not.be.null;
expect(result).to.have.lengthOf(2);
});
});
- it('should find an array of new comments to moderate when post-moderation', () => {
- return Comment.moderationQueue('post').then((result) => {
- expect(result).to.not.be.null;
- expect(result).to.have.lengthOf(1);
- expect(result[0]).to.have.property('body', 'comment 30');
- });
- });
-
});
describe('#removeAction', () => {
@@ -227,6 +223,7 @@ describe('models.Comment', () => {
.then(() => Comment.findById(comment_id))
.then((c) => {
expect(c).to.have.property('status');
+ expect(c.status).to.equal('rejected');
expect(c.status_history).to.have.length(1);
expect(c.status_history[0]).to.have.property('type', 'rejected');
expect(c.status_history[0]).to.have.property('assigned_by', '123');
@@ -238,6 +235,8 @@ describe('models.Comment', () => {
.then(() => Comment.findById(comments[1].id))
.then((c) => {
expect(c).to.have.property('status_history');
+ expect(c).to.have.property('status');
+ expect(c.status).to.equal('rejected');
expect(c.status_history).to.have.length(2);
expect(c.status_history[0]).to.have.property('type', 'accepted');
expect(c.status_history[0]).to.have.property('assigned_by', null);
diff --git a/tests/routes/api/comments/index.js b/tests/routes/api/comments/index.js
index 246da24a7..5edb52099 100644
--- a/tests/routes/api/comments/index.js
+++ b/tests/routes/api/comments/index.js
@@ -34,12 +34,14 @@ describe('/api/v1/comments', () => {
body: 'comment 20',
asset_id: 'asset',
author_id: '456',
+ status: 'rejected',
status_history: [{
type: 'rejected'
}]
}, {
body: 'comment 30',
asset_id: '456',
+ status: 'accepted',
status_history: [{
type: 'accepted'
}]
diff --git a/tests/routes/api/queue/index.js b/tests/routes/api/queue/index.js
index 378899967..6a7507618 100644
--- a/tests/routes/api/queue/index.js
+++ b/tests/routes/api/queue/index.js
@@ -21,6 +21,7 @@ describe('/api/v1/queue', () => {
body: 'comment 10',
asset_id: 'asset',
author_id: '123',
+ status: 'rejected',
status_history: [{
type: 'rejected'
}]
@@ -29,6 +30,7 @@ describe('/api/v1/queue', () => {
body: 'comment 20',
asset_id: 'asset',
author_id: '456',
+ status: 'premod',
status_history: [{
type: 'premod'
}]
@@ -36,6 +38,7 @@ describe('/api/v1/queue', () => {
id: 'hij',
body: 'comment 30',
asset_id: '456',
+ status: 'accepted',
status_history: [{
type: 'accepted'
}]
@@ -89,6 +92,7 @@ describe('/api/v1/queue', () => {
.set(passport.inject({roles: ['admin']}))
.then((res) => {
expect(res).to.have.status(200);
+ expect(res.body.comments).to.have.length(1);
expect(res.body.comments[0]).to.have.property('body');
expect(res.body.users[0]).to.have.property('displayName');
expect(res.body.actions[0]).to.have.property('action_type');
diff --git a/tests/routes/api/stream/index.js b/tests/routes/api/stream/index.js
index 5d192e976..d9b59d6fe 100644
--- a/tests/routes/api/stream/index.js
+++ b/tests/routes/api/stream/index.js
@@ -29,6 +29,7 @@ describe('/api/v1/stream', () => {
body: 'comment 10',
author_id: '',
parent_id: '',
+ status: 'accepted',
status_history: [{
type: 'accepted'
}]
@@ -37,6 +38,7 @@ describe('/api/v1/stream', () => {
body: 'comment 20',
author_id: '',
parent_id: '',
+ status: null,
status_history: []
}, {
id: 'uio',
@@ -44,6 +46,7 @@ describe('/api/v1/stream', () => {
asset_id: 'asset',
author_id: '456',
parent_id: '',
+ status: 'accepted',
status_history: [{
type: 'accepted'
}]
@@ -51,6 +54,7 @@ describe('/api/v1/stream', () => {
id: 'hij',
body: 'comment 40',
asset_id: '456',
+ status: 'rejected',
status_history: [{
type: 'rejected'
}]