diff --git a/client/coral-admin/src/actions/community.js b/client/coral-admin/src/actions/community.js
index 06c8ab0f6..7a4112f8b 100644
--- a/client/coral-admin/src/actions/community.js
+++ b/client/coral-admin/src/actions/community.js
@@ -5,7 +5,8 @@ import {
FETCH_COMMENTERS_SUCCESS,
FETCH_COMMENTERS_FAILURE,
SORT_UPDATE,
- COMMENTERS_NEW_PAGE
+ COMMENTERS_NEW_PAGE,
+ SET_ROLE
} from '../constants/community';
import {base, getInit, handleResp} from '../helpers/response';
@@ -40,3 +41,9 @@ export const newPage = () => ({
type: COMMENTERS_NEW_PAGE
});
+export const setRole = (id, role) => dispatch => {
+ return fetch(`${base}/user/${id}/role`, getInit('POST', {role}))
+ .then(() => {
+ return dispatch({type: SET_ROLE, id, role});
+ });
+};
diff --git a/client/coral-admin/src/constants/community.js b/client/coral-admin/src/constants/community.js
index e628a14d6..2ea77ea77 100644
--- a/client/coral-admin/src/constants/community.js
+++ b/client/coral-admin/src/constants/community.js
@@ -3,3 +3,4 @@ export const FETCH_COMMENTERS_SUCCESS = 'FETCH_COMMENTERS_SUCCESS';
export const FETCH_COMMENTERS_FAILURE = 'FETCH_COMMENTERS_FAILURE';
export const SORT_UPDATE = 'SORT_UPDATE';
export const COMMENTERS_NEW_PAGE = 'COMMENTERS_NEW_PAGE';
+export const SET_ROLE = 'SET_ROLE';
diff --git a/client/coral-admin/src/containers/Community/Community.js b/client/coral-admin/src/containers/Community/Community.js
index 8e0b955a4..fb6f5df8c 100644
--- a/client/coral-admin/src/containers/Community/Community.js
+++ b/client/coral-admin/src/containers/Community/Community.js
@@ -19,6 +19,10 @@ const tableHeaders = [
{
title: lang.t('community.account_creation_date'),
field: 'created_at'
+ },
+ {
+ title: lang.t('community.newsroom_role'),
+ field: 'role'
}
];
diff --git a/client/coral-admin/src/containers/Community/Table.js b/client/coral-admin/src/containers/Community/Table.js
index a15a88723..97848bc9a 100644
--- a/client/coral-admin/src/containers/Community/Table.js
+++ b/client/coral-admin/src/containers/Community/Table.js
@@ -1,34 +1,66 @@
-import React from 'react';
+import React, {Component} from 'react';
+import {connect} from 'react-redux';
+import {SelectField, Option} from 'react-mdl-selectfield';
import styles from './Community.css';
+import I18n from 'coral-framework/i18n/i18n';
+import translations from '../../translations';
+import {setRole} from '../../actions/community';
-const Table = ({headers, data, onHeaderClickHandler}) => (
-
-
-
- {headers.map((header, i) =>(
- | onHeaderClickHandler({field: header.field})}>
- {header.title}
- |
- ))}
-
-
-
- {data.map((row, i)=> (
-
- |
- {row.displayName}
- {row.profiles.map(({id}) => id)}
- |
-
- {row.created_at}
- |
-
- ))}
-
-
-);
+const lang = new I18n(translations);
-export default Table;
+class Table extends Component {
+
+ constructor (props) {
+ super(props);
+ this.onRoleChange = this.onRoleChange.bind(this);
+ }
+
+ onRoleChange (id, role) {
+ this.props.dispatch(setRole(id, role));
+ }
+
+ render () {
+ const {headers, commenters, onHeaderClickHandler} = this.props;
+
+ return (
+
+
+
+ {headers.map((header, i) =>(
+ | onHeaderClickHandler({field: header.field})}>
+ {header.title}
+ |
+ ))}
+
+
+
+ {commenters.map((row, i)=> (
+
+ |
+ {row.displayName}
+ {row.profiles.map(({id}) => id)}
+ |
+
+ {row.created_at}
+ |
+
+ this.onRoleChange(row.id, role)}>
+
+
+
+
+ |
+
+ ))}
+
+
+ );
+ }
+}
+
+export default connect(state => ({commenters: state.community.get('commenters')}))(Table);
diff --git a/client/coral-admin/src/reducers/community.js b/client/coral-admin/src/reducers/community.js
index 8d5dfd2c3..81a09a1cc 100644
--- a/client/coral-admin/src/reducers/community.js
+++ b/client/coral-admin/src/reducers/community.js
@@ -4,7 +4,8 @@ import {
FETCH_COMMENTERS_REQUEST,
FETCH_COMMENTERS_FAILURE,
FETCH_COMMENTERS_SUCCESS,
- SORT_UPDATE
+ SORT_UPDATE,
+ SET_ROLE
} from '../constants/community';
const initialState = Map({
@@ -37,6 +38,13 @@ export default function community (state = initialState, action) {
})
.set('commenters', commenters); // Sets to normal array
}
+ case SET_ROLE : {
+ const commenters = state.get('commenters');
+ const idx = commenters.findIndex(el => el.id === action.id);
+
+ commenters[idx].roles[0] = action.role;
+ return state.set('commenters', commenters.map(id => id));
+ }
case SORT_UPDATE :
return state
.set('field', action.sort.field)
diff --git a/client/coral-admin/src/services/talk-adapter.js b/client/coral-admin/src/services/talk-adapter.js
index 9f4dd4715..1457601ed 100644
--- a/client/coral-admin/src/services/talk-adapter.js
+++ b/client/coral-admin/src/services/talk-adapter.js
@@ -34,7 +34,7 @@ export default store => next => action => {
// Get comments to fill each of the three lists on the mod queue
const fetchModerationQueueComments = store =>
-Promise.all([fetch('/api/v1/comments/status/pending'), fetch('/api/v1/comments/status/rejected'), fetch('/api/v1/comments/action/flag')])
+Promise.all([fetch('/api/v1/queue/comments/pending'), fetch('/api/v1/comments/status/rejected'), fetch('/api/v1/comments/action/flag')])
.then(res => Promise.all(res.map(r => r.json())))
.then(res => {
res[2] = res[2].map(comment => { comment.flagged = true; return comment; });
diff --git a/client/coral-admin/src/translations.js b/client/coral-admin/src/translations.js
index 67a8142fd..fa655294a 100644
--- a/client/coral-admin/src/translations.js
+++ b/client/coral-admin/src/translations.js
@@ -2,7 +2,11 @@ export default {
en: {
'community': {
username_and_email: 'Username and Email',
- account_creation_date: 'Account Creation Date'
+ account_creation_date: 'Account Creation Date',
+ newsroom_role: 'Newsroom Role',
+ admin: 'Administrator',
+ moderator: 'Moderator',
+ role: 'Select role...'
},
'modqueue': {
'pending': 'pending',
@@ -30,7 +34,11 @@ export default {
es: {
'community': {
username_and_email: 'Usuario y E-mail',
- account_creation_date: 'Fecha de creación de la cuenta'
+ account_creation_date: 'Fecha de creación de la cuenta',
+ newsroom_role: 'Rol en la redacción',
+ admin: 'Administrador',
+ moderator: 'Moderador',
+ role: 'Select role...'
},
'modqueue': {
'pending': 'pendiente',
diff --git a/models/user.js b/models/user.js
index c0a846f1a..c6197a220 100644
--- a/models/user.js
+++ b/models/user.js
@@ -24,7 +24,9 @@ const UserSchema = new mongoose.Schema({
required: true
}
}],
- roles: [String]
+ roles: {
+ type: [{type: String, enum: ['admin', 'moderator']}]
+ }
}, {
timestamps: {
createdAt: 'created_at',
diff --git a/package.json b/package.json
index 110a7c47d..ad0e2dbb0 100644
--- a/package.json
+++ b/package.json
@@ -57,6 +57,7 @@
"nodemailer": "^2.6.4",
"nodemailer-sendgrid-transport": "^0.2.0",
"prompt": "^1.0.0",
+ "react-mdl-selectfield": "^0.2.0",
"uuid": "^2.0.3"
},
"devDependencies": {
diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js
index 5126bf93c..ea142c35d 100644
--- a/routes/api/comments/index.js
+++ b/routes/api/comments/index.js
@@ -1,8 +1,6 @@
const express = require('express');
const Comment = require('../../../models/comment');
-const Setting = require('../../../models/setting');
-
const router = express.Router();
//==============================================================================
@@ -25,11 +23,7 @@ router.get('/:comment_id', (req, res, next) => {
.catch(next);
});
-//==============================================================================
-// Moderation Queues Routes
-//==============================================================================
-
-// Get all the comments that have that action_type over them.
+// Get all the comments that have an action_type over them.
router.get('/action/:action_type', (req, res, next) => {
Comment
.findByActionType(req.params.action_type)
@@ -41,34 +35,30 @@ router.get('/action/:action_type', (req, res, next) => {
// Get all the comments that were rejected.
router.get('/status/rejected', (req, res, next) => {
- Comment
- .findByStatus('rejected')
- .then(comments => {
- res.status(200).json(comments);
- })
- .catch(next);
+ Comment.findByStatus('rejected').then(comments => {
+ res.status(200).json(comments);
+ })
+ .catch(next);
});
-// Returns back all the comments that are in the moderation queue. The moderation queue is pre or post moderated,
-// 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('/status/pending', (req, res, next) => {
-
- Setting
- .getModerationSetting()
- .then(({moderation}) => {
- let moderationValue = req.query.moderation;
- if (typeof moderationValue === 'undefined' || moderationValue === undefined) {
- moderationValue = moderation;
- }
- Comment
- .moderationQueue(moderationValue)
- .then((comments) => {
- res.status(200).json(comments);
- });
- })
- .catch(next);
+// Get all the comments that were accepted.
+router.get('/status/accepted', (req, res, next) => {
+ Comment.findByStatus('accepted').then((comments) => {
+ res.status(200).json(comments);
+ })
+ .catch(error => {
+ next(error);
+ });
+});
+
+// Get all the not moderated comments.
+router.get('/status/new', (req, res, next) => {
+ Comment.findByStatus('').then((comments) => {
+ res.status(200).json(comments);
+ })
+ .catch(error => {
+ next(error);
+ });
});
//==============================================================================
@@ -76,9 +66,9 @@ router.get('/status/pending', (req, res, next) => {
//==============================================================================
router.post('/', (req, res, next) => {
-
+
const {body, author_id, asset_id, parent_id, status, username} = req.body;
-
+
Comment
.new(body, author_id, asset_id, parent_id, status, username)
.then((comment) => {
@@ -109,7 +99,7 @@ router.post('/:comment_id', (req, res, next) => {
});
router.post('/:comment_id/status', (req, res, next) => {
-
+
Comment
.changeStatus(req.params.comment_id, req.body.status)
.then(comment => res.status(200).send(comment))
diff --git a/routes/api/index.js b/routes/api/index.js
index 2fb489b88..96e1f59d1 100644
--- a/routes/api/index.js
+++ b/routes/api/index.js
@@ -4,6 +4,7 @@ const router = express.Router();
router.use('/asset', require('./asset'));
router.use('/comments', require('./comments'));
+router.use('/queue', require('./queue'));
router.use('/settings', require('./settings'));
router.use('/stream', require('./stream'));
router.use('/user', require('./user'));
diff --git a/routes/api/queue/index.js b/routes/api/queue/index.js
new file mode 100644
index 000000000..f8ab95d6b
--- /dev/null
+++ b/routes/api/queue/index.js
@@ -0,0 +1,27 @@
+const express = require('express');
+const Comment = require('../../../models/comment');
+
+const Setting = require('../../../models/setting');
+
+const router = express.Router();
+
+//==============================================================================
+// Get Routes
+//==============================================================================
+
+// Returns back all the comments that are in the moderation queue. The moderation queue is pre or post moderated,
+// 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) => {
+ Setting.getModerationSetting().then(function({moderation}){
+ Comment.moderationQueue(moderation).then((comments) => {
+ res.status(200).json(comments);
+ });
+ })
+ .catch(error => {
+ next(error);
+ });
+});
+
+module.exports = router;
diff --git a/routes/api/user/index.js b/routes/api/user/index.js
index 18872eab7..4665f4e52 100644
--- a/routes/api/user/index.js
+++ b/routes/api/user/index.js
@@ -40,11 +40,13 @@ router.get('/', (req, res, next) => {
])
.then(([data, count]) => {
const users = data.map((user) => {
- const {displayName, created_at} = user;
+ const {id, displayName, created_at} = user;
return {
+ id,
displayName,
created_at,
- profiles: user.toObject().profiles
+ profiles: user.toObject().profiles,
+ roles: user.toObject().roles
};
});
@@ -60,4 +62,12 @@ router.get('/', (req, res, next) => {
.catch(next);
});
+router.post('/:user_id/role', (req, res, next) => {
+ User.addRoleToUser(req.params.user_id, req.body.role)
+ .then(role => {
+ res.send(role);
+ })
+ .catch(next);
+});
+
module.exports = router;
diff --git a/tests/routes/api/comments/index.js b/tests/routes/api/comments/index.js
index e635e988c..cde4b9efb 100644
--- a/tests/routes/api/comments/index.js
+++ b/tests/routes/api/comments/index.js
@@ -75,7 +75,7 @@ describe('Get /comments', () => {
});
});
-describe('Get moderation queues rejected, pending, flags', () => {
+describe('Get comments by status and action', () => {
const comments = [{
id: 'abc',
body: 'comment 10',
@@ -133,21 +133,20 @@ describe('Get moderation queues rejected, pending, flags', () => {
});
});
- it('should return all the pending comments', function(done){
+ it('should return all the approved comments', function(done){
chai.request(app)
- .get('/api/v1/comments/status/pending')
+ .get('/api/v1/comments/status/accepted')
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
- expect(res.body[0]).to.have.property('id', 'def');
+ expect(res.body[0]).to.have.property('id', 'hij');
done();
});
});
- it('should return all the pending comments as pre moderated', function(done){
+ it('should return all the new comments', function(done){
chai.request(app)
- .get('/api/v1/comments/status/pending')
- .query({'moderation': 'pre'})
+ .get('/api/v1/comments/status/new')
.end(function(err, res){
expect(err).to.be.null;
expect(res).to.have.status(200);
@@ -156,18 +155,6 @@ describe('Get moderation queues rejected, pending, flags', () => {
});
});
- it('should return all the pending comments as post moderated', function(done){
- chai.request(app)
- .get('/api/v1/comments/status/pending')
- .query({'moderation': 'post'})
- .end(function(err, res){
- expect(err).to.be.null;
- expect(res).to.have.status(200);
- expect(res.body).to.have.lengthOf(0);
- done();
- });
- });
-
it('should return all the flagged comments', function(done){
chai.request(app)
.get('/api/v1/comments/action/flag')
diff --git a/tests/routes/api/queue/index.js b/tests/routes/api/queue/index.js
new file mode 100644
index 000000000..f21fe1331
--- /dev/null
+++ b/tests/routes/api/queue/index.js
@@ -0,0 +1,81 @@
+process.env.NODE_ENV = 'test';
+
+require('../../../utils/mongoose');
+
+const app = require('../../../../app');
+const chai = require('chai');
+const expect = chai.expect;
+
+// Setup chai.
+chai.should();
+chai.use(require('chai-http'));
+
+const Comment = require('../../../../models/comment');
+const Action = require('../../../../models/action');
+const User = require('../../../../models/user');
+
+const Setting = require('../../../../models/setting');
+const settings = {id: '1', moderation: 'pre'};
+
+beforeEach(() => {
+ return Setting.create(settings);
+});
+
+describe('Get moderation queues rejected, pending, flags', () => {
+ const comments = [{
+ id: 'abc',
+ body: 'comment 10',
+ asset_id: 'asset',
+ author_id: '123',
+ status: 'rejected'
+ }, {
+ id: 'def',
+ body: 'comment 20',
+ asset_id: 'asset',
+ author_id: '456'
+ }, {
+ id: 'hij',
+ body: 'comment 30',
+ asset_id: '456',
+ status: 'accepted'
+ }];
+
+ const users = [{
+ displayName: 'Ana',
+ email: 'ana@gmail.com',
+ password: '123'
+ }, {
+ displayName: 'Maria',
+ email: 'maria@gmail.com',
+ password: '123'
+ }];
+
+ const actions = [{
+ action_type: 'flag',
+ item_id: 'abc',
+ item_type: 'comment'
+ }, {
+ action_type: 'like',
+ item_id: 'hij',
+ item_type: 'comment'
+ }];
+
+ beforeEach(() => {
+ return Promise.all([
+ Comment.create(comments),
+ User.createLocalUsers(users),
+ Action.create(actions)
+ ]);
+ });
+
+ it('should return all the pending comments', function(done){
+ chai.request(app)
+ .get('/api/v1/queue/comments/pending')
+ .end(function(err, res){
+ expect(err).to.be.null;
+ expect(res).to.have.status(200);
+ expect(res.body[0]).to.have.property('id', 'def');
+ done();
+ });
+ });
+});