@@ -58,7 +58,7 @@ const People = ({isFetching, commenters, ...props}) => {
hasResults
?
:
{lang.t('community.no-results')}
@@ -73,4 +73,4 @@ const People = ({isFetching, commenters, ...props}) => {
);
};
-export default People;
+export default Community;
diff --git a/client/coral-admin/src/containers/Community/CommunityContainer.js b/client/coral-admin/src/containers/Community/CommunityContainer.js
index 4f65303ff..e4263cc06 100644
--- a/client/coral-admin/src/containers/Community/CommunityContainer.js
+++ b/client/coral-admin/src/containers/Community/CommunityContainer.js
@@ -1,29 +1,14 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
-import {compose} from 'react-apollo';
-
-import {modUserFlaggedQuery} from 'coral-admin/src/graphql/queries';
-import {banUser, setUserStatus, suspendUser} from 'coral-admin/src/graphql/mutations';
-
import {
- fetchAccounts,
+ fetchCommenters,
updateSorting,
newPage,
- showBanUserDialog,
- hideBanUserDialog,
- showSuspendUserDialog,
- hideSuspendUserDialog
} from '../../actions/community';
-import CommunityMenu from './components/CommunityMenu';
-import BanUserDialog from './components/BanUserDialog';
-import SuspendUserDialog from './components/SuspendUserDialog';
-
-import People from './People';
-import FlaggedAccounts from './FlaggedAccounts';
+import Community from './Community';
class CommunityContainer extends Component {
-
constructor(props) {
super(props);
@@ -37,10 +22,6 @@ class CommunityContainer extends Component {
this.onNewPageHandler = this.onNewPageHandler.bind(this);
}
- componentWillMount() {
- this.props.fetchAccounts({});
- }
-
onKeyDownHandler(e) {
if (e.key === 'Enter') {
e.preventDefault();
@@ -57,13 +38,16 @@ class CommunityContainer extends Component {
search(query = {}) {
const {community} = this.props;
- this.props.fetchAccounts({
+ this.props.dispatch(fetchCommenters({
value: this.state.searchValue,
- field: community.fieldPeople,
- asc: community.ascPeople,
+ field: community.field,
+ asc: community.asc,
...query
- });
+ }));
+ }
+ componentDidMount() {
+ this.search();
}
onHeaderClickHandler(sort) {
@@ -76,66 +60,19 @@ class CommunityContainer extends Component {
this.search({page});
}
- getTabContent(searchValue, props) {
- const {community, data} = props;
- const activeTab = props.route.path === ':id' ? 'flagged' : props.route.path;
-
- if (activeTab === 'people') {
- return (
-
- );
- }
-
- return (
-
-
-
-
-
- );
- }
-
render() {
const {searchValue} = this.state;
-
- const tab = this.getTabContent(searchValue, this.props);
-
+ const {community} = this.props;
return (
-
+
);
}
}
@@ -144,18 +81,4 @@ const mapStateToProps = state => ({
community: state.community.toJS()
});
-const mapDispatchToProps = dispatch => ({
- fetchAccounts: query => dispatch(fetchAccounts(query)),
- showBanUserDialog: (user) => dispatch(showBanUserDialog(user)),
- hideBanUserDialog: () => dispatch(hideBanUserDialog(false)),
- showSuspendUserDialog: (user) => dispatch(showSuspendUserDialog(user)),
- hideSuspendUserDialog: () => dispatch(hideSuspendUserDialog())
-});
-
-export default compose(
- connect(mapStateToProps, mapDispatchToProps),
- modUserFlaggedQuery,
- banUser,
- setUserStatus,
- suspendUser
-)(CommunityContainer);
+export default connect(mapStateToProps)(CommunityContainer);
diff --git a/client/coral-admin/src/containers/Community/CommunityLayout.js b/client/coral-admin/src/containers/Community/CommunityLayout.js
deleted file mode 100644
index d6022fed9..000000000
--- a/client/coral-admin/src/containers/Community/CommunityLayout.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react';
-
-const CommunityLayout = props => (
-
- {props.children}
-
-);
-
-export default CommunityLayout;
diff --git a/client/coral-admin/src/containers/Community/FlaggedAccounts.js b/client/coral-admin/src/containers/Community/FlaggedAccounts.js
deleted file mode 100644
index 1a6d349c8..000000000
--- a/client/coral-admin/src/containers/Community/FlaggedAccounts.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import React from 'react';
-
-import I18n from 'coral-framework/modules/i18n/i18n';
-import translations from 'coral-admin/src/translations.json';
-const lang = new I18n(translations);
-
-import styles from './Community.css';
-
-import Loading from './Loading';
-import EmptyCard from '../../components/EmptyCard';
-import User from './components/User';
-
-const FlaggedAccounts = ({...props}) => {
- const {commenters, isFetching} = props;
- const hasResults = !isFetching && commenters && !!commenters.length;
-
- return (
-
-
- { isFetching && }
- {
- hasResults
- ? commenters.map((commenter, index) => {
- if (commenter.status === 'PENDING' && commenter.actions.length > 0) {
- return ;
- }
- return null;
- })
- : {lang.t('community.no-flagged-accounts')}
- }
-
-
- );
-};
-
-export default FlaggedAccounts;
diff --git a/client/coral-admin/src/containers/Community/Table.js b/client/coral-admin/src/containers/Community/Table.js
index aacc9c82f..480ce72bf 100644
--- a/client/coral-admin/src/containers/Community/Table.js
+++ b/client/coral-admin/src/containers/Community/Table.js
@@ -77,4 +77,4 @@ class Table extends Component {
}
}
-export default connect(state => ({commenters: state.community.get('accounts')}))(Table);
+export default connect(state => ({commenters: state.community.get('commenters')}))(Table);
diff --git a/client/coral-admin/src/containers/Community/components/ActionButton.js b/client/coral-admin/src/containers/Community/components/ActionButton.js
deleted file mode 100644
index 6352480fd..000000000
--- a/client/coral-admin/src/containers/Community/components/ActionButton.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-import styles from '../Community.css';
-import BanUserButton from '../../../components/BanUserButton';
-import {Button} from 'coral-ui';
-import {menuActionsMap} from '../../../containers/ModerationQueue/helpers/moderationQueueActionsMap';
-
-const ActionButton = ({type = '', user, ...props}) => {
- if (type === 'BAN') {
- return
props.showBanUserDialog(user)} />;
- }
-
- return (
- {
- type === 'APPROVE' ? props.approveUser({userId: user.id}) : props.showSuspendUserDialog({user: user});
- }}
- >{menuActionsMap[type].text}
- );
-};
-
-export default ActionButton;
diff --git a/client/coral-admin/src/containers/Community/components/BanUserDialog.css b/client/coral-admin/src/containers/Community/components/BanUserDialog.css
deleted file mode 100644
index a46b9da32..000000000
--- a/client/coral-admin/src/containers/Community/components/BanUserDialog.css
+++ /dev/null
@@ -1,164 +0,0 @@
-.dialog {
- border: none;
- box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
- width: 500px;
- top: 50%;
- transform: translateY(-50%);
- height: 184px;
- padding: 20px;
-
- h2 {
- color: black;
- font-size: 1.76em;
- font-weight: 500;
- margin: 0;
- }
-
- h3 {
- color: black;
- font-size: 1.4em;
- font-weight: 500;
- margin: 0;
- }
-}
-
-.textField {
- margin-top: 15px;
-}
-
-.textField label {
- font-size: 1.08em;
- font-weight: bold;
- margin-bottom: 5px;
-}
-
-.textField input {
- width: 100%;
- display: block;
- border: none;
- outline: none;
- border: 1px solid rgba(0,0,0,.12);
- padding: 10px 6px;
- box-sizing: border-box;
- border-radius: 2px;
- margin: 5px auto;
-}
-
-.footer {
- margin: 20px auto 10px;
- text-align: center;
-}
-
-.footer span {
- display: block;
- margin-bottom: 5px;
-}
-
-.footer a {
- color: #2c69b6;
- cursor: pointer;
- margin: 0 5px;
-}
-
-.socialConnections {
- margin-bottom: 20px;
-}
-
-.signInButton {
- margin-top: 10px;
-}
-
-.close {
- font-size: 20px;
- line-height: 14px;
- top: 10px;
- right: 10px;
- position: absolute;
- display: block;
- font-weight: bold;
- color: #363636;
- cursor: pointer;
-}
-
-.close:hover {
- color: #6b6b6b;
-}
-
-input.error{
- border: solid 2px #f44336;
-}
-
-.errorMsg, .hint {
- color: grey;
- font-weight: 600;
- padding: 3px 0 16px;
-}
-
-.alert {
- padding: 10px;
- margin-bottom: 20px;
- border-radius: 2px;
-}
-
-.alert--success {
- border: solid 1px #1ec00e;
- background: #cbf1b8;
- color: #006900;
-}
-
-.alert--error {
- background: #FFEBEE;
- color: #B71C1C;
-}
-
-.userBox a {
- color: #2c69b6;
- cursor: pointer;
- margin: 0px;
-}
-
-.attention {
- display: inline-block;
- width: 15px;
- height: 15px;
- background: #B71C1C;
- color: #FFEBEE;
- font-weight: bolder;
- padding: 4px;
- vertical-align: middle;
- border-radius: 20px;
- box-sizing: border-box;
- font-size: 9px;
- line-height: 7px;
- text-align: center;
- margin-right: 5px;
-}
-
-.action {
- margin-top: 15px;
-}
-
-.passwordRequestSuccess {
- border: 1px solid green;
- background-color: lightgreen;
- padding: 10px;
-}
-
-.passwordRequestFailure {
- border: 1px solid orange;
- background-color: 1px solid coral;
- padding: 10px;
-}
-
-.cancel {
- margin-right: 10px;
- width: 47%;
-}
-
-.ban {
- width: 47%;
-}
-
-.buttons {
- margin: 20px 0;
-}
diff --git a/client/coral-admin/src/containers/Community/components/BanUserDialog.js b/client/coral-admin/src/containers/Community/components/BanUserDialog.js
deleted file mode 100644
index affffcd54..000000000
--- a/client/coral-admin/src/containers/Community/components/BanUserDialog.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import React, {PropTypes} from 'react';
-import {Dialog} from 'coral-ui';
-import styles from './BanUserDialog.css';
-
-import Button from 'coral-ui/components/Button';
-
-import I18n from 'coral-framework/modules/i18n/i18n';
-import translations from 'coral-admin/src/translations.json';
-
-const lang = new I18n(translations);
-
-const BanUserDialog = ({open, handleClose, handleBanUser, user}) => (
-
- ×
-
-
-
{lang.t('community.ban_user')}
-
-
-
{lang.t('community.are_you_sure', user.username)}
- {lang.t('community.note')}
-
-
-
- {lang.t('community.cancel')}
-
- {
- handleBanUser({userId: user.id}).then(() => {
- handleClose();
- });
- }}
- raised>
- {lang.t('community.yes_ban_user')}
-
-
-
-
-);
-
-BanUserDialog.propTypes = {
- handleBanUser: PropTypes.func.isRequired,
- handleClose: PropTypes.func.isRequired,
- user: PropTypes.object.isRequired,
-};
-
-export default BanUserDialog;
diff --git a/client/coral-admin/src/containers/Community/components/CommunityMenu.js b/client/coral-admin/src/containers/Community/components/CommunityMenu.js
deleted file mode 100644
index 9a51798ec..000000000
--- a/client/coral-admin/src/containers/Community/components/CommunityMenu.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react';
-
-import styles from './styles.css';
-
-import I18n from 'coral-framework/modules/i18n/i18n';
-import translations from 'coral-admin/src/translations.json';
-
-import {Link} from 'react-router';
-
-const lang = new I18n(translations);
-
-const CommunityMenu = () => {
- const flaggedPath = '/admin/community/flagged';
- const peoplePath = '/admin/community/people';
- return (
-
-
-
-
- {lang.t('community.flaggedaccounts')}
-
-
- {lang.t('community.people')}
-
-
-
-
- );
-};
-
-export default CommunityMenu;
diff --git a/client/coral-admin/src/containers/Community/components/SuspendUserDialog.js b/client/coral-admin/src/containers/Community/components/SuspendUserDialog.js
deleted file mode 100644
index 44f38f796..000000000
--- a/client/coral-admin/src/containers/Community/components/SuspendUserDialog.js
+++ /dev/null
@@ -1,115 +0,0 @@
-import React, {Component, PropTypes} from 'react';
-
-import {Dialog, Button} from 'coral-ui';
-import styles from './SuspendUserDialog.css';
-
-import I18n from 'coral-framework/modules/i18n/i18n';
-import translations from 'coral-admin/src/translations.json';
-
-const lang = new I18n(translations);
-
-const stages = [
- {
- title: 'suspenduser.title_0',
- description: 'suspenduser.description_0',
- options: {
- 'j': 'suspenduser.no_cancel',
- 'k': 'suspenduser.yes_suspend'
- }
- },
- {
- title: 'suspenduser.title_1',
- description: 'suspenduser.description_1',
- options: {
- 'j': 'bandialog.cancel',
- 'k': 'suspenduser.send'
- }
- }
-];
-
-class SuspendUserDialog extends Component {
-
- state = {email: '', stage: 0}
-
- static propTypes = {
- stage: PropTypes.number,
- handleClose: PropTypes.func.isRequired,
- suspendUser: PropTypes.func.isRequired
- }
-
- componentDidMount() {
- this.setState({email: lang.t('suspenduser.email'), about: lang.t('suspenduser.username')});
- }
-
- /*
- * When an admin clicks to suspend a user a dialog is shown, this function
- * handles the possible actions for that dialog.
- */
- onActionClick = (stage, menuOption) => () => {
- const {suspendUser, user} = this.props;
- const {stage} = this.state;
-
- const cancel = this.props.onClose;
- const next = () => this.setState({stage: stage + 1});
- const suspend = () => {
- suspendUser({userId: user.user.id, message: this.state.email})
- .then(() => {
- this.props.handleClose();
- });
- };
-
- const suspendModalActions = [
- [ cancel, next ],
- [ cancel, suspend ]
- ];
- return suspendModalActions[stage][menuOption]();
- }
-
- onEmailChange = (e) => {
- this.setState({email: e.target.value});
- }
-
- render () {
- const {open, handleClose} = this.props;
- const {stage} = this.state;
-
- return
-
- {lang.t(stages[stage].title, lang.t('suspenduser.username'))}
-
-
-
- {lang.t(stages[stage].description, lang.t('suspenduser.username'))}
-
- {
- stage === 1 &&
-
-
{lang.t('suspenduser.write_message')}
-
-
-
- }
-
- {Object.keys(stages[stage].options).map((key, i) => (
-
- {lang.t(stages[stage].options[key], lang.t('suspenduser.username'))}
-
- ))}
-
-
- ;
- }
-}
-
-export default SuspendUserDialog;
diff --git a/client/coral-admin/src/containers/Community/components/User.js b/client/coral-admin/src/containers/Community/components/User.js
deleted file mode 100644
index 71598e352..000000000
--- a/client/coral-admin/src/containers/Community/components/User.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import React from 'react';
-import styles from '../Community.css';
-
-import ActionButton from './ActionButton';
-
-import I18n from 'coral-framework/modules/i18n/i18n';
-import translations from '../../../translations.json';
-
-const lang = new I18n(translations);
-
-// Render a single user for the list
-const User = props => {
- const {user, modActionButtons} = props;
- let userStatus = user.status;
-
- // Do not display unless the user status is 'pending' or 'banned'.
- // This means that they have already been reviewed and approved.
- return (userStatus === 'PENDING' || userStatus === 'BANNED') &&
-
-
-
-
-
- {user.username}
-
-
-
-
-
-
-
-
- flag Flags({ user.actions.length }) :
- { user.action_summaries.map(
- (action, i ) => {
- return
- {lang.t(`community.${action.reason}`)} ({action.count})
- ;
- }
- )}
-
-
- { user.action_summaries.map(
- (action_sum, i ) => {
- return
-
- {lang.t(`community.${action_sum.reason}`)} ({action_sum.count})
-
- {user.actions.map(
-
- // find the action by action_sum.reason
- (action, j) => {
- if (action.reason === action_sum.reason) {
- return
- {action.user && action.user.username}: {action.message ? action.message : 'n/a'}
-
;
- }
- return null;
- }
- )}
-
;
- }
- )}
-
-
-
-
-
- {modActionButtons.map((action, i) =>
-
- )}
-
-
-
-
- ;
-};
-
-export default User;
diff --git a/client/coral-admin/src/containers/Community/components/styles.css b/client/coral-admin/src/containers/Community/components/styles.css
deleted file mode 100644
index 2cd3f7b50..000000000
--- a/client/coral-admin/src/containers/Community/components/styles.css
+++ /dev/null
@@ -1,339 +0,0 @@
-@custom-media --big-viewport (min-width: 780px);
-
-.listContainer {
- max-width: 860px;
- margin: 0 auto;
-}
-
-.tabBar {
- background-color: rgba(44, 44, 44, 0.89);
- z-index: 5;
-}
-
-.tab {
- flex: 1;
- color: white;
- text-transform: capitalize;
- font-weight: 500;
- font-size: 15px;
- letter-spacing: 1px;
- transition: border-bottom 200ms;
-}
-
-.active {
- color: white;
- box-sizing: border-box;
- border-bottom: solid 5px #F36451;
-}
-
-.active > span {
- color: white;
-}
-
-.active:after {
- background: transparent !important;
-}
-
-.showShortcuts {
- position: absolute;
- right: 130px;
- display: flex;
- align-items: center;
- font-size: 13px;
-
-span {
- margin-left: 7px;
-}
-}
-
-@media (--big-viewport) {
- .tab {
- flex: none;
- }
-}
-
-.notFound {
- position: relative;
- margin: 20px auto;
- text-align: center;
- padding: 68px 45px;
- vertical-align: middle;
- min-width: 500px;
-
- a {
- color: rgb(244, 126, 107);
- font-weight: 500;
-
- &.goToStreams {
- position: absolute;
- right: 10px;
- bottom: 10px;
- }
- }
-}
-
-.header {
- background-color: #2c2c2c;
- color: white;
- margin-bottom: -1px;
-
- .settingsButton {
- i {
- vertical-align: middle;
- margin-left: 10px;
- margin-top: -4px;
- }
- }
-
- .moderateAsset {
- a {
- -webkit-box-flex: 1;
- -ms-flex: 1;
- flex: 1;
- color: white;
- text-transform: capitalize;
- font-weight: 500;
- font-size: 15px;
- letter-spacing: 1px;
- transition: opacity 200ms;
- opacity: 1;
-
- &:hover {
- opacity: .8;
- cursor: pointer;
- }
-
- &:first-child {
- text-align: left;
- }
-
- &:nth-child(2) {
- text-align: center;
- }
-
- &:last-child {
- text-align: right;
- }
- }
- }
-}
-
-
-@custom-media --big-viewport (min-width: 780px);
-
-.list {
- padding: 8px 0;
- list-style: none;
- display: block;
-
- &.singleView .listItem {
- display: none;
- }
-
- &.singleView .listItem.activeItem {
- display: block;
- height: 100%;
- font-size: 1.5em;
- line-height: 1.5em;
- border: none;
-
- .actions {
- position: fixed;
- bottom: 60px;
- left: 25%;
- display: flex;
- justify-content: space-around;
- width: 50%;
- margin: 0;
- }
-
- .actionButton {
- transform: scale(1.4);
- }
- }
-}
-
-.listItem {
- border-bottom: 1px solid #e0e0e0;
- font-size: 18px;
- width: 100%;
- max-width: 660px;
- min-width: 400px;
- margin: 0 auto;
- position: relative;
- transition: all 200ms;
- padding: 10px 0 0;
- min-height: 220px;
-
- .container {
- padding: 0 14px;
- min-height: 180px;
- }
-
- &:hover {
- box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
- }
-
- &:last-child {
- border-bottom: none;
- }
-
- .context {
- a {
- color: #f36451;
- text-decoration: underline;
- float: right;
- }
- }
-
- .sideActions {
- position: absolute;
- right: 0;
- height: 100%;
- top: 0;
- padding: 40px 18px;
- box-sizing: border-box;
- }
-
- .itemHeader {
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- .author {
- min-width: 230px;
- display: flex;
- align-items: center;
- }
- }
-
- .itemBody {
- display: flex;
- justify-content: space-between;
- }
-
- .avatar {
- margin-right: 16px;
- height: 40px;
- width: 40px;
- border-radius: 50%;
- background-color: #757575;
- font-size: 40px;
- color: #fff;
- }
-
- .created {
- color: #666;
- font-size: 13px;
- margin-left: 40px;
- }
-
- .actionButton {
- transform: scale(.8);
- margin: 0;
- }
-
- .body {
- margin-top: 0px;
- flex: 1;
- color: black;
- max-width: 500px;
- word-wrap: break-word;
- font-weight: 300;
- }
-
- .flagged {
- color: rgba(255, 0, 0, .5);
- padding-top: 15px;
- padding-left: 10px;
- }
-
- .flagCount{
- font-size: 12px;
- color: #d32f2f;
- }
-
-}
-
-.empty {
- color: #444;
- margin-top: 50px;
- text-align: center;
-}
-
-
-@media (--big-viewport) {
- .listItem {
- border: 1px solid #e0e0e0;
- margin-bottom: 30px;
-
- &:last-child {
- border-bottom: 1px solid #e0e0e0;
- }
-
- &.activeItem {
- border: 2px solid #333;
- }
- }
-
-}
-
-.hasLinks {
- color: #f00;
- text-align: right;
- display: flex;
- align-items: center;
-
- i {
- margin-right: 5px;
- }
-}
-
-.banned {
- color: #f00;
- text-align: left;
- display: flex;
- align-items: center;
-
- i {
- margin-right: 5px;
- }
-}
-
-.ban {
- display: block;
- text-align: center;
- margin-top: 5px;
-}
-
-.Comment {
- .moderateArticle {
- font-size: 12px;
- a {
- display: inline-block;
- color: #679af3;
- text-decoration: none;
- font-size: 1em;
- font-weight: 400;
- letter-spacing: .5px;
- font-size: 12px;
- margin-left: 10px;
-
- &:hover {
- text-decoration: underline;
- opacity: .9;
- cursor: pointer;
- }
- }
- }
-}
-
-.flagBox {
- max-width: 480px;
- border-top: 1px solid rgba(66, 66, 66, 0.12);
- h3 {
- font-size: 14px;
- margin: 0;
- font-weight: 500;
- }
-}
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
index a6d3527c7..61352b921 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js
@@ -163,6 +163,7 @@ class ModerationContainer extends Component {
activeTab={activeTab}
singleView={moderation.singleView}
selectedIndex={this.state.selectedIndex}
+ bannedWords={settings.wordlist.banned}
suspectWords={settings.wordlist.suspect}
showBanUserDialog={props.showBanUserDialog}
acceptComment={props.acceptComment}
diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
index b90b59f81..79b4ce7e4 100644
--- a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
+++ b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js
@@ -24,6 +24,7 @@ const ModerationQueue = ({comments, selectedIndex, commentCount, singleView, loa
commentType={activeTab}
selected={i === selectedIndex}
suspectWords={props.suspectWords}
+ bannedWords={props.bannedWords}
actions={actionsMap[status]}
showBanUserDialog={props.showBanUserDialog}
acceptComment={props.acceptComment}
@@ -47,6 +48,7 @@ const ModerationQueue = ({comments, selectedIndex, commentCount, singleView, loa
};
ModerationQueue.propTypes = {
+ bannedWords: PropTypes.arrayOf(PropTypes.string).isRequired,
suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired,
currentAsset: PropTypes.object,
showBanUserDialog: PropTypes.func.isRequired,
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
index f68bab2b7..3dd5526fe 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js
@@ -19,21 +19,22 @@ const lang = new I18n(translations);
const Comment = ({actions = [], ...props}) => {
const links = linkify.getMatches(props.comment.body);
+ const linkText = links ? links.map(link => link.raw) : [];
const actionSummaries = props.comment.action_summaries;
return (
-
+
-
- {props.comment.user.name}
-
-
- {timeago().format(props.comment.created_at || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}
-
- props.showBanUserDialog(props.comment.user, props.comment.id, props.comment.status !== 'REJECTED')} />
-
-
+
+ {props.comment.user.name}
+
+
+ {timeago().format(props.comment.created_at || (Date.now() - props.index * 60 * 1000), lang.getLocale().replace('-', '_'))}
+
+
props.showBanUserDialog(props.comment.user, props.comment.id, props.comment.status !== 'REJECTED')} />
+
+
{props.comment.user.status === 'banned' ?
@@ -49,9 +50,9 @@ const Comment = ({actions = [], ...props}) => {
-
-
-
+
{links ?
Contains Link : null}
@@ -77,6 +78,7 @@ Comment.propTypes = {
acceptComment: PropTypes.func.isRequired,
rejectComment: PropTypes.func.isRequired,
suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired,
+ bannedWords: PropTypes.arrayOf(PropTypes.string).isRequired,
currentAsset: PropTypes.object,
comment: PropTypes.shape({
body: PropTypes.string.isRequired,
@@ -92,9 +94,4 @@ Comment.propTypes = {
})
};
-const linkStyles = {
- backgroundColor: 'rgb(255, 219, 135)',
- padding: '1px 2px'
-};
-
export default Comment;
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js b/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
index b8160e21b..e428f6a52 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
+++ b/client/coral-admin/src/containers/ModerationQueue/components/ModerationMenu.js
@@ -1,35 +1,53 @@
-import React, {PropTypes} from 'react';
+import React, { PropTypes } from 'react';
import CommentCount from './CommentCount';
import styles from './styles.css';
-import {SelectField, Option} from 'react-mdl-selectfield';
+import { SelectField, Option } from 'react-mdl-selectfield';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from 'coral-admin/src/translations.json';
-import {Link} from 'react-router';
+import { Link } from 'react-router';
const lang = new I18n(translations);
-const ModerationMenu = ({asset, premodCount, rejectedCount, flaggedCount, selectSort, sort}) => {
- const premodPath = asset ? `/admin/moderate/premod/${asset.id}` : '/admin/moderate/premod';
- const rejectPath = asset ? `/admin/moderate/rejected/${asset.id}` : '/admin/moderate/rejected';
- const flagPath = asset ? `/admin/moderate/flagged/${asset.id}` : '/admin/moderate/flagged';
+const ModerationMenu = (
+ { asset, premodCount, rejectedCount, flaggedCount, selectSort, sort }
+) => {
+ const premodPath = asset
+ ? `/admin/moderate/premod/${asset.id}`
+ : '/admin/moderate/premod';
+ const rejectPath = asset
+ ? `/admin/moderate/rejected/${asset.id}`
+ : '/admin/moderate/rejected';
+ const flagPath = asset
+ ? `/admin/moderate/flagged/${asset.id}`
+ : '/admin/moderate/flagged';
+
return (
-
+
-
+
-
+
{lang.t('modqueue.premod')}
-
- {lang.t('modqueue.rejected')}
-
-
+
{lang.t('modqueue.flagged')}
+
+ {lang.t('modqueue.rejected')}
+
selectSort(sort)}>
Newest First
diff --git a/client/coral-admin/src/containers/ModerationQueue/components/styles.css b/client/coral-admin/src/containers/ModerationQueue/components/styles.css
index 9576045de..51086dfd4 100644
--- a/client/coral-admin/src/containers/ModerationQueue/components/styles.css
+++ b/client/coral-admin/src/containers/ModerationQueue/components/styles.css
@@ -170,7 +170,7 @@ span {
border-bottom: 1px solid #e0e0e0;
font-size: 18px;
width: 100%;
- max-width: 660px;
+ max-width: 700px;
min-width: 400px;
margin: 0 auto;
position: relative;
@@ -192,7 +192,7 @@ span {
}
&.selected {
- max-width: 670px;
+ max-width: 720px;
max-height: 410px;
}
@@ -216,10 +216,12 @@ span {
justify-content: space-between;
.author {
- font-weight: 600;
+ font-weight: 300;
min-width: 230px;
display: flex;
align-items: center;
+ color: #262626;
+ font-size: 16px;
}
}
@@ -242,10 +244,11 @@ span {
}
.created {
- color: #666;
- font-size: 13px;
+ color: #262626;
+ font-size: 14px;
margin-left: 15px;
line-height: 1px;
+ font-weight: 300;
}
.actionButton {
@@ -260,6 +263,7 @@ span {
max-width: 500px;
word-wrap: break-word;
font-weight: 300;
+ font-size: 16px;
}
.flagged {
diff --git a/client/coral-admin/src/graphql/mutations/index.js b/client/coral-admin/src/graphql/mutations/index.js
index 7ca1b078f..884e30e0c 100644
--- a/client/coral-admin/src/graphql/mutations/index.js
+++ b/client/coral-admin/src/graphql/mutations/index.js
@@ -1,7 +1,6 @@
import {graphql} from 'react-apollo';
import SET_USER_STATUS from './setUserStatus.graphql';
import SET_COMMENT_STATUS from './setCommentStatus.graphql';
-import SUSPEND_USER from './suspendUser.graphql';
export const banUser = graphql(SET_USER_STATUS, {
props: ({mutate}) => ({
@@ -10,40 +9,11 @@ export const banUser = graphql(SET_USER_STATUS, {
variables: {
userId,
status: 'BANNED'
- },
- refetchQueries: ['Users']
+ }
});
}}),
});
-export const setUserStatus = graphql(SET_USER_STATUS, {
- props: ({mutate}) => ({
- approveUser: ({userId}) => {
- return mutate({
- variables: {
- userId,
- status: 'APPROVED'
- },
- refetchQueries: ['Users']
- });
- }
- })
-});
-
-export const suspendUser = graphql(SUSPEND_USER, {
- props: ({mutate}) => ({
- suspendUser: ({userId, message}) => {
- return mutate({
- variables: {
- userId,
- message
- },
- refetchQueries: ['Users']
- });
- }
- })
-});
-
export const setCommentStatus = graphql(SET_COMMENT_STATUS, {
props: ({mutate}) => ({
acceptComment: ({commentId}) => {
diff --git a/client/coral-admin/src/graphql/mutations/suspendUser.graphql b/client/coral-admin/src/graphql/mutations/suspendUser.graphql
deleted file mode 100644
index 0d56a3cc0..000000000
--- a/client/coral-admin/src/graphql/mutations/suspendUser.graphql
+++ /dev/null
@@ -1,7 +0,0 @@
-mutation suspendUser($userId: ID!, $message: String) {
- suspendUser(id: $userId, message: $message) {
- errors {
- translation_key
- }
- }
-}
diff --git a/client/coral-admin/src/graphql/queries/index.js b/client/coral-admin/src/graphql/queries/index.js
index 291ea6bc5..13164bca2 100644
--- a/client/coral-admin/src/graphql/queries/index.js
+++ b/client/coral-admin/src/graphql/queries/index.js
@@ -2,7 +2,6 @@ import {graphql} from 'react-apollo';
import MOD_QUEUE_QUERY from './modQueueQuery.graphql';
import MOD_QUEUE_LOAD_MORE from './loadMore.graphql';
-import MOD_USER_FLAGGED_QUERY from './modUserFlaggedQuery.graphql';
import METRICS from './metricsQuery.graphql';
export const modQueueQuery = graphql(MOD_QUEUE_QUERY, {
@@ -67,8 +66,6 @@ export const loadMore = (fetchMore) => ({limit, cursor, sort, tab, asset_id}) =>
});
};
-export const modUserFlaggedQuery = graphql(MOD_USER_FLAGGED_QUERY);
-
export const modQueueResort = (id, fetchMore) => (sort) => {
return fetchMore({
query: MOD_QUEUE_QUERY,
diff --git a/client/coral-admin/src/graphql/queries/modUserFlaggedQuery.graphql b/client/coral-admin/src/graphql/queries/modUserFlaggedQuery.graphql
deleted file mode 100644
index 98f2191eb..000000000
--- a/client/coral-admin/src/graphql/queries/modUserFlaggedQuery.graphql
+++ /dev/null
@@ -1,26 +0,0 @@
-query Users ($n: ACTION_TYPE) {
- users (query:{action_type: $n}){
- id
- username
- status
- roles
- actions{
- id
- created_at
- ... on FlagAction {
- reason
- message
- user {
- id
- username
- }
- }
- }
- action_summaries {
- count
- ... on FlagActionSummary {
- reason
- }
- }
- }
-}
diff --git a/client/coral-admin/src/reducers/community.js b/client/coral-admin/src/reducers/community.js
index d051ae0ff..367e67b2a 100644
--- a/client/coral-admin/src/reducers/community.js
+++ b/client/coral-admin/src/reducers/community.js
@@ -6,88 +6,58 @@ import {
FETCH_COMMENTERS_SUCCESS,
SORT_UPDATE,
SET_ROLE,
- SET_COMMENTER_STATUS,
- SHOW_BANUSER_DIALOG,
- HIDE_BANUSER_DIALOG,
- SHOW_SUSPENDUSER_DIALOG,
- HIDE_SUSPENDUSER_DIALOG
+ SET_COMMENTER_STATUS
} from '../constants/community';
const initialState = Map({
community: Map(),
- isFetchingPeople: false,
- errorPeople: '',
- accounts: [],
- fieldPeople: 'created_at',
- ascPeople: false,
- totalPagesPeople: 0,
- pagePeople: 0,
- user: Map({}),
- banDialog: false,
- suspendDialog: false
+ isFetching: false,
+ error: '',
+ commenters: [],
+ field: 'created_at',
+ asc: false,
+ totalPages: 0,
+ page: 0
});
export default function community (state = initialState, action) {
switch (action.type) {
case FETCH_COMMENTERS_REQUEST :
return state
- .set('isFetchingPeople', true);
+ .set('isFetching', true);
case FETCH_COMMENTERS_FAILURE :
return state
- .set('isFetchingPeople', false)
- .set('errorPeople', action.error);
-
+ .set('isFetching', false)
+ .set('error', action.error);
case FETCH_COMMENTERS_SUCCESS : {
- const {accounts, type, page, count, limit, totalPages, ...rest} = action; // eslint-disable-line
+ const {commenters, type, ...rest} = action; // eslint-disable-line
return state
.merge({
- isFetchingPeople: false,
- errorPeople: '',
- pagePeople: page,
- countPeople: count,
- limitPeople: limit,
- totalPagesPeople: totalPages,
+ isFetching: false,
+ error: '',
...rest
})
- .set('accounts', accounts); // Sets to normal array
+ .set('commenters', commenters); // Sets to normal array
}
case SET_ROLE : {
- const commenters = state.get('accounts');
+ const commenters = state.get('commenters');
const idx = commenters.findIndex(el => el.id === action.id);
commenters[idx].roles[0] = action.role;
- return state.set('accounts', commenters.map(id => id));
+ return state.set('commenters', commenters.map(id => id));
}
case SET_COMMENTER_STATUS: {
- const commenters = state.get('accounts');
+ const commenters = state.get('commenters');
const idx = commenters.findIndex(el => el.id === action.id);
commenters[idx].status = action.status;
- return state.set('accounts', commenters.map(id => id));
+ return state.set('commenters', commenters.map(id => id));
}
case SORT_UPDATE :
return state
- .set('fieldPeople', action.sort.field)
- .set('ascPeople', !state.get('ascPeople'));
- case HIDE_BANUSER_DIALOG:
- return state
- .set('banDialog', false);
- case SHOW_BANUSER_DIALOG:
- return state
- .merge({
- user: Map(action.user),
- banDialog: true
- });
- case HIDE_SUSPENDUSER_DIALOG:
- return state
- .set('suspendDialog', false);
- case SHOW_SUSPENDUSER_DIALOG:
- return state
- .merge({
- user: Map(action.user),
- suspendDialog: true
- });
+ .set('field', action.sort.field)
+ .set('asc', !state.get('asc'));
default :
return state;
}
diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json
index 13691a1fc..d39c8f175 100644
--- a/client/coral-admin/src/translations.json
+++ b/client/coral-admin/src/translations.json
@@ -17,20 +17,7 @@
"active": "Active",
"banned": "Banned",
"banned-user": "Banned User",
- "loading": "Loading results",
- "flaggedaccounts": "Flagged Usernames",
- "people": "People",
- "no-flagged-accounts": "The Account Flags queue is currently empty.",
- "I don't like this username": "I don't like this username",
- "This user is impersonating": "Impersonation",
- "This looks like an ad/marketing": "Spam/Ads",
- "This username is offensive": "Offensive",
- "Other": "Other",
- "ban_user": "Ban User?",
- "are_you_sure": "Are you sure you would like to ban {0}?",
- "note": "Note: Banning this user will not let them edit, comment or remove anything.",
- "cancel": "Cancel",
- "yes_ban_user": "Yes, Ban User"
+ "loading": "Loading results"
},
"modqueue": {
"likes": "likes",
@@ -117,9 +104,8 @@
"yes_ban_user": "Yes, Ban User"
},
"suspenduser": {
- "title": "Suspend a user",
- "title_0": "We noticed you rejected a username",
- "description_0": "Would you like to temporarily ban this user because of their {0}? Doing so will temporarily hide their comments until they rewrite their {0}.",
+ "title_0": "We noticed you rejected a {0}",
+ "description_0": "Would you like to temporarily ban this user becuase of their {0}? Doing so will temporarily hide their comments until they rewrite their {0}.",
"title_1": "Notify the user of their temporary suspension",
"description_1": "Suspending this user will temporarily disable their account and hide all of their comments on the site.",
"no_cancel": "No, cancel",
@@ -128,7 +114,7 @@
"bio": "bio",
"username": "username",
"email_subject": "Your account has been suspended",
- "email": "Another member of the community recently flagged your username for review. Because of its content your user was rejected. This means you can no longer comment, like, or flag content until you rewrite your username. Please e-mail us if you have any questions or concerns.",
+ "email": "Another member of the community recently flagged your {0} for review. Because of its content your {0} was rejected. This means you can no longer comment, like, or flag content until you rewrite your {0}. Please e-mail moderator@newsorg.com if you have any questions or concerns.",
"write_message": "Write a message"
},
"dashboard": {
@@ -203,7 +189,8 @@
"username": "nombre de usuario",
"email_subject": "Su cuenta ha sido suspendida temporalmente",
"email": "Una persona de la comunidad recientemente marcó su nombre de usuario para ser revisado. Por su contenido, el nombre de usuario ha sido rechazado. Esto quiere decir que no puede comentar, gustar o marcar contenido hasta que modifique su nombre de usuario. Por favor, envienos un correo si tiene alguna pregunta o comentario.",
- "write_message": "Escribir un mensaje"
+ "write_message": "Escribir un mensaje",
+ "loading": "Cargando resultados"
},
"modqueue": {
"likes": "gustos",
diff --git a/client/coral-embed-stream/src/Comment.css b/client/coral-embed-stream/src/Comment.css
index 6966d82ab..01022ae66 100644
--- a/client/coral-embed-stream/src/Comment.css
+++ b/client/coral-embed-stream/src/Comment.css
@@ -1,9 +1,10 @@
.Reply {
position: relative;
+ margin-bottom: 15px;
}
.Comment {
-
+ margin-bottom: 15px;
}
.pendingComment {
diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js
index 4722a44b1..a3d2a24a9 100644
--- a/client/coral-embed-stream/src/Embed.js
+++ b/client/coral-embed-stream/src/Embed.js
@@ -121,7 +121,7 @@ class Embed extends Component {
- {lang.t('profile')}
+ {lang.t('MY_COMMENTS')}
Configure Stream
{loggedIn && this.props.logout().then(refetch)} changeTab={this.changeTab}/>}
@@ -160,7 +160,6 @@ class Embed extends Component {
charCount={asset.settings.charCountEnable && asset.settings.charCount} />
: null
}
-
: {asset.settings.closedMessage}
@@ -170,6 +169,7 @@ class Embed extends Component {
refetch={refetch}
offset={signInOffset}/>}
{loggedIn && user && }
+ {loggedIn && }
{
highlightedComment &&
{
- this.initialState = false;
- loadMoreComments(assetId, comments, loadMore, parentId);
- }}>
- {topLevel ? lang.t('viewMoreComments') : this.replyCountFormat(replyCount)}
-
+ ?
+ {
+ this.initialState = false;
+ loadMoreComments(assetId, comments, loadMore, parentId);
+ }}>
+ {topLevel ? lang.t('viewMoreComments') : this.replyCountFormat(replyCount)}
+
+
: null;
}
}
diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css
index 629efd29f..4c427543e 100644
--- a/client/coral-embed-stream/style/default.css
+++ b/client/coral-embed-stream/style/default.css
@@ -212,6 +212,7 @@ hr {
.coral-plugin-commentcontent-text {
margin-bottom: 7px;
+ font-size: 16px;
}
.coral-plugin-author-name-text {
@@ -417,8 +418,11 @@ button.comment__action-button[disabled],
/* Load More */
-button.coral-load-more {
- width: 100%;
+.coral-load-more {
+ text-align: center;
+}
+
+.coral-load-more button {
text-align: center;
color: #FFF;
background-color: #2376D8;
@@ -427,9 +431,10 @@ button.coral-load-more {
border-radius: 2px;
line-height: 1em;
text-transform: capitalize;
+ display: inline-block;
}
-button.coral-load-more:hover {
+.coral-load-more:hover button {
background-color: #4399FF;
}
diff --git a/client/coral-framework/actions/asset.js b/client/coral-framework/actions/asset.js
index 02e72ea98..609525986 100644
--- a/client/coral-framework/actions/asset.js
+++ b/client/coral-framework/actions/asset.js
@@ -2,7 +2,7 @@ import * as actions from '../constants/asset';
import coralApi from '../helpers/response';
import {addNotification} from '../actions/notification';
-import I18n from 'coral-framework/modules/i18n/i18n';
+import I18n from '../../coral-framework/modules/i18n/i18n';
import translations from './../translations';
const lang = new I18n(translations);
diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js
index 9dedaa2f2..01174cf05 100644
--- a/client/coral-framework/actions/auth.js
+++ b/client/coral-framework/actions/auth.js
@@ -1,8 +1,9 @@
-import I18n from 'coral-framework/modules/i18n/i18n';
+import I18n from '../../coral-framework/modules/i18n/i18n';
import translations from './../translations';
const lang = new I18n(translations);
import * as actions from '../constants/auth';
import coralApi, {base} from '../helpers/response';
+import {pym} from 'coral-framework';
// Dialog Actions
export const showSignInDialog = (offset = 0) => ({type: actions.SHOW_SIGNIN_DIALOG, offset});
@@ -135,7 +136,8 @@ const forgotPassowordFailure = () => ({type: actions.FETCH_FORGOT_PASSWORD_FAILU
export const fetchForgotPassword = email => (dispatch) => {
dispatch(forgotPassowordRequest(email));
- coralApi('/account/password/reset', {method: 'POST', body: {email}})
+ const redirectUri = pym.parentUrl || location.href;
+ coralApi('/account/password/reset', {method: 'POST', body: {email, loc: redirectUri}})
.then(() => dispatch(forgotPassowordSuccess()))
.catch(error => dispatch(forgotPassowordFailure(error)));
};
diff --git a/client/coral-framework/actions/notification.js b/client/coral-framework/actions/notification.js
index f2cc053ec..cb1aee5dd 100644
--- a/client/coral-framework/actions/notification.js
+++ b/client/coral-framework/actions/notification.js
@@ -1,4 +1,4 @@
-import {pym} from 'coral-framework';
+import {pym} from '../../coral-framework';
export const addNotification = (notifType, text) => {
pym.sendMessage('coral-alert', `${notifType}|${text}`);
diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json
index 914cce0fe..f9038aba7 100644
--- a/client/coral-framework/translations.json
+++ b/client/coral-framework/translations.json
@@ -1,5 +1,6 @@
{
"en": {
+ "MY_COMMENTS": "My Comments",
"profile": "Profile",
"successUpdateSettings": "The changes you have made have been applied to the comment stream on this article",
"successNameUpdate": "Your username has been updated",
@@ -15,7 +16,7 @@
"viewReply": "view reply",
"viewAllRepliesInitial": "view all {0} replies",
"viewAllReplies": "view {0} replies",
- "newCount": "View {0} more {1}",
+ "newCount": "View {0} new {1}",
"comment": "comment",
"comments": "comments",
"error": {
@@ -40,11 +41,14 @@
}
},
"es": {
+ "profile": "Pérfil",
+ "MY_COMMENTS": "Mis Comentarios",
"profile": "Pérfil",
"successUpdateSettings": "La configuración de este articulo fue actualizada",
"successBioUpdate": "Tu biografia fue actualizada",
"contentNotAvailable": "El contenido no se encuentra disponible",
"bannedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes gustar, marcar o escribir commentarios.",
+ "editNameMsg": "",
"viewMoreComments": "Ver commentarios más",
"viewReply": "ver respuesta",
"viewAllRepliesInitial": "ver todas las {0} respuestas",
diff --git a/client/coral-plugin-flags/FlagComment.js b/client/coral-plugin-flags/FlagComment.js
index 3f3051f28..24fde6b0f 100644
--- a/client/coral-plugin-flags/FlagComment.js
+++ b/client/coral-plugin-flags/FlagComment.js
@@ -23,14 +23,14 @@ const getPopupMenu = [
{val: 'This comment is offensive', text: lang.t('comment-offensive')},
{val: 'This looks like an ad/marketing', text: lang.t('marketing')},
{val: 'I don\'t agree with this comment', text: lang.t('no-agree-comment')},
- {val: 'Other', text: lang.t('other')}
+ {val: 'other', text: lang.t('other')}
]
: [
{val: 'This username is offensive', text: lang.t('username-offensive')},
{val: 'I don\'t like this username', text: lang.t('no-like-username')},
{val: 'This user is impersonating', text: lang.t('user-impersonating')},
{val: 'This looks like an ad/marketing', text: lang.t('marketing')},
- {val: 'Other', text: lang.t('other')}
+ {val: 'other', text: lang.t('other')}
];
return {
header: lang.t('step-2-header'),
diff --git a/client/coral-plugin-history/Comment.css b/client/coral-plugin-history/Comment.css
index f54b0ad29..2c0dee094 100644
--- a/client/coral-plugin-history/Comment.css
+++ b/client/coral-plugin-history/Comment.css
@@ -1,16 +1,74 @@
+@custom-media --big-viewport (min-width: 780px);
+
.myComment {
border-bottom: 1px solid lightgrey;
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
}
.myComment:last-child {
- border-bottom: none;
+ border-bottom: solid 1px #EBEBEB;
}
.assetURL {
font-size: 16px;
color: black;
+ text-decoration: none;
+ font-weight: bold;
}
.commentBody {
}
+
+.sidebar {
+ ul {
+ min-width: 136px;
+ }
+
+ li {
+ margin-bottom: 10px;
+
+ &:nth-child(1) {
+ color: #5394D7;
+ }
+
+ &:nth-child(2) {
+ color: #909090;
+ }
+
+
+ i {
+ margin-right: 5px;
+ font-size: 15px;
+ vertical-align: bottom;
+ }
+
+ a:hover {
+ cursor: pointer;
+ }
+ }
+}
+
+@custom-media --mobile-viewport (max-width: 480px);
+
+@media (--mobile-viewport) {
+ .myComment {
+ flex-direction: column;
+ }
+
+ .sidebar ul {
+ display: flex;
+ li {
+ margin-right: 20px;
+ }
+ }
+}
+
+.pubdate {
+ display: inline-block;
+ font-size: inherit;
+ margin: inherit;
+ color: inherit;
+}
diff --git a/client/coral-plugin-history/Comment.js b/client/coral-plugin-history/Comment.js
index 00dcc40c7..707939536 100644
--- a/client/coral-plugin-history/Comment.js
+++ b/client/coral-plugin-history/Comment.js
@@ -1,13 +1,42 @@
-import React, {PropTypes} from 'react';
+import React, { PropTypes } from 'react';
+import { Icon } from '../coral-ui';
import styles from './Comment.css';
+import PubDate from '../coral-plugin-pubdate/PubDate';
+import Content from '../coral-plugin-commentcontent/CommentContent';
const Comment = props => {
return (
);
};
diff --git a/client/coral-ui/components/TabBar.css b/client/coral-ui/components/TabBar.css
index 33a4c6d8b..b5d44d570 100644
--- a/client/coral-ui/components/TabBar.css
+++ b/client/coral-ui/components/TabBar.css
@@ -19,7 +19,7 @@
}
.base li:hover {
- background: #f3f3f3;
+ background: #d5d5d5;
cursor: pointer;
}
diff --git a/graph/loaders/users.js b/graph/loaders/users.js
index 145b0dfdb..90c661f71 100644
--- a/graph/loaders/users.js
+++ b/graph/loaders/users.js
@@ -3,51 +3,11 @@ const DataLoader = require('dataloader');
const util = require('./util');
const UsersService = require('../../services/users');
-const UserModel = require('../../models/user');
const genUserByIDs = (context, ids) => UsersService
.findByIdArray(ids)
.then(util.singleJoinBy(ids, 'id'));
-/**
- * Retrieves users based on the passed in query that is filtered by the
- * current used passed in via the context.
- * @param {Object} context graph context
- * @param {Object} query query terms to apply to the users query
- */
-const getUsersByQuery = ({user}, {ids, limit, cursor, sort}) => {
-
- let users = UserModel.find();
-
- if (ids) {
- users = users.find({
- id: {
- $in: ids
- }
- });
- }
-
- if (cursor) {
- if (sort === 'REVERSE_CHRONOLOGICAL') {
- users = users.where({
- created_at: {
- $lt: cursor
- }
- });
- } else {
- users = users.where({
- created_at: {
- $gt: cursor
- }
- });
- }
- }
-
- return users
- .sort({created_at: sort === 'REVERSE_CHRONOLOGICAL' ? -1 : 1})
- .limit(limit);
-};
-
/**
* Creates a set of loaders based on a GraphQL context.
* @param {Object} context the context of the GraphQL request
@@ -55,7 +15,6 @@ const getUsersByQuery = ({user}, {ids, limit, cursor, sort}) => {
*/
module.exports = (context) => ({
Users: {
- getByQuery: (query) => getUsersByQuery(context, query),
getByID: new DataLoader((ids) => genUserByIDs(context, ids))
}
});
diff --git a/graph/mutators/user.js b/graph/mutators/user.js
index 48966c5e8..3f87c1fb5 100644
--- a/graph/mutators/user.js
+++ b/graph/mutators/user.js
@@ -6,28 +6,18 @@ const setUserStatus = ({user}, {id, status}) => {
.then(res => res);
};
-const suspendUser = ({user}, {id, message}) => {
- return UsersService.suspendUser(id, message)
- .then(res => {
- return res;
- });
-};
-
module.exports = (context) => {
- let mutators = {
+ if (context.user && context.user.can('mutation:setUserStatus')) {
+ return {
+ User: {
+ setUserStatus: (action) => setUserStatus(context, action)
+ }
+ };
+ }
+
+ return {
User: {
- setUserStatus: () => Promise.reject(errors.ErrNotAuthorized),
- suspendUser: () => Promise.reject(errors.ErrNotAuthorized)
+ setUserStatus: () => Promise.reject(errors.ErrNotAuthorized)
}
};
-
- if (context.user && context.user.can('mutation:setUserStatus')) {
- mutators.User.setUserStatus = (action) => setUserStatus(context, action);
- }
-
- if (context.user && context.user.can('mutation:suspendUser')) {
- mutators.User.suspendUser = (action) => suspendUser(context, action);
- }
-
- return mutators;
};
diff --git a/graph/resolvers/flag_action.js b/graph/resolvers/flag_action.js
index 80d1583ad..44cf7a410 100644
--- a/graph/resolvers/flag_action.js
+++ b/graph/resolvers/flag_action.js
@@ -1,15 +1,9 @@
const FlagAction = {
// Stored in the metadata, extract and return.
- message({metadata: {message}}) {
- return message;
- },
- reason({group_id}) {
- return group_id;
- },
- user({user_id}, _, {loaders: {Users}}) {
- return Users.getByID.load(user_id);
- },
+ reason({metadata: {reason}}) {
+ return reason;
+ }
};
module.exports = FlagAction;
diff --git a/graph/resolvers/root_mutation.js b/graph/resolvers/root_mutation.js
index a474407d2..dc540b202 100644
--- a/graph/resolvers/root_mutation.js
+++ b/graph/resolvers/root_mutation.js
@@ -47,9 +47,6 @@ const RootMutation = {
setUserStatus(_, {id, status}, {mutators: {User}}) {
return wrapResponse(null)(User.setUserStatus({id, status}));
},
- suspendUser(_, {id, message}, {mutators: {User}}) {
- return wrapResponse(null)(User.suspendUser({id, message}));
- },
setCommentStatus(_, {id, status}, {mutators: {Comment}}) {
return wrapResponse(null)(Comment.setCommentStatus({id, status}));
},
diff --git a/graph/resolvers/root_query.js b/graph/resolvers/root_query.js
index ce11fcae9..e2836c51d 100644
--- a/graph/resolvers/root_query.js
+++ b/graph/resolvers/root_query.js
@@ -86,28 +86,6 @@ const RootQuery = {
}
return user;
- },
-
- // This endpoint is used for loading the user moderation queues (users whose username has been flagged),
- // so hide it in the event that we aren't an admin.
- users(_, {query: {action_type, limit, cursor, sort}}, {user, loaders: {Users, Actions}}) {
-
- if (user == null || !user.hasRoles('ADMIN')) {
- return null;
- }
-
- const query = {limit, cursor, sort};
-
- if (action_type) {
- return Actions.getByTypes({action_type, item_type: 'USERS'})
- .then((ids) => {
-
- // Perform the query using the available resolver.
- return Users.getByQuery({ids, limit, cursor, sort});
- });
- }
-
- return Users.getByQuery(query);
}
};
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index 8b07c2ab2..e656d0183 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -45,9 +45,6 @@ type User {
# returns all comments based on a query.
comments(query: CommentsQuery): [Comment]
- # returns all users based on a query.
- users(query: UsersQuery): [User]
-
# returns user status
status: USER_STATUS
}
@@ -63,20 +60,6 @@ type Tag {
created_at: Date!
}
-# UsersQuery allows the ability to query users by a specific fields.
-input UsersQuery {
- action_type: ACTION_TYPE
-
- # Limit the number of results to be returned.
- limit: Int = 10
-
- # Skip results from the last created_at timestamp.
- cursor: Date
-
- # Sort the results by created_at.
- sort: SORT_ORDER = REVERSE_CHRONOLOGICAL
-}
-
################################################################################
## Comments
################################################################################
@@ -537,9 +520,6 @@ type RootQuery {
# role.
me: User
- # Users returned based on a query.
- users(query: UsersQuery): [User]
-
# Asset metrics related to user actions are saturated into the assets
# returned.
assetMetrics(from: Date!, to: Date!, sort: ASSET_METRICS_SORT!, limit: Int = 10): [Asset!]
@@ -673,14 +653,6 @@ type SetUserStatusResponse implements Response {
errors: [UserError]
}
-# SuspendUserResponse is the response returned with possibly some errors
-# relating to the suspend action attempt.
-type SuspendUserResponse implements Response {
-
- # An array of errors relating to the mutation that occurred.
- errors: [UserError]
-}
-
# SetCommentStatusResponse is the response returned with possibly some errors
# relating to the delete action attempt.
type SetCommentStatusResponse implements Response {
@@ -724,9 +696,6 @@ type RootMutation {
# Sets User status. Requires the `ADMIN` role.
setUserStatus(id: ID!, status: USER_STATUS!): SetUserStatusResponse
- # Sets User status to BANNED and canEditName to true. It sends a message to the banned User. Requires the `ADMIN` role.
- suspendUser(id: ID!, message: String): SuspendUserResponse
-
# Sets Comment status. Requires the `ADMIN` role.
setCommentStatus(id: ID!, status: COMMENT_STATUS!): SetCommentStatusResponse
diff --git a/models/user.js b/models/user.js
index e1cbb466b..e23acaa56 100644
--- a/models/user.js
+++ b/models/user.js
@@ -176,10 +176,9 @@ const USER_GRAPH_OPERATIONS = [
'mutation:deleteAction',
'mutation:editName',
'mutation:setUserStatus',
- 'mutation:suspendUser',
'mutation:setCommentStatus',
'mutation:addCommentTag',
- 'mutation:removeCommentTag'
+ 'mutation:removeCommentTag',
];
/**
@@ -195,7 +194,7 @@ UserSchema.method('can', function(...actions) {
return false;
}
- if (actions.some((action) => action === 'mutation:setUserStatus' || action === 'mutation:suspendUser' || action === 'mutation:setCommentStatus') && !this.hasRoles('ADMIN')) {
+ if (actions.some((action) => action === 'mutation:setUserStatus' || action === 'mutation:setCommentStatus') && !this.hasRoles('ADMIN')) {
return false;
}
diff --git a/routes/admin/index.js b/routes/admin/index.js
index d250ea32a..1a9769591 100644
--- a/routes/admin/index.js
+++ b/routes/admin/index.js
@@ -12,7 +12,7 @@ router.get('/password-reset', (req, res) => {
// TODO: store the redirect uri in the token or something fancy.
// admins and regular users should probably be redirected to different places.
- res.render('admin/password-reset', {redirectUri: process.env.TALK_ROOT_URL});
+ res.render('admin/password-reset');
});
router.get('*', (req, res) => {
diff --git a/routes/api/account/index.js b/routes/api/account/index.js
index 44b4b3953..e1c73d638 100644
--- a/routes/api/account/index.js
+++ b/routes/api/account/index.js
@@ -41,14 +41,14 @@ router.post('/email/verify', (req, res, next) => {
* if it does, create a JWT and send an email
*/
router.post('/password/reset', (req, res, next) => {
- const {email} = req.body;
+ const {email, loc} = req.body;
if (!email) {
return next('you must submit an email when requesting a password.');
}
UsersService
- .createPasswordResetToken(email)
+ .createPasswordResetToken(email, loc)
.then((token) => {
// Check to see if the token isn't defined.
@@ -101,11 +101,11 @@ router.put('/password/reset', (req, res, next) => {
UsersService
.verifyPasswordResetToken(token)
- .then((user) => {
- return UsersService.changePassword(user.id, password);
+ .then(([user, loc]) => {
+ return Promise.all([UsersService.changePassword(user.id, password), loc]);
})
- .then(() => {
- res.status(204).end();
+ .then(([ , loc]) => {
+ res.json({redirect: loc});
})
.catch(() => {
next(authorization.ErrNotAuthorized);
diff --git a/services/email/suspension.ejs b/services/email/suspension.ejs
deleted file mode 100644
index b36560ec5..000000000
--- a/services/email/suspension.ejs
+++ /dev/null
@@ -1 +0,0 @@
-<%= body %>
diff --git a/services/email/suspension.txt.ejs b/services/email/suspension.txt.ejs
deleted file mode 100644
index b36560ec5..000000000
--- a/services/email/suspension.txt.ejs
+++ /dev/null
@@ -1 +0,0 @@
-<%= body %>
diff --git a/services/users.js b/services/users.js
index 4e1196252..0bce64425 100644
--- a/services/users.js
+++ b/services/users.js
@@ -1,11 +1,9 @@
const bcrypt = require('bcrypt');
+const url = require('url');
const jwt = require('jsonwebtoken');
const Wordlist = require('./wordlist');
-
const errors = require('../errors');
-
const uuid = require('uuid');
-
const redis = require('./redis');
const redisClient = redis.createClient();
@@ -16,8 +14,8 @@ const USER_ROLES = require('../models/user').USER_ROLES;
const RECAPTCHA_WINDOW_SECONDS = 60 * 10; // 10 minutes.
const RECAPTCHA_INCORRECT_TRIGGER = 5; // after 3 incorrect attempts, recaptcha will be required.
+const SettingsService = require('./settings');
const ActionsService = require('./actions');
-const MailerService = require('./mailer');
// In the event that the TALK_SESSION_SECRET is missing but we are testing, then
// set the process.env.TALK_SESSION_SECRET.
@@ -450,48 +448,6 @@ module.exports = class UsersService {
});
}
- /**
- * Suspend a user. It changes the status to BANNED and canEditName to True.
- * @param {String} id id of a user
- * @param {Function} done callback after the operation is complete
- */
- static suspendUser(id, message) {
- return UserModel.update({
- id
- }, {
- $set: {
- status: 'BANNED',
- canEditName: true
- }
- })
- .then(() => {
- return UsersService.findById(id)
- .then((user) => {
- if (message) {
- let localProfile = user.profiles.find((profile) => profile.provider === 'local');
-
- if (localProfile) {
- const options =
- {
- template: 'suspension', // needed to know which template to render!
- locals: { // specifies the template locals.
- body: message
- },
- subject: 'Email Suspension',
- to: localProfile.id // This only works if the user has registered via e-mail.
- // We may want a standard way to access a user's e-mail address in the future
- };
-
- return MailerService.sendSimple(options);
- } else {
- return Promise.reject(errors.ErrMissingEmail);
- }
- }
- });
-
- });
- }
-
/**
* Finds a user with the id.
* @param {String} id user id (uuid)
@@ -524,15 +480,18 @@ module.exports = class UsersService {
* Creates a JWT from a user email. Only works for local accounts.
* @param {String} email of the local user
*/
- static createPasswordResetToken(email) {
+ static createPasswordResetToken(email, loc) {
if (!email || typeof email !== 'string') {
return Promise.reject('email is required when creating a JWT for resetting passord');
}
email = email.toLowerCase();
- return UserModel.findOne({profiles: {$elemMatch: {id: email}}})
- .then((user) => {
+ return Promise.all([
+ UserModel.findOne({profiles: {$elemMatch: {id: email}}}),
+ SettingsService.retrieve()
+ ])
+ .then(([user, settings]) => {
if (!user) {
// Since we don't want to reveal that the email does/doesn't exist
@@ -541,9 +500,21 @@ module.exports = class UsersService {
return;
}
+ let redirectDomain;
+ try {
+ redirectDomain = url.parse(loc).hostname;
+ } catch (e) {
+ return Promise.reject('redirect location is invalid');
+ }
+
+ if (settings.domains.whitelist.indexOf(redirectDomain) === -1) {
+ return Promise.reject('redirect location is not on the list of acceptable domains');
+ }
+
const payload = {
jti: uuid.v4(),
email,
+ loc,
userId: user.id,
version: user.__v
};
@@ -588,7 +559,9 @@ module.exports = class UsersService {
})
// TODO: add search by __v as well
- .then((decoded) => UsersService.findById(decoded.userId));
+ .then((decoded) => {
+ return Promise.all([UsersService.findById(decoded.userId), decoded.loc]);
+ });
}
/**
diff --git a/test/client/coral-plugin-history/Comment.spec.js b/test/client/coral-plugin-history/Comment.spec.js
deleted file mode 100644
index 6d69d54cd..000000000
--- a/test/client/coral-plugin-history/Comment.spec.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react';
-import {shallow, mount} from 'enzyme';
-import {expect} from 'chai';
-import Comment from '../../../client/coral-plugin-history/Comment';
-
-describe('coral-plugin-history/Comment', () => {
- let render;
- const comment = {body: 'this is a comment', id: '123'};
- const asset = {url: 'https://google.com'};
-
- beforeEach(() => {
- render = shallow({}}/>);
- });
-
- it('should render the provided comment body', () => {
- const wrapper = mount({}}/>);
- expect(wrapper.find('.myCommentBody')).to.have.length(1);
- expect(wrapper.find('.myCommentBody').text()).to.equal('this is a comment');
- });
-
- it('should render the asset url as a link', () => {
- const wrapper = mount({}}/>);
- expect(wrapper.find('.myCommentAnchor')).to.have.length(1);
- expect(wrapper.find('.myCommentAnchor').text()).to.equal('https://google.com');
- });
-
- it('should render the comment with styles', () => {
- expect(render.props().style).to.be.defined;
- });
-});
diff --git a/test/client/coral-plugin-history/CommentHistory.spec.js b/test/client/coral-plugin-history/CommentHistory.spec.js
deleted file mode 100644
index 671a50137..000000000
--- a/test/client/coral-plugin-history/CommentHistory.spec.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import React from 'react';
-import {shallow, mount} from 'enzyme';
-import {expect} from 'chai';
-import CommentHistory from '../../../client/coral-plugin-history/CommentHistory';
-
-describe('coral-plugin-history/CommentHistory', () => {
- let render;
- const comments = [{body: 'a comment or something', 'status_history':[{'type':'premod', 'created_at':'2016-12-09T01:40:53.327Z', 'assigned_by':null}, {'created_at':'2016-12-09T22:52:44.888Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-09T01:40:53.360Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-09T22:52:44.893Z', 'id':'3962c2ea-4ec4-42e4-b9bd-c571ff30f56b'}, {'body':'another comment', 'status_history':[{'type':'premod', 'created_at':'2016-12-09T22:53:43.148Z', 'assigned_by':null}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-09T22:53:43.158Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'premod', '__v':0, 'updated_at':'2016-12-09T22:53:43.158Z', 'id':'b51e27af-bcfd-4932-91be-e3f01a4802e6'}, {'body':'can I comment?', 'status_history':[{'type':'premod', 'created_at':'2016-12-13T23:23:47.123Z', 'assigned_by':null}, {'created_at':'2016-12-13T23:23:58.487Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'cef81015-1b53-4d70-b9af-6eca680f22fc', 'created_at':'2016-12-13T23:23:47.131Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-13T23:23:58.493Z', 'id':'dc9d7be1-b911-4dc3-8e1e-400e8b8d110e'}, {'body':'pre-mod comment', 'status_history':[{'type':'premod', 'created_at':'2016-12-08T21:34:56.994Z', 'assigned_by':null}, {'created_at':'2016-12-08T21:38:04.961Z', 'type':'rejected', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T21:34:56.997Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'rejected', '__v':0, 'updated_at':'2016-12-08T21:38:04.965Z', 'id':'6f02af16-a8f8-4ead-80ea-0d48824eb74d'}, {'body':'a flagged commetn', 'status_history':[{'type':'premod', 'created_at':'2016-12-08T21:38:26.342Z', 'assigned_by':null}, {'created_at':'2016-12-09T23:47:27.009Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T21:38:26.344Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-09T23:47:27.018Z', 'id':'784c5f91-36b9-4bda-b4ca-a114cef2c9f0'}, {'body':'a post mod comment', 'status_history':[{'type':'premod', 'created_at':'2016-12-08T22:19:05.870Z', 'assigned_by':null}, {'created_at':'2016-12-09T23:26:41.427Z', 'type':'accepted', 'assigned_by':'92256159-1164-4f66-9970-c7f23de7e461'}], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T22:19:05.874Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':'accepted', '__v':0, 'updated_at':'2016-12-09T23:26:41.450Z', 'id':'e8b86039-f850-4e53-bd9d-f8c9186a9637'}, {'body':'an actual post-mod comment here', 'status_history':[], 'asset_id':'96fddf96-7c83-4008-80ad-50091997d006', 'created_at':'2016-12-08T22:20:11.147Z', 'author_id':'92256159-1164-4f66-9970-c7f23de7e461', 'status':null, '__v':0, 'updated_at':'2016-12-08T22:20:11.147Z', 'id':'cff1a318-50c6-431e-9a63-de7a7b7136bf'}];
- const asset = {
- 'settings': null,
- 'created_at':'2016-12-06T21:36:09.302Z',
- 'url':'localhost:3000/',
- 'scraped':null,
- 'status':'open',
- 'updated_at':'2016-12-08T02:11:15.943Z',
- '_id':'58472f499e775a38f23d5da0',
- 'type':'article',
- 'closedMessage':null,
- 'id':'7302e637-f884-47c0-9723-02cc10a18617',
- 'closedAt':null
- };
-
- comments.forEach((comment) => {
- comment.asset = asset;
- });
-
- beforeEach(() => {
- render = shallow({}}/>);
- });
-
- it('should render Comments as children when given comments and assets', () => {
- const wrapper = mount({}}/>);
- expect(wrapper.find('.commentHistory__list').children()).to.have.length(7);
- });
-
- it('should render with styles', () => {
- expect(render.props().style).to.be.defined;
- });
-});
diff --git a/test/graph/mutations/addCommentTag.js b/test/graph/mutations/addCommentTag.js
index 0835e81bc..14a746705 100644
--- a/test/graph/mutations/addCommentTag.js
+++ b/test/graph/mutations/addCommentTag.js
@@ -54,7 +54,7 @@ describe('graph.mutations.addCommentTag', () => {
}
expect(response.errors).to.be.empty;
expect(response.data.addCommentTag.errors).to.deep.equal([{'translation_key':'NOT_AUTHORIZED'}]);
- expect(response.data.addCommentTag.comment).to.be.null;
+ expect(response.data.addCommentTag.comment).to.be.null;
});
});
});
diff --git a/views/admin/password-reset.ejs b/views/admin/password-reset.ejs
index 06b9d2a86..4b25d1279 100644
--- a/views/admin/password-reset.ejs
+++ b/views/admin/password-reset.ejs
@@ -126,7 +126,7 @@
},
data: JSON.stringify({password: password, token: location.hash.replace('#', '')})
}).then(function (success) {
- location.href = '<%= redirectUri %>';
+ location.href = success.redirect;
}).catch(function (error) {
showError(error.responseText);
});
diff --git a/yarn.lock b/yarn.lock
index 25171f74d..ef0b4fa82 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -49,7 +49,7 @@
"@types/express-serve-static-core" "*"
"@types/mime" "*"
-abab@^1.0.0:
+abab@^1.0.0, abab@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d"
@@ -76,7 +76,7 @@ acorn-globals@^1.0.4:
dependencies:
acorn "^2.1.0"
-acorn-globals@^3.0.0:
+acorn-globals@^3.0.0, acorn-globals@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
dependencies:
@@ -2022,11 +2022,11 @@ csso@~2.3.1:
clap "^1.0.9"
source-map "^0.5.3"
-cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0":
+cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0", "cssom@>= 0.3.2 < 0.4.0":
version "0.3.2"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b"
-"cssstyle@>= 0.2.29 < 0.3.0", "cssstyle@>= 0.2.36 < 0.3.0":
+"cssstyle@>= 0.2.29 < 0.3.0", "cssstyle@>= 0.2.37 < 0.3.0":
version "0.2.37"
resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54"
dependencies:
@@ -3635,7 +3635,7 @@ iconv-lite@0.4.13:
version "0.4.13"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
-iconv-lite@0.4.15, iconv-lite@^0.4.13, iconv-lite@^0.4.5, iconv-lite@~0.4.13:
+iconv-lite@0.4.15, iconv-lite@^0.4.5, iconv-lite@~0.4.13:
version "0.4.15"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
@@ -4188,6 +4188,10 @@ jsbn@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd"
+jsdom-global@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-2.1.1.tgz#47d46fe77f6167baf5d34431d3bb59fc41b0915a"
+
jsdom@^7.0.2:
version "7.2.2"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-7.2.2.tgz#40b402770c2bda23469096bee91ab675e3b1fc6e"
@@ -4208,30 +4212,29 @@ jsdom@^7.0.2:
whatwg-url-compat "~0.6.5"
xml-name-validator ">= 2.0.1 < 3.0.0"
-jsdom@^9.8.3:
- version "9.9.1"
- resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.9.1.tgz#84f3972ad394ab963233af8725211bce4d01bfd5"
+jsdom@^9.12.0:
+ version "9.12.0"
+ resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4"
dependencies:
- abab "^1.0.0"
- acorn "^2.4.0"
- acorn-globals "^1.0.4"
+ abab "^1.0.3"
+ acorn "^4.0.4"
+ acorn-globals "^3.1.0"
array-equal "^1.0.0"
content-type-parser "^1.0.1"
- cssom ">= 0.3.0 < 0.4.0"
- cssstyle ">= 0.2.36 < 0.3.0"
+ cssom ">= 0.3.2 < 0.4.0"
+ cssstyle ">= 0.2.37 < 0.3.0"
escodegen "^1.6.1"
html-encoding-sniffer "^1.0.1"
- iconv-lite "^0.4.13"
nwmatcher ">= 1.3.9 < 2.0.0"
parse5 "^1.5.1"
- request "^2.55.0"
- sax "^1.1.4"
- symbol-tree ">= 3.1.0 < 4.0.0"
- tough-cookie "^2.3.1"
- webidl-conversions "^3.0.1"
+ request "^2.79.0"
+ sax "^1.2.1"
+ symbol-tree "^3.2.1"
+ tough-cookie "^2.3.2"
+ webidl-conversions "^4.0.0"
whatwg-encoding "^1.0.1"
- whatwg-url "^4.1.0"
- xml-name-validator ">= 2.0.1 < 3.0.0"
+ whatwg-url "^4.3.0"
+ xml-name-validator "^2.0.1"
jsesc@^1.3.0:
version "1.3.0"
@@ -6919,7 +6922,7 @@ sax@0.5.x:
version "0.5.8"
resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1"
-sax@^1.1.4, sax@~1.2.1:
+sax@^1.1.4, sax@^1.2.1, sax@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
@@ -7401,7 +7404,7 @@ symbol-observable@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d"
-"symbol-tree@>= 3.1.0 < 4.0.0":
+"symbol-tree@>= 3.1.0 < 4.0.0", symbol-tree@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.1.tgz#8549dd1d01fa9f893c18cc9ab0b106b4d9b168cb"
@@ -7565,7 +7568,7 @@ touch@1.0.0:
dependencies:
nopt "~1.0.10"
-tough-cookie@^2.0.0, tough-cookie@^2.2.0, tough-cookie@^2.3.1, tough-cookie@~2.3.0:
+tough-cookie@^2.0.0, tough-cookie@^2.2.0, tough-cookie@^2.3.2, tough-cookie@~2.3.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
dependencies:
@@ -7801,10 +7804,14 @@ webidl-conversions@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506"
-webidl-conversions@^3.0.0, webidl-conversions@^3.0.1:
+webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+webidl-conversions@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.1.tgz#8015a17ab83e7e1b311638486ace81da6ce206a0"
+
webpack-sources@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.4.tgz#ccc2c817e08e5fa393239412690bb481821393cd"
@@ -7853,9 +7860,9 @@ whatwg-url-compat@~0.6.5:
dependencies:
tr46 "~0.0.1"
-whatwg-url@^4.1.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.3.0.tgz#92aaee21f4f2a642074357d70ef8500a7cbb171a"
+whatwg-url@^4.3.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.6.0.tgz#ef98da442273be04cf9632e176f257d2395a1ae4"
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
@@ -7956,7 +7963,7 @@ xdg-basedir@^2.0.0:
dependencies:
os-homedir "^1.0.0"
-"xml-name-validator@>= 2.0.1 < 3.0.0":
+"xml-name-validator@>= 2.0.1 < 3.0.0", xml-name-validator@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"