Merge branch 'master' into ban-user

This commit is contained in:
gaba
2016-12-09 08:28:46 -10:00
12 changed files with 136 additions and 26 deletions
+14 -11
View File
@@ -10,6 +10,7 @@ You can view product ideas and our longer term roadmap here: https://trello.com/
### Local Dependencies
Node
Mongo
### Getting Started
@@ -26,17 +27,17 @@ inside the environment in order to run, those variables are listed here:
- `TALK_SESSION_SECRET` (*required*) - a random string which will be used to
secure cookies.
`TALK_FACEBOOK_APP_ID` (*required*) - the Facebook app id for your Facebook
- `TALK_FACEBOOK_APP_ID` (*required*) - the Facebook app id for your Facebook
Login enabled app.
`TALK_FACEBOOK_APP_SECRET` (*required*) - the Facebook app secret for your
- `TALK_FACEBOOK_APP_SECRET` (*required*) - the Facebook app secret for your
Facebook Login enabled app.
`TALK_ROOT_URL` (*required*) - root url of the installed application externally
- `TALK_ROOT_URL` (*required*) - root url of the installed application externally
available in the format: `<scheme>://<host>` without the path.
`TALK_SMTP_PROVIDER` (*required*) - SMTP provider name.
`TALK_SMTP_USERNAME` (*required*) - username of the SMTP provider you are using.
`TALK_SMTP_PASSWORD` (*required*) - password for the SMTP provider you are using.
`TALK_SMTP_HOST` (*required*) - SMTP host url with format `smtp.domain.com`.
`TALK_SMTP_PORT` (*required*) - SMTP port.
- `TALK_SMTP_PROVIDER` (*required*) - SMTP provider name.
- `TALK_SMTP_USERNAME` (*required*) - username of the SMTP provider you are using.
- `TALK_SMTP_PASSWORD` (*required*) - password for the SMTP provider you are using.
- `TALK_SMTP_HOST` (*required*) - SMTP host url with format `smtp.domain.com`.
- `TALK_SMTP_PORT` (*required*) - SMTP port.
### Running with Docker
Make sure you have Docker running first and then run `docker-compose up -d`
@@ -48,9 +49,11 @@ Make sure you have Docker running first and then run `docker-compose up -d`
`npm run lint`
### Helpful URLs
Bare comment stream: http://localhost:5000/client/coral-embed-stream/
Comment stream embedded on sample article: http://localhost:5000/client/coral-embed-stream/samplearticle.html
Moderator view: http://localhost:5000/admin/
Comment stream: http://localhost:3000/
Comment stream embedded on sample article: http://localhost:3000/assets/samplearticle.html
Moderator view: http://localhost:3000/admin
### Docs
`swagger.yaml`
@@ -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 {
</p>
</ListItemContent>
</ListItem>
<ListItem className={styles.configSettingInfoBox}>
<ListItemContent>
{lang.t('configure.closed-comments-desc')}
<Textfield
onChange={this.updateClosedMessage}
value={this.props.settings.closedMessage}
label={lang.t('configure.closed-comments-label')}
rows={3}/>
</ListItemContent>
</ListItem>
<ListItem className={`${styles.configSettingInfoBox} ${this.props.settings.infoBoxEnable ? null : styles.hidden}`} >
<ListItemContent>
<Textfield
+9 -1
View File
@@ -50,7 +50,9 @@
"copy-and-paste": "Copy and paste code below into your CMS to embed your comment box in your articles",
"moderate": "Moderate",
"configure": "Configure",
"community": "Community"
"community": "Community",
"closed-comments-desc": "Write a message for closed threads",
"closed-comments-label": "Write a message..."
},
"bandialog": {
"ban_user": "Ban User?",
@@ -100,6 +102,7 @@
"copy-and-paste": "Copiar y pegar el código de más abajo en tu CMS para colocar la caja de comentarios en tus articulos",
"moderate": "Moderar",
"configure": "Configurar",
<<<<<<< HEAD
"community": "Comunidad"
},
"bandialog": {
@@ -108,6 +111,11 @@
"note": "Nota: Suspender este usuario también va a colocar este comentario en la cola de Rechazados.",
"cancel": "Cancelar",
"yes_ban_user": "Si, Suspendan el usuario"
=======
"community": "Comunidad",
"closed-comments-desc": "Escribe un mensaje para cuando los comentarios se encuentran cerrados",
"closed-comments-label": "Escribe un mensaje..."
>>>>>>> master
}
}
}
@@ -97,7 +97,7 @@ class CommentStream extends Component {
const rootItem = this.props.items.assets && this.props.items.assets[rootItemId];
const {actions, users, comments} = this.props.items;
const {loggedIn, user, showSignInDialog, signInOffset} = this.props.auth;
const {status} = this.props.config;
const {status, closedMessage} = this.props.config;
const {activeTab} = this.state;
const banned = (this.props.userData.status === 'banned');
@@ -137,7 +137,7 @@ class CommentStream extends Component {
/>
</RestrictedContent>
</div>
: <p>Comments are closed for this thread.</p>
: <p>{closedMessage}</p>
}
{!loggedIn && <SignInContainer offset={signInOffset} />}
{
+1
View File
@@ -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';
+2 -1
View File
@@ -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) => {
+1
View File
@@ -7,6 +7,7 @@ services:
restart: always
ports:
- "5000:5000"
- "2525:2525"
environment:
- "TALK_PORT=5000"
- "TALK_MONGO_URL=mongodb://mongo"
+15
View File
@@ -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,
@@ -56,6 +64,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).
+10
View File
@@ -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: {
+14 -1
View File
@@ -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.
+4 -8
View File
@@ -3,7 +3,7 @@ const nodemailer = require('nodemailer');
const smtpRequiredProps = [
'TALK_SMTP_USERNAME',
'TALK_SMTP_PASSWORD',
'TALK_SMTP_PROVIDER'
'TALK_SMTP_HOST'
];
smtpRequiredProps.forEach(prop => {
@@ -13,9 +13,7 @@ smtpRequiredProps.forEach(prop => {
});
const options = {
// list of providers here:
// https://github.com/nodemailer/nodemailer-wellknown#supported-services
service: process.env.TALK_SMTP_PROVIDER,
host: process.env.TALK_SMTP_HOST,
auth: {
user: process.env.TALK_SMTP_USERNAME,
pass: process.env.TALK_SMTP_PASSWORD
@@ -24,10 +22,8 @@ const options = {
if (process.env.TALK_SMTP_PORT) {
options.port = process.env.TALK_SMTP_PORT;
}
if (process.env.TALK_SMTP_HOST) {
options.host = process.env.TALK_SMTP_HOST;
} else {
options.port = 25;
}
const defaultTransporter = nodemailer.createTransport(options);
+48 -2
View File
@@ -19,6 +19,7 @@ const settings = {id: '1', moderation: 'pre'};
describe('/api/v1/comments', () => {
// Ensure that the settings are always available.
beforeEach(() => Promise.all([
wordlist.insert(['bad words']),
Setting.init(settings)
@@ -143,11 +144,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');
@@ -158,7 +167,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');
@@ -187,6 +196,43 @@ describe('/api/v1/comments', () => {
expect(res.body).to.have.property('status', '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);
});
});
});
});