From b82f1304542c1ab148bf8ce1f22dfa231682b6dc Mon Sep 17 00:00:00 2001
From: Wyatt Johnson
Date: Tue, 6 Dec 2016 14:44:15 -0500
Subject: [PATCH 1/3] Adds a new closedAt time for assets to prevent new
comments from being created
---
models/asset.js | 15 +++++++
models/setting.js | 10 +++++
routes/api/comments/index.js | 15 ++++++-
tests/routes/api/comments/index.js | 63 ++++++++++++++++++++++++++----
4 files changed, 95 insertions(+), 8 deletions(-)
diff --git a/models/asset.js b/models/asset.js
index a0619576f..7b0717674 100644
--- a/models/asset.js
+++ b/models/asset.js
@@ -29,6 +29,14 @@ const AssetSchema = new Schema({
type: Schema.Types.Mixed,
default: null
},
+ closedAt: {
+ type: Date,
+ default: null
+ },
+ closedMessage: {
+ type: String,
+ default: null
+ },
title: String,
description: String,
image: String,
@@ -60,6 +68,13 @@ AssetSchema.index({
background: true
});
+/**
+ * Returns true if the asset is closed, false else.
+ */
+AssetSchema.virtual('isClosed').get(function() {
+ return this.closedAt && this.closedAt.getTime() <= new Date().getTime();
+});
+
/**
* Finds an asset by its id.
* @param {String} id identifier of the asset (uuid).
diff --git a/models/setting.js b/models/setting.js
index 22b2b103f..5d71b8c97 100644
--- a/models/setting.js
+++ b/models/setting.js
@@ -28,6 +28,16 @@ const SettingSchema = new Schema({
type: String,
default: ''
},
+ closedTimeout: {
+ type: Number,
+
+ // Two weeks default expiry.
+ default: 60 * 60 * 24 * 7 * 2
+ },
+ closedMessage: {
+ type: String,
+ default: ''
+ },
wordlist: [String]
}, {
timestamps: {
diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js
index b82c1faf3..748f64f5b 100644
--- a/routes/api/comments/index.js
+++ b/routes/api/comments/index.js
@@ -80,7 +80,20 @@ router.post('/', wordlist.filter('body'), (req, res, next) => {
status = Promise.resolve('rejected');
} else {
status = Asset
- .rectifySettings(Asset.findById(asset_id))
+ .rectifySettings(Asset.findById(asset_id).then((asset) => {
+ if (!asset) {
+ return Promise.reject(new Error('asset referenced is not found'));
+ }
+
+ // Check to see if the asset has closed commenting...
+ if (asset.isClosed) {
+
+ // They have, ensure that we send back an error.
+ return Promise.reject(new Error(`asset has commenting closed because: ${asset.closedMessage}`));
+ }
+
+ return asset;
+ }))
// Return `premod` if pre-moderation is enabled and an empty "new" status
// in the event that it is not in pre-moderation mode.
diff --git a/tests/routes/api/comments/index.js b/tests/routes/api/comments/index.js
index bf7a06ff6..70912eac7 100644
--- a/tests/routes/api/comments/index.js
+++ b/tests/routes/api/comments/index.js
@@ -19,6 +19,14 @@ const settings = {id: '1', moderation: 'pre'};
describe('/api/v1/comments', () => {
+ // Ensure that the settings are always available.
+ beforeEach(() => Promise.all([
+ Setting.init(settings),
+ wordlist.insert([
+ 'bad words'
+ ])
+ ]));
+
describe('#get', () => {
const comments = [{
body: 'comment 10',
@@ -75,11 +83,7 @@ describe('/api/v1/comments', () => {
return Action.create(actions);
}),
- User.createLocalUsers(users),
- wordlist.insert([
- 'bad words'
- ]),
- Setting.init(settings)
+ User.createLocalUsers(users)
]);
});
@@ -142,11 +146,19 @@ describe('/api/v1/comments', () => {
describe('#post', () => {
+ let asset_id;
+
+ beforeEach(() => Asset.findOrCreateByUrl('https://coralproject.net/section/article-is-the-best').then((asset) => {
+
+ // Update the asset id.
+ asset_id = asset.id;
+ }));
+
it('should create a comment', () => {
return chai.request(app)
.post('/api/v1/comments')
.set(passport.inject({roles: []}))
- .send({'body': 'Something body.', 'author_id': '123', 'asset_id': '1', 'parent_id': ''})
+ .send({'body': 'Something body.', 'author_id': '123', 'asset_id': asset_id, 'parent_id': ''})
.then((res) => {
expect(res).to.have.status(201);
expect(res.body).to.have.property('id');
@@ -157,7 +169,7 @@ describe('/api/v1/comments', () => {
return chai.request(app)
.post('/api/v1/comments')
.set(passport.inject({roles: []}))
- .send({'body': 'bad words are the baddest', 'author_id': '123', 'asset_id': '1', 'parent_id': ''})
+ .send({'body': 'bad words are the baddest', 'author_id': '123', 'asset_id': asset_id, 'parent_id': ''})
.then((res) => {
expect(res).to.have.status(201);
expect(res.body).to.have.property('id');
@@ -188,6 +200,43 @@ describe('/api/v1/comments', () => {
expect(res.body.status[0]).to.have.property('type', 'premod');
});
});
+
+ it('shouldn\'t create a comment when the asset has expired commenting', () => {
+ return Asset.create({
+ closedAt: new Date().setDate(0),
+ closedMessage: 'tests said expired!'
+ })
+ .then((asset) => {
+ return chai.request(app)
+ .post('/api/v1/comments')
+ .set(passport.inject({roles: []}))
+ .send({'body': 'Something body.', 'author_id': '123', 'asset_id': asset.id, 'parent_id': ''});
+ })
+ .then((res) => {
+ expect(res).to.have.status(500);
+ })
+ .catch((err) => {
+ expect(err.response.body).to.not.be.null;
+ expect(err.response.body).to.have.property('message');
+ expect(err.response.body.message).to.contain('tests said expired!');
+ });
+ });
+
+ it('should create a comment when the asset has not expired yet', () => {
+ return Asset.create({
+ closedAt: new Date().setDate(32),
+ closedMessage: 'tests said expired!'
+ })
+ .then((asset) => {
+ return chai.request(app)
+ .post('/api/v1/comments')
+ .set(passport.inject({roles: []}))
+ .send({'body': 'Something body.', 'author_id': '123', 'asset_id': asset.id, 'parent_id': ''});
+ })
+ .then((res) => {
+ expect(res).to.have.status(201);
+ });
+ });
});
});
From fb392422eae88c96be5016c1e8d26bcd21a53e30 Mon Sep 17 00:00:00 2001
From: Dan Zajdband
Date: Thu, 8 Dec 2016 12:44:02 -0500
Subject: [PATCH 2/3] Added textarea for closed message on admin
---
.../src/containers/Configure/Configure.js | 16 ++++++++++++++++
client/coral-admin/src/translations.json | 8 ++++++--
client/coral-embed-stream/src/CommentStream.js | 4 ++--
client/coral-framework/actions/config.js | 1 +
client/coral-framework/reducers/config.js | 3 ++-
models/setting.js | 2 +-
6 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/client/coral-admin/src/containers/Configure/Configure.js b/client/coral-admin/src/containers/Configure/Configure.js
index a9e2f3ef2..e171fc27d 100644
--- a/client/coral-admin/src/containers/Configure/Configure.js
+++ b/client/coral-admin/src/containers/Configure/Configure.js
@@ -28,6 +28,7 @@ class Configure extends React.Component {
// InfoBox has two settings. Enable or not and the content of it if it is enable.
this.updateInfoBoxEnable = this.updateInfoBoxEnable.bind(this);
this.updateInfoBoxContent = this.updateInfoBoxContent.bind(this);
+ this.updateClosedMessage = this.updateClosedMessage.bind(this);
this.saveSettings = this.saveSettings.bind(this);
}
@@ -51,6 +52,11 @@ class Configure extends React.Component {
this.props.dispatch(updateSettings({infoBoxContent}));
}
+ updateClosedMessage (event) {
+ const closedMessage = event.target.value;
+ this.props.dispatch(updateSettings({closedMessage}));
+ }
+
saveSettings () {
this.props.dispatch(saveSettingsToServer());
}
@@ -78,6 +84,16 @@ class Configure extends React.Component {
+
+
+ {lang.t('configure.closed-comments-desc')}
+
+
+
@@ -114,7 +114,7 @@ class CommentStream extends Component {
author={user}
/>
- : Comments are closed for this thread.
+ : {this.props.config.closedMessage}
}
{!loggedIn && }
{
diff --git a/client/coral-framework/actions/config.js b/client/coral-framework/actions/config.js
index 6dc880434..e004fa1eb 100644
--- a/client/coral-framework/actions/config.js
+++ b/client/coral-framework/actions/config.js
@@ -5,6 +5,7 @@ import coralApi from '../helpers/response';
* Action name constants
*/
+export const UPDATE_SETTINGS = 'UPDATE_SETTINGS';
export const OPEN_COMMENTS = 'OPEN_COMMENTS';
export const CLOSE_COMMENTS = 'CLOSE_COMMENTS';
export const ADD_ITEM = 'ADD_ITEM';
diff --git a/client/coral-framework/reducers/config.js b/client/coral-framework/reducers/config.js
index 9620f5b97..7fbccaa49 100644
--- a/client/coral-framework/reducers/config.js
+++ b/client/coral-framework/reducers/config.js
@@ -5,7 +5,8 @@ import * as actions from '../actions/config';
const initialState = Map({
features: Map({}),
- status: 'open'
+ status: 'open',
+ closedMessage: ''
});
export default (state = initialState, action) => {
diff --git a/models/setting.js b/models/setting.js
index 5d71b8c97..2414381fa 100644
--- a/models/setting.js
+++ b/models/setting.js
@@ -99,7 +99,7 @@ SettingService.update = (settings) => Setting.findOneAndUpdate(selector, {
* @param {Object} settings the source settings object
* @return {Object} the filtered settings object
*/
-SettingService.public = (settings) => _.pick(settings, ['moderation', 'infoBoxEnable', 'infoBoxContent']);
+SettingService.public = (settings) => _.pick(settings, ['moderation', 'infoBoxEnable', 'infoBoxContent', 'closedMessage']);
/**
* This is run once when the app starts to ensure settings are populated.
From fed41e1fba80250545f8192ca1c7e7fb7ec7eb5d Mon Sep 17 00:00:00 2001
From: Dan Zajdband
Date: Thu, 8 Dec 2016 16:59:29 -0500
Subject: [PATCH 3/3] Linted code
---
client/coral-embed-stream/src/CommentStream.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/coral-embed-stream/src/CommentStream.js b/client/coral-embed-stream/src/CommentStream.js
index 69fc5f73f..7f3b1b30f 100644
--- a/client/coral-embed-stream/src/CommentStream.js
+++ b/client/coral-embed-stream/src/CommentStream.js
@@ -114,7 +114,7 @@ class CommentStream extends Component {
author={user}
/>
- : {this.props.config.closedMessage}
+ : {closedMessage}
}
{!loggedIn && }
{