Merge branch 'master' into ban-user

This commit is contained in:
Gabriela Rodríguez Berón
2016-12-09 10:28:32 -10:00
committed by GitHub
18 changed files with 357 additions and 56 deletions
@@ -0,0 +1,37 @@
.container {
position: relative;
}
.apply {
position: absolute;
top: 38%;
transform: translateX(-50%);
right: 0;
}
ul {
list-style: none;
padding: 0;
}
ul ul {
padding-left: 20px
}
.checkbox {
vertical-align: top;
margin: 12px 12px 12px 0;
}
h4 {
font-size: 14px;
margin-bottom: 5px;
}
p {
max-width: 380px;
}
.wrapper {
margin-bottom: 20px;
}
@@ -0,0 +1,53 @@
import React from 'react';
import {Button, Checkbox} from 'coral-ui';
import styles from './ConfigureCommentStream.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations.json';
const lang = new I18n(translations);
export default ({handleChange, handleApply, changed, ...props}) => (
<div className={styles.wrapper}>
<div className={styles.container}>
<h3>{lang.t('configureCommentStream.title')}</h3>
<p>{lang.t('configureCommentStream.description')}</p>
<Button
className={styles.apply}
cStyle={changed ? 'green' : 'darkGrey'}
onClick={handleApply}
>
{lang.t('configureCommentStream.apply')}
</Button>
</div>
<ul>
<li>
<Checkbox
className={styles.checkbox}
cStyle={changed ? 'green' : 'darkGrey'}
name="premod"
onChange={handleChange}
checked={props.premod}
info={{
title: lang.t('configureCommentStream.enablePremod'),
description: lang.t('configureCommentStream.enablePremodDescription')
}}
/>
<ul>
<li>
<Checkbox
className={styles.checkbox}
cStyle={changed ? 'green' : 'darkGrey'}
name="premodLinks"
onChange={handleChange}
checked={props.premodLinks}
info={{
title: lang.t('configureCommentStream.enablePremodLinks'),
description: lang.t('configureCommentStream.enablePremodDescription')
}}
/>
</li>
</ul>
</li>
</ul>
</div>
);
@@ -0,0 +1,83 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {updateOpenStatus, updateConfiguration} from '../../coral-framework/actions/config';
import CloseCommentsInfo from '../components/CloseCommentsInfo';
import ConfigureCommentStream from '../components/ConfigureCommentStream';
class ConfigureStreamContainer extends Component {
constructor (props) {
super(props);
this.state = {
premod: props.config.moderation === 'pre',
premodLinks: false
};
this.toggleStatus = this.toggleStatus.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleApply = this.handleApply.bind(this);
}
handleApply () {
const {premod, changed} = this.state;
const newConfig = {
moderation: premod ? 'pre' : 'post'
};
if (changed) {
this.props.updateConfiguration(newConfig);
setTimeout(() => {
this.setState({
changed: false
});
}, 300);
}
}
handleChange (e) {
const {name, checked} = e.target;
this.setState({
[name]: checked,
changed: true
});
}
toggleStatus () {
this.props.updateStatus(this.props.config.status === 'open' ? 'closed' : 'open');
}
render () {
const {status} = this.props;
return (
<div>
<ConfigureCommentStream
handleChange={this.handleChange}
handleApply={this.handleApply}
changed={this.state.changed}
{...this.state}
/>
<hr />
<h3>{status === 'open' ? 'Close' : 'Open'} Comment Stream</h3>
<CloseCommentsInfo
onClick={this.toggleStatus}
status={status}
/>
</div>
);
}
}
const mapStateToProps = (state) => ({
config: state.config.toJS()
});
const mapDispatchToProps = dispatch => ({
updateStatus: status => dispatch(updateOpenStatus(status)),
updateConfiguration: newConfig => dispatch(updateConfiguration(newConfig))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ConfigureStreamContainer);
+24
View File
@@ -0,0 +1,24 @@
{
"en": {
"configureCommentStream": {
"apply": "Apply",
"title": "Configure Comment Stream",
"description": "As an admin you may customize the settings for the comment stream for this article",
"enablePremod": "Enable Premoderation",
"enablePremodDescription": "Moderators must approve any comment before its published.",
"enablePremodLinks": "Pre-Moderate Comments Containing Links",
"enablePremodLinksDescription": "Moderators must approve any comment containing a link before its published."
}
},
"es": {
"configureCommentStream": {
"apply": "Aplicar",
"title": "Configurar los comentarios",
"description": "Como Administrador puedes modificar las opciones de los comentarios en este artículo",
"enablePremod": "Activar Pre Moderación",
"enablePremodDescription": "Los Moderadores deben aprobar cualquier comentario antes de su publicación",
"enablePremodLinks": "Pre-Moderar Commentarios que contienen Links",
"enablePremodLinksDescription": "Los Moderadores deben probar cualquier comentario que contengan links antes de su publicación."
}
}
}
+8 -13
View File
@@ -6,8 +6,7 @@ import {
itemActions,
Notification,
notificationActions,
authActions,
configActions
authActions
} from '../../coral-framework';
import CommentBox from '../../coral-plugin-commentbox/CommentBox';
@@ -27,12 +26,12 @@ import {TabBar, Tab, TabContent, Spinner} from '../../coral-ui';
import SettingsContainer from '../../coral-settings/containers/SettingsContainer';
import RestrictedContent from '../../coral-framework/components/RestrictedContent';
import SuspendedAccount from '../../coral-framework/components/SuspendedAccount';
import CloseCommentsInfo from '../../coral-framework/components/CloseCommentsInfo';
import ConfigureStreamContainer from '../../coral-configure/containers/ConfigureStreamContainer';
const {addItem, updateItem, postItem, getStream, postAction, deleteAction, appendItemArray} = itemActions;
const {addNotification, clearNotification} = notificationActions;
const {logout, showSignInDialog} = authActions;
const {updateOpenStatus} = configActions;
class CommentStream extends Component {
@@ -44,7 +43,6 @@ class CommentStream extends Component {
};
this.changeTab = this.changeTab.bind(this);
this.toggleStatus = this.toggleStatus.bind(this);
}
changeTab (tab) {
@@ -53,10 +51,6 @@ class CommentStream extends Component {
});
}
toggleStatus () {
this.props.updateStatus(this.props.config.status === 'open' ? 'closed' : 'open');
}
static propTypes = {
items: PropTypes.object.isRequired,
addItem: PropTypes.func.isRequired,
@@ -272,9 +266,11 @@ class CommentStream extends Component {
/>
</TabContent>
<TabContent show={activeTab === 2}>
<h3>{status === 'open' ? 'Close' : 'Open'} Comment Stream</h3>
<RestrictedContent restricted={!loggedIn}>
<CloseCommentsInfo onClick={this.toggleStatus} status={status} />
<ConfigureStreamContainer
status={status}
onClick={this.toggleStatus}
/>
</RestrictedContent>
</TabContent>
<Notification
@@ -310,8 +306,7 @@ const mapDispatchToProps = (dispatch) => ({
deleteAction: (item, action, user, itemType) => dispatch(deleteAction(item, action, user, itemType)),
appendItemArray: (item, property, value, addToFront, itemType) => dispatch(appendItemArray(item, property, value, addToFront, itemType)),
handleSignInDialog: () => dispatch(authActions.showSignInDialog()),
logout: () => dispatch(logout()),
updateStatus: status => dispatch(updateOpenStatus(status))
logout: () => dispatch(logout())
});
export default connect(mapStateToProps, mapDispatchToProps)(CommentStream);
+24 -10
View File
@@ -1,19 +1,33 @@
import coralApi from '../helpers/response';
/* Config Actions */
import * as actions from '../constants/config';
import {addNotification} from '../actions/notification';
/**
* 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';
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' ? OPEN_COMMENTS : CLOSE_COMMENTS}));
.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});
export const updateConfiguration = newConfig => (dispatch, getState) => {
const assetId = getState().items.get('assets')
.keySeq()
.toArray()[0];
dispatch(updateConfigRequest());
coralApi(`/asset/${assetId}/settings`, {method: 'PUT', body: newConfig})
.then(() => {
dispatch(addNotification('success', lang.t('successUpdateSettings')));
dispatch(updateConfigSuccess(newConfig));
})
.catch(error => dispatch(updateConfigFailure(error)));
};
+2 -7
View File
@@ -1,14 +1,9 @@
import coralApi from '../helpers/response';
import {fromJS} from 'immutable';
/* Item Actions */
/**
* Action name constants
*/
import {UPDATE_CONFIG} from '../constants/config';
export const ADD_ITEM = 'ADD_ITEM';
export const UPDATE_ITEM = 'UPDATE_ITEM';
export const UPDATE_SETTINGS = 'UPDATE_SETTINGS';
export const APPEND_ITEM_ARRAY = 'APPEND_ITEM_ARRAY';
/**
@@ -106,7 +101,7 @@ export function getStream (assetUrl) {
dispatch(addItem(action, 'actions'));
});
} else if (type === 'settings') {
dispatch({type: UPDATE_SETTINGS, config: fromJS(json[type])});
dispatch({type: UPDATE_CONFIG, config: fromJS(json[type])});
} else {
json[type].forEach(item => {
dispatch(addItem(item, type));
@@ -0,0 +1,9 @@
export const UPDATE_CONFIG_REQUEST = 'UPDATE_CONFIG_REQUEST';
export const UPDATE_CONFIG_SUCCESS = 'UPDATE_CONFIG_SUCCESS';
export const UPDATE_CONFIG_FAILURE = 'UPDATE_CONFIG_FAILURE';
export const UPDATE_CONFIG = 'UPDATE_CONFIG';
export const OPEN_COMMENTS = 'OPEN_COMMENTS';
export const CLOSE_COMMENTS = 'CLOSE_COMMENTS';
export const ADD_ITEM = 'ADD_ITEM';
+13 -16
View File
@@ -1,31 +1,28 @@
/* @flow */
import {Map} from 'immutable';
import * as actions from '../actions/config';
import * as actions from '../constants/config';
const initialState = Map({
features: Map({}),
status: 'open',
closedMessage: ''
moderation: null
});
export default (state = initialState, action) => {
switch(action.type) {
// Override config if worked
case actions.UPDATE_SETTINGS:
return action.config;
case actions.UPDATE_CONFIG:
return state
.merge(Map(action.config));
case actions.UPDATE_CONFIG_SUCCESS:
return state
.merge(Map(action.config));
case actions.OPEN_COMMENTS:
return state.set('status', 'open');
return state
.set('status', 'open');
case actions.CLOSE_COMMENTS:
return state.set('status', 'closed');
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.status) : state;
default:
return state;
}
+2
View File
@@ -1,5 +1,6 @@
{
"en": {
"successUpdateSettings": "The changes you have made have been applied to the comment stream on this article",
"successBioUpdate": "Your Bio has been updated",
"contentNotAvailable": "This content is not available",
"suspendedAccountMsg": "Your account is currently suspended. This means that you cannot Like, Flag, or write comments. Please contact moderator@fakeurl.com for more information",
@@ -13,6 +14,7 @@
}
},
"es": {
"successUpdateSettings": "La configuración de este articulo fue actualizada",
"successBioUpdate": "Tu bio fue actualizada",
"contentNotAvailable": "El contenido no se encuentra disponible",
"suspendedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes dar Like, Marcar o escribir commentarios. Por favor, contacta moderator@fakeurl for more information",
View File
+10
View File
@@ -77,6 +77,16 @@
background: #696969;
}
.type--green {
color: white;
background: #00897B;
}
.type--green:hover {
color: white;
background: #00a291;
}
.full {
width: 100%;
margin: 0;
+70
View File
@@ -0,0 +1,70 @@
.label {
position: relative;
display: inline-block;
}
.label input {
visibility: hidden;
position: absolute;
left: 7px;
bottom: 7px;
margin: 0;
padding: 0;
outline: none;
cursor: pointer;
opacity: 0;
}
.checkbox {
cursor: pointer;
}
.label input[type="checkbox"]:checked + .checkbox:before {
content: "\e834";
}
.label input[type="checkbox"] + .checkbox:before {
content: "\e835";
color: #717171;
}
.label.type--green input[type="checkbox"] + .checkbox:before {
color: #00a291;
}
.label input[type="checkbox"] + .checkbox:before {
position: absolute;
left: 4px;
top: 0px;
width: 18px;
height: 18px;
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
vertical-align: -6px;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
-moz-osx-font-smoothing: grayscale;
-webkit-font-feature-settings: 'liga';
font-feature-settings: 'liga';
-webkit-transition: all .2s ease;
transition: all .2s ease;
z-index: 1;
}
.checkboxInfo {
display: inline-block;
max-width: 360px;
margin-left: 50px;
}
.checkboxInfo h4 {
margin: 0 0 5px;
}
+16
View File
@@ -0,0 +1,16 @@
import React from 'react';
import styles from './Checkbox.css';
export default ({name, cStyle = 'base', onChange, label, className, info, checked = 'false'}) => (
<label className={`${styles.label} ${styles[`type--${cStyle}`]} ${className}`} htmlFor={name}>
<input type="checkbox" id={name} name={name} onChange={onChange} checked={checked} />
<span className={styles.checkbox}></span>
{label && <span>{label}</span>}
{info && (
<div className={styles.checkboxInfo}>
<h4>{info.title}</h4>
<span>{info.description}</span>
</div>
)}
</label>
);
+1
View File
@@ -7,3 +7,4 @@ export {default as TabContent} from './components/TabContent';
export {default as Button} from './components/Button';
export {default as Spinner} from './components/Spinner';
export {default as Tooltip} from './components/Tooltip';
export {default as Checkbox} from './components/Checkbox';
+3 -1
View File
@@ -132,10 +132,12 @@ AssetSchema.statics.findOrCreateByUrl = (url) => Asset.findOneAndUpdate({url}, {
* @param {[type]} settings [description]
* @return {[type]} [description]
*/
AssetSchema.statics.overrideSettings = (id, settings) => Asset.update({id}, {
AssetSchema.statics.overrideSettings = (id, settings) => Asset.findOneAndUpdate({id}, {
$set: {
settings
}
}, {
new: true
});
/**
+2 -9
View File
@@ -82,18 +82,11 @@ router.post('/:asset_id/scrape', (req, res, next) => {
});
router.put('/:asset_id/settings', (req, res, next) => {
// Override the settings for the asset.
Asset
.overrideSettings(req.params.asset_id, req.body)
.then(() => {
res.status(204).end();
})
.catch((err) => {
next(err);
});
.then(() => res.status(204).end())
.catch((err) => next(err));
});
router.put('/:asset_id/status', (req, res, next) => {