@@ -94,41 +98,26 @@ class ModerationQueue extends React.Component {
- comments
- .get('byId')
- .get(id)
- .get('status') === 'premod')
- }
- comments={comments.get('byId')}
- users={users.get('byId')}
+ commentIds={premodIds}
+ comments={comments.byId}
+ users={users.byId}
onClickAction={(action, commentId) => this.onCommentAction(action, commentId)}
onClickShowBanDialog={(userId, userName, commentId) => this.showBanUserDialog(userId, userName, commentId)}
actions={['reject', 'approve', 'ban']}
loading={comments.loading} />
this.hideBanUserDialog()}
onClickBanUser={(userId, commentId) => this.banUser(userId, commentId)}
- user={comments.get('banUser')}/>
+ user={comments.banUser}/>
- comments
- .get('byId')
- .get(id)
- .get('status') === 'rejected')
- }
- comments={comments.get('byId')}
- users={users.get('byId')}
+ commentIds={rejectedIds}
+ comments={comments.byId}
+ users={users.byId}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['approve']}
loading={comments.loading} />
@@ -137,12 +126,9 @@ class ModerationQueue extends React.Component {
{
- const data = comments.get('byId').get(id);
- return !data.get('status') && data.get('flagged') === true;
- })}
- comments={comments.get('byId')}
- users={users.get('byId')}
+ commentIds={flaggedIds}
+ comments={comments.byId}
+ users={users.byId}
onClickAction={(action, id) => this.onCommentAction(action, id)}
actions={['reject', 'approve']}
loading={comments.loading} />
@@ -155,6 +141,11 @@ class ModerationQueue extends React.Component {
}
}
-export default connect(({comments, users}) => ({comments, users}))(ModerationQueue);
+const mapStateToProps = state => ({
+ comments: state.comments.toJS(),
+ users: state.users.toJS()
+});
+
+export default connect(mapStateToProps)(ModerationQueue);
const lang = new I18n(translations);
diff --git a/client/coral-configure/translations.json b/client/coral-configure/translations.json
index e597c9c9a..5aa7f72b6 100644
--- a/client/coral-configure/translations.json
+++ b/client/coral-configure/translations.json
@@ -3,7 +3,7 @@
"configureCommentStream": {
"apply": "Apply",
"title": "Configure Comment Stream",
- "description": "As an admin you may customize the settings for the comment stream for this article",
+ "description": "As an admin you may customize the settings for the comment stream for this asset",
"enablePremod": "Enable Premoderation",
"enablePremodDescription": "Moderators must approve any comment before its published.",
"enablePremodLinks": "Pre-Moderate Comments Containing Links",
diff --git a/client/coral-framework/actions/config.js b/client/coral-framework/actions/config.js
index f85e609eb..c88138534 100644
--- a/client/coral-framework/actions/config.js
+++ b/client/coral-framework/actions/config.js
@@ -6,14 +6,6 @@ import I18n from 'coral-framework/modules/i18n/i18n';
import translations from './../translations';
const lang = new I18n(translations);
-export const updateOpenStatus = status => (dispatch, getState) => {
- const assetId = getState().items.get('assets')
- .keySeq()
- .toArray()[0];
- return coralApi(`/asset/${assetId}/status?status=${status}`, {method: 'PUT'})
- .then(() => dispatch({type: status === 'open' ? actions.OPEN_COMMENTS : actions.CLOSE_COMMENTS}));
-};
-
const updateConfigRequest = () => ({type: actions.UPDATE_CONFIG_REQUEST});
const updateConfigSuccess = config => ({type: actions.UPDATE_CONFIG_SUCCESS, config});
const updateConfigFailure = () => ({type: actions.UPDATE_CONFIG_FAILURE});
@@ -31,3 +23,31 @@ export const updateConfiguration = newConfig => (dispatch, getState) => {
})
.catch(error => dispatch(updateConfigFailure(error)));
};
+
+export const updateOpenStream = closedBody => (dispatch, getState) => {
+ const assetId = getState().items.get('assets')
+ .keySeq()
+ .toArray()[0];
+
+ dispatch(updateConfigRequest());
+
+ coralApi(`/asset/${assetId}/status`, {method: 'PUT', body: closedBody})
+ .then(() => {
+ dispatch(addNotification('success', lang.t('successUpdateSettings')));
+ dispatch(updateConfigSuccess(closedBody));
+ })
+ .catch(error => dispatch(updateConfigFailure(error)));
+};
+
+const openStream = () => ({type: actions.OPEN_COMMENTS});
+const closeStream = () => ({type: actions.CLOSE_COMMENTS});
+
+export const updateOpenStatus = status => dispatch => {
+ if (status === 'open') {
+ dispatch(openStream());
+ dispatch(updateOpenStream({closedAt: null}));
+ } else {
+ dispatch(closeStream());
+ dispatch(updateOpenStream({closedAt: new Date().getTime()}));
+ }
+};
diff --git a/client/coral-framework/reducers/config.js b/client/coral-framework/reducers/config.js
index 8266fbfa1..362608d6d 100644
--- a/client/coral-framework/reducers/config.js
+++ b/client/coral-framework/reducers/config.js
@@ -22,7 +22,7 @@ export default (state = initialState, action) => {
return state
.set('status', 'closed');
case actions.ADD_ITEM:
- return action.item_type === 'assets' ? state.set('status', action.item.status) : state;
+ return action.item_type === 'assets' ? state.set('status', (action.item && action.item.closedAt && new Date(action.item.closedAt).getTime() <= new Date().getTime()) ? 'closed' : 'open') : state;
default:
return state;
}
diff --git a/architecture.png b/docs/architecture.png
similarity index 100%
rename from architecture.png
rename to docs/architecture.png
diff --git a/architecture.xml b/docs/architecture.xml
similarity index 100%
rename from architecture.xml
rename to docs/architecture.xml
diff --git a/ data-model.png b/docs/data-model.png
similarity index 100%
rename from data-model.png
rename to docs/data-model.png
diff --git a/ data-model.xml b/docs/data-model.xml
similarity index 100%
rename from data-model.xml
rename to docs/data-model.xml
diff --git a/moderation-flow-post.png b/docs/moderation-flow-post.png
similarity index 100%
rename from moderation-flow-post.png
rename to docs/moderation-flow-post.png
diff --git a/moderation-flow-pre.png b/docs/moderation-flow-pre.png
similarity index 100%
rename from moderation-flow-pre.png
rename to docs/moderation-flow-pre.png
diff --git a/moderation-flow.xml b/docs/moderation-flow.xml
similarity index 100%
rename from moderation-flow.xml
rename to docs/moderation-flow.xml
diff --git a/swagger.yaml b/docs/swagger.yaml
similarity index 100%
rename from swagger.yaml
rename to docs/swagger.yaml
diff --git a/models/action.js b/models/action.js
index ed18e48c5..45f828226 100644
--- a/models/action.js
+++ b/models/action.js
@@ -1,4 +1,4 @@
-const mongoose = require('../mongoose');
+const mongoose = require('../services/mongoose');
const uuid = require('uuid');
const _ = require('lodash');
const Schema = mongoose.Schema;
diff --git a/models/asset.js b/models/asset.js
index 3c68b7c13..09c06a09d 100644
--- a/models/asset.js
+++ b/models/asset.js
@@ -1,4 +1,4 @@
-const mongoose = require('../mongoose');
+const mongoose = require('../services/mongoose');
const Schema = mongoose.Schema;
const Setting = require('./setting');
@@ -19,7 +19,7 @@ const AssetSchema = new Schema({
},
type: {
type: String,
- default: 'article'
+ default: 'assets'
},
scraped: {
type: Date,
@@ -64,13 +64,6 @@ 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).
@@ -83,6 +76,13 @@ AssetSchema.statics.findById = (id) => Asset.findOne({id});
*/
AssetSchema.statics.findByUrl = (url) => Asset.findOne({url});
+/**
+ * Returns true if the asset is closed, false else.
+ */
+AssetSchema.virtual('isClosed').get(function() {
+ return this.closedAt && this.closedAt.getTime() <= new Date().getTime();
+});
+
/**
* Retrieves the settings given an asset query and rectifies it against the
* global settings.
diff --git a/models/comment.js b/models/comment.js
index 9cfee2ee7..b3b3bc52f 100644
--- a/models/comment.js
+++ b/models/comment.js
@@ -1,4 +1,4 @@
-const mongoose = require('../mongoose');
+const mongoose = require('../services/mongoose');
const Schema = mongoose.Schema;
const _ = require('lodash');
const uuid = require('uuid');
diff --git a/models/setting.js b/models/setting.js
index f736600ff..6f142c760 100644
--- a/models/setting.js
+++ b/models/setting.js
@@ -1,7 +1,7 @@
-const mongoose = require('../mongoose');
+const mongoose = require('../services/mongoose');
const Schema = mongoose.Schema;
const _ = require('lodash');
-const cache = require('../cache');
+const cache = require('../services/cache');
/**
* SettingSchema manages application settings that get used on front and backend.
diff --git a/models/user.js b/models/user.js
index 857eb1260..216345cbd 100644
--- a/models/user.js
+++ b/models/user.js
@@ -1,4 +1,4 @@
-const mongoose = require('../mongoose');
+const mongoose = require('../services/mongoose');
const uuid = require('uuid');
const _ = require('lodash');
const bcrypt = require('bcrypt');
diff --git a/package.json b/package.json
index c54bd6723..53af9910b 100644
--- a/package.json
+++ b/package.json
@@ -5,21 +5,26 @@
"main": "app.js",
"scripts": {
"start": "./bin/cli serve --jobs",
- "build": "NODE_ENV=production webpack --config webpack.config.js --bail",
- "build-watch": "NODE_ENV=development webpack --config webpack.config.dev.js --watch",
- "lint": "eslint bin/* .",
- "lint-fix": "eslint . --fix",
- "test": "NODE_ENV=test mocha --compilers js:babel-core/register --recursive tests",
- "test-watch": "NODE_ENV=test mocha --compilers js:babel-core/register --recursive -w tests",
- "pree2e": "NODE_ENV=test ./pree2e.sh",
- "e2e": "NODE_ENV=test node_modules/.bin/nightwatch",
+ "build": "NODE_ENV=production ./node_modules/.bin/webpack --config webpack.config.js --bail",
+ "build-watch": "NODE_ENV=development ./node_modules/.bin/webpack --config webpack.config.dev.js --watch",
+ "lint": "./node_modules/.bin/eslint bin/* .",
+ "lint-fix": "./node_modules/.bin/eslint bin/* . --fix",
+ "test": "NODE_ENV=test ./node_modules/.bin/mocha --compilers js:babel-core/register --recursive tests",
+ "test-watch": "NODE_ENV=test ./node_modules/.bin/mocha --compilers js:babel-core/register --recursive -w tests",
+ "pree2e": "NODE_ENV=test ./scripts/pree2e.sh",
+ "e2e": "NODE_ENV=test ./node_modules/.bin/nightwatch",
"embed-start": "NODE_ENV=development npm run build && ./bin/cli serve --jobs"
},
"config": {
"pre-git": {
"commit-msg": [],
- "pre-commit": ["npm run lint", "npm test"],
- "pre-push": ["npm test"],
+ "pre-commit": [
+ "npm run lint",
+ "npm test"
+ ],
+ "pre-push": [
+ "npm test"
+ ],
"post-commit": [],
"post-merge": []
}
@@ -28,7 +33,12 @@
"type": "git",
"url": "git+https://github.com/coralproject/talk.git"
},
- "keywords": ["talk", "coral", "coralproject", "ask"],
+ "keywords": [
+ "talk",
+ "coral",
+ "coralproject",
+ "ask"
+ ],
"author": "",
"license": "Apache-2.0",
"bugs": {
@@ -59,7 +69,6 @@
"passport-facebook": "^2.1.1",
"passport-local": "^1.0.0",
"prompt": "^1.0.0",
- "react-linkify": "^0.1.3",
"redis": "^2.6.3",
"uuid": "^2.0.3"
},
@@ -84,9 +93,10 @@
"copy-webpack-plugin": "^4.0.0",
"css-loader": "^0.25.0",
"dialog-polyfill": "^0.4.4",
- "eslint": "^3.9.1",
+ "eslint": "^3.12.1",
"eslint-config-postcss": "^2.0.2",
"eslint-config-standard": "^6.2.1",
+ "eslint-module-utils": "^2.0.0",
"eslint-plugin-flowtype": "^2.25.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-mocha": "^4.7.0",
@@ -113,6 +123,7 @@
"pym.js": "^1.1.1",
"react": "15.3.2",
"react-dom": "15.3.2",
+ "react-linkify": "^0.1.3",
"react-mdl": "^1.7.2",
"react-mdl-selectfield": "^0.2.0",
"react-onclickoutside": "^5.7.1",
diff --git a/pree2e.sh b/pree2e.sh
deleted file mode 100755
index 8857e5745..000000000
--- a/pree2e.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules/selenium-standalone/bin/selenium-standalone install
-npm start &
diff --git a/routes/api/asset/index.js b/routes/api/asset/index.js
index 2ddc3ea23..5aa9b8cc6 100644
--- a/routes/api/asset/index.js
+++ b/routes/api/asset/index.js
@@ -90,11 +90,28 @@ router.put('/:asset_id/settings', (req, res, next) => {
});
router.put('/:asset_id/status', (req, res, next) => {
- // Update the asset status
+
+ const id = req.params.asset_id;
+
+ const {
+ closedAt,
+ closedMessage
+ } = req.body;
+
Asset
- .update({id: req.params.asset_id}, {status: req.query.status})
- .then(() => res.status(204).end())
- .catch((err) => next(err));
+ .update({id}, {
+ $set: {
+ closedAt,
+ closedMessage
+ }
+ })
+ .then(() => {
+
+ res.status(204).json();
+ })
+ .catch((err) => {
+ next(err);
+ });
});
module.exports = router;
diff --git a/routes/api/auth/index.js b/routes/api/auth/index.js
index 369e5ec77..bcbfcc27e 100644
--- a/routes/api/auth/index.js
+++ b/routes/api/auth/index.js
@@ -1,5 +1,5 @@
const express = require('express');
-const passport = require('../../../passport');
+const passport = require('../../../services/passport');
const authorization = require('../../../middleware/authorization');
const router = express.Router();
diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js
index 168b89fc3..b54717c78 100644
--- a/routes/api/comments/index.js
+++ b/routes/api/comments/index.js
@@ -87,7 +87,6 @@ router.post('/', wordlist.filter('body'), (req, res, next) => {
// 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}`));
}
diff --git a/routes/api/index.js b/routes/api/index.js
index 7863b0cd0..6a7f6ff10 100644
--- a/routes/api/index.js
+++ b/routes/api/index.js
@@ -19,6 +19,6 @@ router.use('/stream', require('./stream'));
router.use('/users', require('./users'));
// Bind the kue handler to the /kue path.
-router.use('/kue', authorization.needed('admin'), require('../../kue').kue.app);
+router.use('/kue', authorization.needed('admin'), require('../../services/kue').kue.app);
module.exports = router;
diff --git a/scripts/pree2e.sh b/scripts/pree2e.sh
new file mode 100755
index 000000000..dc3266dd2
--- /dev/null
+++ b/scripts/pree2e.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# install selenium
+../node_modules/selenium-standalone/bin/selenium-standalone install
+
+# start the app server
+npm start &
diff --git a/cache.js b/services/cache.js
similarity index 100%
rename from cache.js
rename to services/cache.js
diff --git a/kue.js b/services/kue.js
similarity index 100%
rename from kue.js
rename to services/kue.js
diff --git a/mongoose.js b/services/mongoose.js
similarity index 100%
rename from mongoose.js
rename to services/mongoose.js
diff --git a/passport.js b/services/passport.js
similarity index 98%
rename from passport.js
rename to services/passport.js
index 2e084eafd..f709970dc 100644
--- a/passport.js
+++ b/services/passport.js
@@ -1,5 +1,5 @@
const passport = require('passport');
-const User = require('./models/user');
+const User = require('../models/user');
const LocalStrategy = require('passport-local').Strategy;
const FacebookStrategy = require('passport-facebook').Strategy;
diff --git a/redis.js b/services/redis.js
similarity index 100%
rename from redis.js
rename to services/redis.js
diff --git a/services/scraper.js b/services/scraper.js
index 10e0e4aa8..c75a12750 100644
--- a/services/scraper.js
+++ b/services/scraper.js
@@ -1,4 +1,4 @@
-const kue = require('../kue');
+const kue = require('./kue');
const debug = require('debug')('talk:services:scraper');
const Asset = require('../models/asset');
const JOB_NAME = 'scraper';
diff --git a/tests/mongoose.js b/tests/mongoose.js
index 74a0b04f7..7544072ec 100644
--- a/tests/mongoose.js
+++ b/tests/mongoose.js
@@ -1,4 +1,4 @@
-const mongoose = require('../mongoose');
+const mongoose = require('../services/mongoose');
beforeEach(function (done) {
function clearDB() {
diff --git a/tests/routes/api/assets/index.js b/tests/routes/api/assets/index.js
index c56e5b1ba..64e820722 100644
--- a/tests/routes/api/assets/index.js
+++ b/tests/routes/api/assets/index.js
@@ -17,7 +17,8 @@ describe('/api/v1/assets', () => {
{
url: 'https://coralproject.net/news/asset1',
title: 'Asset 1',
- description: 'term1'
+ description: 'term1',
+ id: '1'
},
{
url: 'https://coralproject.net/news/asset2',
@@ -82,4 +83,32 @@ describe('/api/v1/assets', () => {
});
+ describe('#put', () => {
+ it('should close the asset', function() {
+
+ const today = Date.now();
+
+ return Asset.findOrCreateByUrl('http://test.com')
+ .then((asset) => {
+ expect(asset).to.have.property('isClosed', null);
+ expect(asset).to.have.property('closedAt', null);
+
+ return chai.request(app)
+ .put(`/api/v1/asset/${asset.id}/status`)
+ .set(passport.inject({roles: ['admin']}))
+ .send({closedAt: today});
+ })
+ .then((res) => {
+
+ expect(res).to.have.status(204);
+
+ return Asset.findByUrl('http://test.com');
+ })
+ .then((asset) => {
+ expect(asset).to.have.property('isClosed', true);
+ expect(asset).to.have.property('closedAt').and.to.not.equal(null);
+ });
+ });
+ });
+
});
diff --git a/tests/utils/e2e-mongoose.js b/tests/utils/e2e-mongoose.js
index 1bb7e695b..0b630c0e3 100644
--- a/tests/utils/e2e-mongoose.js
+++ b/tests/utils/e2e-mongoose.js
@@ -1,4 +1,4 @@
-const mongoose = require('../../mongoose');
+const mongoose = require('../../services/mongoose');
// Ensure the NODE_ENV is set to 'test',
// this is helpful when you would like to change behavior when testing.