mirror of
https://github.com/wassname/talk.git
synced 2026-07-05 20:33:41 +08:00
Merge branch 'master' of github.com:coralproject/talk into passport
This commit is contained in:
@@ -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; });
|
||||
|
||||
@@ -14,6 +14,7 @@ import AuthorName from '../../coral-plugin-author-name/AuthorName';
|
||||
import {ReplyBox, ReplyButton} from '../../coral-plugin-replies';
|
||||
import Pym from 'pym.js';
|
||||
import FlagButton from '../../coral-plugin-flags/FlagButton';
|
||||
import PermalinkButton from '../../coral-plugin-permalinks/PermalinkButton';
|
||||
|
||||
const {addItem, updateItem, postItem, getStream, postAction, appendItemArray} = itemActions;
|
||||
const {addNotification, clearNotification} = notificationActions;
|
||||
@@ -131,6 +132,9 @@ class CommentStream extends Component {
|
||||
<ReplyButton
|
||||
updateItem={this.props.updateItem}
|
||||
id={commentId}/>
|
||||
<PermalinkButton
|
||||
comment_id={commentId}
|
||||
asset_id={comment.asset_id}/>
|
||||
</div>
|
||||
<ReplyBox
|
||||
addNotification={this.props.addNotification}
|
||||
@@ -162,6 +166,10 @@ class CommentStream extends Component {
|
||||
<ReplyButton
|
||||
updateItem={this.props.updateItem}
|
||||
parent_id={reply.parent_id}/>
|
||||
<PermalinkButton
|
||||
comment_id={reply.comment_id}
|
||||
asset_id={reply.comment_id}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
})
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import I18n from 'coral-framework/i18n/i18n';
|
||||
import translations from './translations';
|
||||
import onClickOutside from 'react-onclickoutside';
|
||||
const name = 'coral-plugin-permalinks';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
class PermalinkButton extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
asset_id: PropTypes.string.isRequired,
|
||||
comment_id: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {popoverOpen: false, copySuccessful: null, copyFailure: null};
|
||||
this.toggle = this.toggle.bind(this);
|
||||
this.copyPermalink = this.copyPermalink.bind(this);
|
||||
}
|
||||
|
||||
toggle () {
|
||||
this.setState({popoverOpen: !this.state.popoverOpen});
|
||||
}
|
||||
|
||||
handleClickOutside () {
|
||||
this.setState({popoverOpen: false});
|
||||
}
|
||||
|
||||
copyPermalink () {
|
||||
this.permalinkInput.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
this.setState({copySuccessful: true});
|
||||
} catch (err) {
|
||||
this.setState({copyFailure: true});
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.setState({copyFailure: null, copySuccessful: null});
|
||||
}, 4500);
|
||||
}
|
||||
|
||||
render () {
|
||||
const publisherUrl = `${location.protocol}//${location.host}/`;
|
||||
|
||||
return (
|
||||
<div className={`${name}-container`} style={styles}>
|
||||
<button onClick={this.toggle} className={`${name}-button`}>
|
||||
<i className={`${name}-icon material-icons`} aria-hidden={true}>link</i>
|
||||
{lang.t('permalink.permalink')}
|
||||
</button>
|
||||
<div
|
||||
style={styles.popover(this.state.popoverOpen)}
|
||||
className={`${name}-popover`}>
|
||||
<input
|
||||
type='text'
|
||||
ref={input => this.permalinkInput = input}
|
||||
value={`${publisherUrl}${this.props.asset_id}#${this.props.comment_id}`}
|
||||
onChange={() => {}} />
|
||||
<button className={`${name}-copy-button`} onClick={this.copyPermalink}>Copy</button>
|
||||
{
|
||||
this.state.copySuccessful ? <p>copied to clipboard</p> : null
|
||||
}
|
||||
{
|
||||
this.state.copyFailure
|
||||
? <p>copying to clipboard not supported in this browser. Use Cmd + C.</p>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default onClickOutside(PermalinkButton);
|
||||
|
||||
const styles = {
|
||||
position: 'relative',
|
||||
|
||||
popover: active => {
|
||||
return {
|
||||
display: active ? 'block' : 'none',
|
||||
backgroundColor: 'white',
|
||||
border: '1px solid black',
|
||||
minWidth: 400,
|
||||
position: 'absolute',
|
||||
top: 30,
|
||||
right: 0,
|
||||
padding: 5
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"en": {
|
||||
"permalink": {
|
||||
"permalink": "Permalink"
|
||||
}
|
||||
},
|
||||
"es": {
|
||||
"permalink": {
|
||||
"permalink": "Enlace permanente"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,7 @@
|
||||
"react": "15.3.2",
|
||||
"react-dom": "15.3.2",
|
||||
"react-mdl": "^1.7.2",
|
||||
"react-onclickoutside": "^5.7.1",
|
||||
"react-redux": "^4.4.5",
|
||||
"react-router": "^3.0.0",
|
||||
"redux": "^3.6.0",
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -5,6 +5,7 @@ const router = express.Router();
|
||||
router.use('/asset', require('./asset'));
|
||||
router.use('/auth', require('./auth'));
|
||||
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'));
|
||||
|
||||
@@ -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;
|
||||
@@ -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,9 +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', 'hij');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return all the new comments', function(done){
|
||||
chai.request(app)
|
||||
.get('/api/v1/comments/status/new')
|
||||
.end(function(err, res){
|
||||
expect(err).to.be.null;
|
||||
expect(res).to.have.status(200);
|
||||
@@ -144,37 +155,15 @@ describe('Get moderation queues rejected, pending, flags', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should return all the pending comments as pre moderated', function(done){
|
||||
it('should return all the flagged comments', function(done){
|
||||
chai.request(app)
|
||||
.get('/api/v1/comments/status/pending')
|
||||
.query({'moderation': 'pre'})
|
||||
.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();
|
||||
});
|
||||
});
|
||||
|
||||
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', () => {
|
||||
return chai.request(app)
|
||||
.get('/api/v1/comments/action/flag')
|
||||
.then((res) => {
|
||||
.end(function(err, res){
|
||||
expect(res).to.have.status(200);
|
||||
expect(err).to.be.null;
|
||||
expect(res.body.length).to.equal(1);
|
||||
expect(res.body[0]).to.have.property('id', 'abc');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -390,6 +379,11 @@ describe('Remove /:comment_id', () => {
|
||||
});
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
console.error('Reason: ');
|
||||
console.error(reason);
|
||||
});
|
||||
|
||||
describe('Post /:comment_id/status', () => {
|
||||
|
||||
const comments = [{
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user