From 24391de61d9d185ee5003d9c778b8d1e9c164c22 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 18 Sep 2017 11:06:31 -0300 Subject: [PATCH 01/26] userCount --- graph/loaders/users.js | 31 ++++++++++++++++++++++++++++++- graph/resolvers/root_query.js | 9 +++++++++ graph/typeDefs.graphql | 13 +++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/graph/loaders/users.js b/graph/loaders/users.js index b943c60ed..aa74aad29 100644 --- a/graph/loaders/users.js +++ b/graph/loaders/users.js @@ -107,6 +107,34 @@ const getUsersByQuery = async ({user, loaders: {Actions}}, {ids, limit, cursor, }; }; + +/** + * Retrieves the count of users based on the passed in query. + * @param {Object} context graph context + * @param {Object} query query to execute against the users collection + * to compute the counts + * @return {Promise} resolves to the counts of the users from the + * query + */ +const getCountByQuery = async ({loaders: {Actions}}, {action_type}) => { + let query = UserModel.find(); + + if (action_type) { + const userIds = await Actions.getByTypes({action_type, item_type: 'USERS'}); + + query = query.find({ + id: { + $in: userIds + } + }); + } + + + return UserModel + .find(query) + .count(); +}; + /** * Creates a set of loaders based on a GraphQL context. * @param {Object} context the context of the GraphQL request @@ -115,6 +143,7 @@ const getUsersByQuery = async ({user, loaders: {Actions}}, {ids, limit, cursor, module.exports = (context) => ({ Users: { getByQuery: (query) => getUsersByQuery(context, query), - getByID: new DataLoader((ids) => genUserByIDs(context, ids)) + getByID: new DataLoader((ids) => genUserByIDs(context, ids)), + getCountByQuery: (query) => getCountByQuery(context, query) } }); diff --git a/graph/resolvers/root_query.js b/graph/resolvers/root_query.js index 13e41a312..c42b7e9c2 100644 --- a/graph/resolvers/root_query.js +++ b/graph/resolvers/root_query.js @@ -50,6 +50,15 @@ const RootQuery = { return Comments.getCountByQuery(query); }, + + async userCount(_, {query}, {user, loaders: {Users}}) { + if (user == null || !user.can(SEARCH_OTHER_USERS)) { + return null; + } + + return Users.getCountByQuery(query); + }, + assetMetrics(_, query, {user, loaders: {Metrics: {Assets}}}) { if (user == null || !user.can(SEARCH_ASSETS)) { return null; diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 916c0ade5..3191ddf06 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -337,6 +337,15 @@ input CommentCountQuery { tags: [String!] } +# UserCountQuery allows the ability to query user counts by specific +# methods. +input UserCountQuery { + + # comments returned will only be ones which have at least one action of this + # type. + action_type: ACTION_TYPE +} + type EditInfo { edited: Boolean! editableUntil: Date @@ -833,6 +842,10 @@ type RootQuery { # expensive as it is not batched. Requires the `ADMIN` role. commentCount(query: CommentCountQuery!): Int + # Return the count of users satisfied by the query. Note that this edge is + # expensive as it is not batched. Requires the `ADMIN` role. + userCount(query: UserCountQuery!): Int + # The currently logged in user based on the request. Requires any logged in # role. me: User From a07d6616551c4bb0f9fd059bb362a97408f6ac0d Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 18 Sep 2017 11:53:52 -0300 Subject: [PATCH 02/26] Adding CountBadge and flaggedUsernamesCount --- .../CountBadge.css} | 2 +- .../CountBadge.js} | 8 ++++---- .../routes/Community/components/Community.js | 5 ++++- .../Community/components/CommunityMenu.js | 6 ++++-- .../routes/Community/containers/Community.js | 17 ++++++++++++++++- .../Moderation/components/ModerationMenu.js | 4 ++-- 6 files changed, 31 insertions(+), 11 deletions(-) rename client/coral-admin/src/{routes/Moderation/components/CommentCount.css => components/CountBadge.css} (93%) rename client/coral-admin/src/{routes/Moderation/components/CommentCount.js => components/CountBadge.js} (81%) diff --git a/client/coral-admin/src/routes/Moderation/components/CommentCount.css b/client/coral-admin/src/components/CountBadge.css similarity index 93% rename from client/coral-admin/src/routes/Moderation/components/CommentCount.css rename to client/coral-admin/src/components/CountBadge.css index 998133d07..343692ecb 100644 --- a/client/coral-admin/src/routes/Moderation/components/CommentCount.css +++ b/client/coral-admin/src/components/CountBadge.css @@ -5,7 +5,7 @@ vertical-align: middle; padding: 1px 5px; border-radius: 2px; - margin-left: 2px; + margin-left: 5px; line-height: 18px; box-sizing: border-box; height: 18px; diff --git a/client/coral-admin/src/routes/Moderation/components/CommentCount.js b/client/coral-admin/src/components/CountBadge.js similarity index 81% rename from client/coral-admin/src/routes/Moderation/components/CommentCount.js rename to client/coral-admin/src/components/CountBadge.js index 53d611ed6..ff47cf9a6 100644 --- a/client/coral-admin/src/routes/Moderation/components/CommentCount.js +++ b/client/coral-admin/src/components/CountBadge.js @@ -1,10 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; -import styles from './CommentCount.css'; +import styles from './CountBadge.css'; import t from 'coral-framework/services/i18n'; -const CommentCount = ({count}) => { +const CountBadge = ({count}) => { let number = count; // shorten large counts to abbreviations @@ -21,8 +21,8 @@ const CommentCount = ({count}) => { ); }; -CommentCount.propTypes = { +CountBadge.propTypes = { count: PropTypes.number.isRequired }; -export default CommentCount; +export default CountBadge; diff --git a/client/coral-admin/src/routes/Community/components/Community.js b/client/coral-admin/src/routes/Community/components/Community.js index e59f9b6ca..1b3e4ac72 100644 --- a/client/coral-admin/src/routes/Community/components/Community.js +++ b/client/coral-admin/src/routes/Community/components/Community.js @@ -90,10 +90,13 @@ export default class Community extends Component { render() { const {searchValue} = this.state; const tab = this.getTabContent(searchValue, this.props); + const {root: {flaggedUsernamesCount}} = this.props; return (
- +
{ tab }
diff --git a/client/coral-admin/src/routes/Community/components/CommunityMenu.js b/client/coral-admin/src/routes/Community/components/CommunityMenu.js index 1e226313d..71e01d817 100644 --- a/client/coral-admin/src/routes/Community/components/CommunityMenu.js +++ b/client/coral-admin/src/routes/Community/components/CommunityMenu.js @@ -1,18 +1,20 @@ import React from 'react'; - import styles from './CommunityMenu.css'; import t from 'coral-framework/services/i18n'; import {Link} from 'react-router'; +import CountBadge from '../../../components/CountBadge'; -const CommunityMenu = () => { +const CommunityMenu = ({flaggedUsernamesCount = 0}) => { const flaggedPath = '/admin/community/flagged'; const peoplePath = '/admin/community/people'; + return (
{t('community.flaggedaccounts')} + {t('community.people')} diff --git a/client/coral-admin/src/routes/Community/containers/Community.js b/client/coral-admin/src/routes/Community/containers/Community.js index a41c257d6..cc4b3b343 100644 --- a/client/coral-admin/src/routes/Community/containers/Community.js +++ b/client/coral-admin/src/routes/Community/containers/Community.js @@ -1,7 +1,8 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; -import {compose} from 'react-apollo'; +import {compose, gql} from 'react-apollo'; +import withQuery from 'coral-framework/hocs/withQuery'; import {withSetUserStatus, withRejectUsername} from 'coral-framework/graphql/mutations'; import { @@ -29,6 +30,19 @@ const mapStateToProps = (state) => ({ community: state.community, }); + +const withFlaggedUsernamesCount = withQuery(gql` + query TalkAdmin_FlaggedUsernamesCount { + flaggedUsernamesCount: userCount(query: { + action_type: FLAG + }) + } + `, { + options: { + fetchPolicy: 'network-only', + }, +}); + const mapDispatchToProps = (dispatch) => bindActionCreators({ fetchAccounts, @@ -41,4 +55,5 @@ export default compose( connect(mapStateToProps, mapDispatchToProps), withSetUserStatus, withRejectUsername, + withFlaggedUsernamesCount, )(CommunityContainer); diff --git a/client/coral-admin/src/routes/Moderation/components/ModerationMenu.js b/client/coral-admin/src/routes/Moderation/components/ModerationMenu.js index 25b90cf64..27b8409c6 100644 --- a/client/coral-admin/src/routes/Moderation/components/ModerationMenu.js +++ b/client/coral-admin/src/routes/Moderation/components/ModerationMenu.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import CommentCount from './CommentCount'; +import CountBadge from '../../../components/CountBadge'; import styles from './styles.css'; import {SelectField, Option} from 'react-mdl-selectfield'; import {Icon} from 'coral-ui'; @@ -28,7 +28,7 @@ const ModerationMenu = ({ to={getModPath(queue.key, asset.id)} className={cn('mdl-tabs__tab', styles.tab, {[styles.active]: activeTab === queue.key})} activeClassName={styles.active}> - {queue.name} + {queue.name} )}
From 41afe9fae7d24adba9dcd0730c75239d685c8630 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 19 Sep 2017 00:41:38 +0700 Subject: [PATCH 03/26] Replace hard-coded string on all comments tab with translation --- client/coral-embed-stream/src/components/Stream.js | 2 +- locales/en.yml | 2 +- locales/es.yml | 2 +- locales/fr.yml | 2 +- locales/pt_BR.yml | 5 ++--- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index af2ec698b..1ec780e63 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -160,7 +160,7 @@ class Stream extends React.Component { loading={loading} appendTabs={ - All Comments {totalCommentCount} + {t('stream.all_comments')} {totalCommentCount} } appendTabPanes={ diff --git a/locales/en.yml b/locales/en.yml index 9e1bf1056..1c4805394 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -313,7 +313,6 @@ en: report_notif_remove: "Your report has been removed." reported: Reported settings: - all_comments: "All Comments" from_settings_page: "From the Profile Page you can see your comment history." my_comment_history: "My comment History" profile: Profile @@ -322,6 +321,7 @@ en: to_access: "to access Profile" user_no_comment: "You've never left a comment. Join the conversation!" stream: + all_comments: "All Comments" temporarily_suspended: "In accordance with {0}'s community guidelines, your account has been temporarily suspended. Please rejoin the conversation {1}." comment_not_found: "Comment was not found" step_1_header: "Report an issue" diff --git a/locales/es.yml b/locales/es.yml index 83edac80b..8324ddd73 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -305,7 +305,6 @@ es: report_notif_remove: "Tu reporte ha sido eliminado." reported: "Reportado" settings: - all_comments: "Todos los comentarios" from_settings_page: "Desde la página de configuración puedes ver tu historial de comentarios." my_comment_history: "Mi historial de comentarios" profile: Perfil @@ -348,6 +347,7 @@ es: step_2_header: "Ayúdanos a comprender" step_3_header: "Gracias por tu participación" stream: + all_comments: "Todos los comentarios" temporarily_suspended: "De acuerdo con la guía de la comunidad de {0}, su cuenta ha sido temporalmente suspendida. Por favor unirse a la conversación {1}." comment_not_found: "Comentario no encontrado" streams: diff --git a/locales/fr.yml b/locales/fr.yml index 5ece19c67..69240b157 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -263,7 +263,6 @@ fr: reported: Signalé set_best: "Sélectionner comme le meilleur" settings: - all_comments: "Tous les commentaires" from_settings_page: "Dans la page Profil, vous pouvez voir l'historique de vos commentaires." my_comment_history: "Mon historique de commentaires" profile: Profil @@ -272,6 +271,7 @@ fr: to_access: "Accéder au profil" user_no_comment: "Vous n'avez jamais laissé de commentaire. Rejoignez la conversation!" stream: + all_comments: "Tous les commentaires" temporarily_suspended: "Conformément à la charte d'utilisation des commentaires de {0}, votre compte a été temporairement suspendu. Merci de revenir dans la conversation {1}." step_1_header: "Signaler un problème" step_2_header: "Aidez-nous à comprendre" diff --git a/locales/pt_BR.yml b/locales/pt_BR.yml index e2e93c531..783105f81 100644 --- a/locales/pt_BR.yml +++ b/locales/pt_BR.yml @@ -303,7 +303,6 @@ pt_BR: report_notif_remove: "Seu marcado foi removido." reported: Reportado settings: - all_comments: "Todos os comentários" from_settings_page: "Na página de Perfil, você pode ver seu histórico de comentários." my_comment_history: "Histórico de comentários" profile: Perfil @@ -312,8 +311,8 @@ pt_BR: to_access: "para acessar o perfil" user_no_comment: "Você nunca deixou um comentário. Participe da conversa!" stream: - temporarily_suspended: " - De acordo com as diretrizes da comunidade de {0}, sua conta foi temporariamente suspensa. Por favor, volte para a conversa {1}." + all_comments: "Todos os comentários" + temporarily_suspended: "De acordo com as diretrizes da comunidade de {0}, sua conta foi temporariamente suspensa. Por favor, volte para a conversa {1}." step_1_header: "Relatar um problema" step_2_header: "Ajude-nos a entender" step_3_header: "Obrigdo por sua contribuição" From 52c1989780af3fa7ddceddb8690093f64ba69a90 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 18 Sep 2017 11:57:57 -0600 Subject: [PATCH 04/26] Expose Helmet configuration - Fixes #962 --- app.js | 6 ++++-- config.js | 8 ++++++++ docs/_docs/00-01-faq.md | 15 +++++++++++++++ docs/_docs/02-01-configuration.md | 6 ++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 4e74062d5..9da2041fc 100644 --- a/app.js +++ b/app.js @@ -2,6 +2,7 @@ const express = require('express'); const bodyParser = require('body-parser'); const morgan = require('morgan'); const path = require('path'); +const merge = require('lodash/merge'); const helmet = require('helmet'); const compression = require('compression'); const cookieParser = require('cookie-parser'); @@ -10,6 +11,7 @@ const { BASE_PATH, MOUNT_PATH, STATIC_URL, + HELMET_CONFIGURATION, } = require('./url'); const routes = require('./routes'); const debug = require('debug')('talk:app'); @@ -31,9 +33,9 @@ app.set('trust proxy', 1); // Enable a suite of security good practices through helmet. We disable // frameguard to allow crossdomain injection of the embed. -app.use(helmet({ +app.use(helmet(merge(HELMET_CONFIGURATION, { frameguard: false, -})); +}))); // Compress the responses if appropriate. app.use(compression()); diff --git a/config.js b/config.js index 1a54ece5a..62c6fccf4 100644 --- a/config.js +++ b/config.js @@ -79,6 +79,14 @@ const CONFIG = { INSTALL_LOCK: process.env.TALK_INSTALL_LOCK === 'TRUE', + //------------------------------------------------------------------------------ + // Middleware Configuration + //------------------------------------------------------------------------------ + + // HELMET_CONFIGURATION provides the entrypoint to override options for the + // helmet middleware used. + HELMET_CONFIGURATION: JSON.parse(process.env.TALK_HELMET_CONFIGURATION || '{}'), + //------------------------------------------------------------------------------ // External database url's //------------------------------------------------------------------------------ diff --git a/docs/_docs/00-01-faq.md b/docs/_docs/00-01-faq.md index e114fce72..77b7b86ed 100644 --- a/docs/_docs/00-01-faq.md +++ b/docs/_docs/00-01-faq.md @@ -3,6 +3,21 @@ title: Frequently Asked Questions permalink: /docs/faq/ --- +{% include toc %} + +### My site doesn't use HSTS headers, how do I stop Talk from sending them too? + +You can specify the configuration option `TALK_HELMET_CONFIGURATION` and set it +to: + +``` +TALK_HELMET_CONFIGURATION={"hsts": false} +``` + +Which will disable the HSTS module. See the +[helmet](https://github.com/helmetjs/helmet) repository for more information on +how to configure other security middleware used by default. + ### How are new stories/assets added to Talk? Is there an API? There are three ways that new assets can make their way into Talk: diff --git a/docs/_docs/02-01-configuration.md b/docs/_docs/02-01-configuration.md index 021735fb9..04cabff78 100644 --- a/docs/_docs/02-01-configuration.md +++ b/docs/_docs/02-01-configuration.md @@ -96,6 +96,12 @@ These are only used during the webpack build. and you would then specify the CDN/Storage url. (Default `process.env.TALK_ROOT_URL`) - `TALK_DISABLE_STATIC_SERVER` (_optional_) - When `TRUE`, it will not mount the static asset serving routes on the router. (Default `FALSE`) +- `TALK_HELMET_CONFIGURATION` (_optional_) - A JSON string representing the + configuration passed to the + [helmet](https://github.com/helmetjs/helmet) middleware. It can be used to + disable features like [HSTS](https://helmetjs.github.io/docs/hsts/) and others + by simply providing the configuration as detailed on the + [helmet README](https://github.com/helmetjs/helmet). (Default `{}`) ### Word Filter From ab2e036047a8321185fa18dab0824f2c6c0f864b Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 18 Sep 2017 15:49:23 -0300 Subject: [PATCH 05/26] withFragments and parent withQuery added --- .../routes/Community/components/Community.js | 12 ++--- .../Community/components/FlaggedAccounts.js | 2 + .../routes/Community/containers/Community.js | 22 +++++++-- .../Community/containers/FlaggedAccounts.js | 48 ++++++++----------- 4 files changed, 45 insertions(+), 39 deletions(-) diff --git a/client/coral-admin/src/routes/Community/components/Community.js b/client/coral-admin/src/routes/Community/components/Community.js index 1b3e4ac72..94f064ff6 100644 --- a/client/coral-admin/src/routes/Community/components/Community.js +++ b/client/coral-admin/src/routes/Community/components/Community.js @@ -74,9 +74,11 @@ export default class Community extends Component { ); } + // console.log('Community Container', this.props); + return (
- + - -
- { tab } -
+ +
{tab}
); } diff --git a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js index 1d78c015a..578dc2d82 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js @@ -19,6 +19,8 @@ class FlaggedAccounts extends React.Component { me, viewUserDetail, } = this.props; + + console.log('Flagged Accounts ROOT', this.props.root); const hasResults = users.nodes && !!users.nodes.length; diff --git a/client/coral-admin/src/routes/Community/containers/Community.js b/client/coral-admin/src/routes/Community/containers/Community.js index cc4b3b343..46ae43a40 100644 --- a/client/coral-admin/src/routes/Community/containers/Community.js +++ b/client/coral-admin/src/routes/Community/containers/Community.js @@ -4,7 +4,12 @@ import {bindActionCreators} from 'redux'; import {compose, gql} from 'react-apollo'; import withQuery from 'coral-framework/hocs/withQuery'; +import FlaggedAccounts from '../containers/FlaggedAccounts'; +import FlaggedUser from '../containers/FlaggedUser'; + import {withSetUserStatus, withRejectUsername} from 'coral-framework/graphql/mutations'; +import {getDefinitionName} from 'coral-framework/utils'; + import { fetchAccounts, updateSorting, @@ -21,9 +26,7 @@ class CommunityContainer extends Component { } render() { - return ( - - ); + return } } const mapStateToProps = (state) => ({ @@ -31,12 +34,21 @@ const mapStateToProps = (state) => ({ }); -const withFlaggedUsernamesCount = withQuery(gql` +const withData = withQuery(gql` query TalkAdmin_FlaggedUsernamesCount { flaggedUsernamesCount: userCount(query: { action_type: FLAG }) + ...${getDefinitionName(FlaggedAccounts.fragments.root)} + ...${getDefinitionName(FlaggedUser.fragments.root)} + me { + ...${getDefinitionName(FlaggedUser.fragments.me)} + __typename + } } + ${FlaggedAccounts.fragments.root} + ${FlaggedUser.fragments.root} + ${FlaggedUser.fragments.me} `, { options: { fetchPolicy: 'network-only', @@ -55,5 +67,5 @@ export default compose( connect(mapStateToProps, mapDispatchToProps), withSetUserStatus, withRejectUsername, - withFlaggedUsernamesCount, + withData, )(CommunityContainer); diff --git a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js index 00c6fdf33..ad824b4af 100644 --- a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import {compose, gql} from 'react-apollo'; -import withQuery from 'coral-framework/hocs/withQuery'; +import {withFragments} from 'plugin-api/beta/client/hocs'; import {Spinner} from 'coral-ui'; import {withSetUserStatus} from 'coral-framework/graphql/mutations'; @@ -50,6 +50,8 @@ class FlaggedAccountsContainer extends Component { }; render() { + console.log('Flagged AccountsContainer', this.props); + if (this.props.data.error) { return
{this.props.data.error.message}
; } @@ -88,31 +90,6 @@ const LOAD_MORE_QUERY = gql` ${FlaggedUser.fragments.user} `; -export const withFlaggedAccountsyQuery = withQuery(gql` - query TalkAdmin_FlaggedAccounts { - ...${getDefinitionName(FlaggedUser.fragments.root)} - users(query:{action_type: FLAG, statuses: [PENDING], limit: 10}){ - hasNextPage - endCursor - nodes { - __typename - ...${getDefinitionName(FlaggedUser.fragments.user)} - } - } - me { - __typename - ...${getDefinitionName(FlaggedUser.fragments.me)} - } - } - ${FlaggedUser.fragments.root} - ${FlaggedUser.fragments.user} - ${FlaggedUser.fragments.me} -`, { - options: { - fetchPolicy: 'network-only', - }, -}); - const mapDispatchToProps = (dispatch) => bindActionCreators({ showBanUserDialog, @@ -123,6 +100,23 @@ const mapDispatchToProps = (dispatch) => export default compose( connect(null, mapDispatchToProps), - withFlaggedAccountsyQuery, withSetUserStatus, + withFragments({ + root: gql` + fragment TalkAdminCommunity_FlaggedAccounts_root on RootQuery { + users(query:{action_type: FLAG, statuses: [PENDING], limit: 10}){ + hasNextPage + endCursor + nodes { + __typename + ...${getDefinitionName(FlaggedUser.fragments.user)} + } + } + me { + id + } + } + ${FlaggedUser.fragments.user} + `, + }), )(FlaggedAccountsContainer); From ebe7ccfd10427c207df4c3e0fbe7dee002871d8f Mon Sep 17 00:00:00 2001 From: Kim Gardner Date: Tue, 19 Sep 2017 11:09:22 +0100 Subject: [PATCH 06/26] Make in-stream moderation a default plugin --- plugins.default.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins.default.json b/plugins.default.json index 950cec229..6ce126983 100644 --- a/plugins.default.json +++ b/plugins.default.json @@ -20,6 +20,7 @@ "talk-plugin-sort-most-replied", "talk-plugin-author-menu", "talk-plugin-member-since", - "talk-plugin-ignore-user" + "talk-plugin-ignore-user", + "talk-plugin-moderation-actions" ] } From b5deac7b48ea00ec5c0c4e71531ba124efa334bd Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 19 Sep 2017 19:20:38 +0700 Subject: [PATCH 07/26] Bugfixes and show different notification for toxic comments Bugfixes: - Toxic comment flag was not added - GraphQL warnings for missing fields --- .../coral-embed-stream/src/graphql/index.js | 2 ++ client/talk-plugin-commentbox/CommentBox.js | 11 ++++++- graph/mutators/comment.js | 29 +++++++++++-------- .../client/components/CheckToxicityHook.js | 14 ++++++++- .../client/containers/CheckToxicityHook.js | 9 ++++++ .../client/index.js | 2 +- .../client/translations.yml | 4 +++ 7 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 plugins/talk-plugin-toxic-comments/client/containers/CheckToxicityHook.js diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index dc81b56a4..82d6ca9e3 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -123,6 +123,7 @@ export default { optimisticResponse: { createComment: { __typename: 'CreateCommentResponse', + errors: null, comment: { __typename: 'Comment', user: { @@ -132,6 +133,7 @@ export default { }, created_at: new Date().toISOString(), body, + actions: [], action_summaries: [], tags: tags.map((tag) => ({ tag: { diff --git a/client/talk-plugin-commentbox/CommentBox.js b/client/talk-plugin-commentbox/CommentBox.js index e5717e08a..409ad86ec 100644 --- a/client/talk-plugin-commentbox/CommentBox.js +++ b/client/talk-plugin-commentbox/CommentBox.js @@ -11,12 +11,20 @@ import {CommentForm} from './CommentForm'; export const name = 'talk-plugin-commentbox'; +const notifyReasons = ['LINKS', 'TRUST']; + +function shouldNotify(actions = []) { + return actions.some(({reason}) => notifyReasons.includes(reason)); +} + // Given a newly posted comment's status, show a notification to the user // if needed export const notifyForNewCommentStatus = (notify, comment) => { if (comment.status === 'REJECTED') { notify('error', t('comment_box.comment_post_banned_word')); - } else if (comment.status === 'PREMOD' || comment.status === 'SYSTEM_WITHHELD') { + } else if ( + comment.status === 'PREMOD' || + comment.status === 'SYSTEM_WITHHELD' && shouldNotify(comment.actions)) { notify('success', t('comment_box.comment_post_notif_premod')); } }; @@ -187,6 +195,7 @@ CommentBox.propTypes = { isReply: PropTypes.bool.isRequired, canPost: PropTypes.bool, notify: PropTypes.func.isRequired, + commentBox: PropTypes.object, }; const mapStateToProps = ({commentBox}) => ({commentBox}); diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js index 75cad99b7..2aee766a5 100644 --- a/graph/mutators/comment.js +++ b/graph/mutators/comment.js @@ -393,13 +393,12 @@ const moderationPhases = [ ]; /** - * This resolves a given comment's status to take into account moderator actions - * are applied. + * This resolves a given comment's status and actions. * @param {Object} context graphql context * @param {String} body body of the comment * @param {String} [asset_id] asset for the comment * @param {Object} [wordlist={}] the results of the wordlist scan - * @return {Promise} resolves to the comment's status + * @return {Object} resolves to the comment's status and actions */ const resolveCommentModeration = async (context, comment) => { @@ -418,6 +417,8 @@ const resolveCommentModeration = async (context, comment) => { // Combine the asset and the settings to get the asset settings. const assetSettings = await AssetsService.rectifySettings(asset, settings); + let actions = comment.actions || []; + // Loop over all the moderation phases and see if we've resolved the status. for (const phase of moderationPhases) { const result = await phase(context, comment, { @@ -429,13 +430,14 @@ const resolveCommentModeration = async (context, comment) => { if (result) { - // Merge the comment and the result together. - comment = merge(comment, result); + if (result.actions) { + actions.push(...result.actions); + } // If this result contained a status, then we've finished resolving // phases! if (result.status) { - return comment.actions; + return {status: result.status, actions}; } } } @@ -454,17 +456,20 @@ const createPublicComment = async (context, comment) => { // We then take the wordlist and the comment into consideration when // considering what status to assign the new comment, and resolve the new // status to set the comment to. - let actions = await resolveCommentModeration(context, comment); + let {actions, status} = await resolveCommentModeration(context, comment); + + // Assign status to comment. + comment.status = status; // Then we actually create the comment with the new status. - comment = await createComment(context, comment); + const result = await createComment(context, comment); // Create all the actions that were determined during the moderation check // phase. - await createActions(comment.id, actions); + await createActions(result.id, actions); // Finally, we return the comment. - return comment; + return result; }; // createActions will for each of the provided actions, create the given action @@ -515,10 +520,10 @@ const edit = async (context, {id, asset_id, edit: {body}}) => { let comment = {id, asset_id, body}; // Determine the new status of the comment. - const actions = await resolveCommentModeration(context, comment); + const {actions, status} = await resolveCommentModeration(context, comment); // Execute the edit. - comment = await CommentsService.edit({id, author_id: context.user.id, body, status: comment.status}); + comment = await CommentsService.edit({id, author_id: context.user.id, body, status}); // Create all the actions that were determined during the moderation check // phase. diff --git a/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js b/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js index 22c378435..8572bdd5a 100644 --- a/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js +++ b/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js @@ -1,4 +1,6 @@ import React from 'react'; +import PropTypes from 'prop-types'; +import {t} from 'plugin-api/beta/client/services'; /** * CheckToxicityHook adds hooks to the `commentBox` @@ -20,7 +22,11 @@ export default class CheckToxicityHook extends React.Component { } }); - this.toxicityPostHook = this.props.registerHook('postSubmit', () => { + this.toxicityPostHook = this.props.registerHook('postSubmit', (result) => { + const actions = result.createComment.comment && result.createComment.comment.actions; + if (actions && actions.some(({reason}) => reason === 'TOXIC_COMMENT')) { + this.props.notify('error', t('talk-plugin-toxic-comments.still_toxic')); + } // Reset `checked` after comment was successfully posted. this.checked = false; @@ -36,3 +42,9 @@ export default class CheckToxicityHook extends React.Component { return null; } } + +CheckToxicityHook.propTypes = { + notify: PropTypes.func.isRequired, + registerHook: PropTypes.func.isRequired, + unregisterHook: PropTypes.func.isRequired, +}; diff --git a/plugins/talk-plugin-toxic-comments/client/containers/CheckToxicityHook.js b/plugins/talk-plugin-toxic-comments/client/containers/CheckToxicityHook.js new file mode 100644 index 000000000..f2b010b0d --- /dev/null +++ b/plugins/talk-plugin-toxic-comments/client/containers/CheckToxicityHook.js @@ -0,0 +1,9 @@ +import {bindActionCreators} from 'redux'; +import {connect} from 'plugin-api/beta/client/hocs'; +import {notify} from 'plugin-api/beta/client/actions/notification'; +import CheckToxicityHook from '../components/CheckToxicityHook'; + +const mapDispatchToProps = (dispatch) => + bindActionCreators({notify}, dispatch); + +export default connect(null, mapDispatchToProps)(CheckToxicityHook); diff --git a/plugins/talk-plugin-toxic-comments/client/index.js b/plugins/talk-plugin-toxic-comments/client/index.js index c364a2a00..bb0417606 100644 --- a/plugins/talk-plugin-toxic-comments/client/index.js +++ b/plugins/talk-plugin-toxic-comments/client/index.js @@ -1,5 +1,5 @@ import translations from './translations.yml'; -import CheckToxicityHook from './components/CheckToxicityHook'; +import CheckToxicityHook from './containers/CheckToxicityHook'; export default { translations, diff --git a/plugins/talk-plugin-toxic-comments/client/translations.yml b/plugins/talk-plugin-toxic-comments/client/translations.yml index ebcc31903..787095046 100644 --- a/plugins/talk-plugin-toxic-comments/client/translations.yml +++ b/plugins/talk-plugin-toxic-comments/client/translations.yml @@ -3,4 +3,8 @@ en: COMMENT_IS_TOXIC: | Are you sure? The language in this comment might violate our community guidelines. You can edit the comment or submit it for moderator review. + talk-plugin-toxic-comments: + still_toxic: | + This edited comment might still violate our community guidelines. + Our moderation team will review your comment shortly. es: From f1f98680284fa2591606d3d6c9ddcb4040e20189 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 19 Sep 2017 11:13:06 -0300 Subject: [PATCH 08/26] Cleaning console.logs --- .../src/routes/Community/components/FlaggedAccounts.js | 2 -- .../coral-admin/src/routes/Community/containers/Community.js | 3 +-- .../src/routes/Community/containers/FlaggedAccounts.js | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js index 578dc2d82..1d78c015a 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.js @@ -19,8 +19,6 @@ class FlaggedAccounts extends React.Component { me, viewUserDetail, } = this.props; - - console.log('Flagged Accounts ROOT', this.props.root); const hasResults = users.nodes && !!users.nodes.length; diff --git a/client/coral-admin/src/routes/Community/containers/Community.js b/client/coral-admin/src/routes/Community/containers/Community.js index 46ae43a40..a74128188 100644 --- a/client/coral-admin/src/routes/Community/containers/Community.js +++ b/client/coral-admin/src/routes/Community/containers/Community.js @@ -26,14 +26,13 @@ class CommunityContainer extends Component { } render() { - return + return ; } } const mapStateToProps = (state) => ({ community: state.community, }); - const withData = withQuery(gql` query TalkAdmin_FlaggedUsernamesCount { flaggedUsernamesCount: userCount(query: { diff --git a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js index ad824b4af..ee3ec979f 100644 --- a/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js +++ b/client/coral-admin/src/routes/Community/containers/FlaggedAccounts.js @@ -50,8 +50,6 @@ class FlaggedAccountsContainer extends Component { }; render() { - console.log('Flagged AccountsContainer', this.props); - if (this.props.data.error) { return
{this.props.data.error.message}
; } From 818ad07eac35dbb467b88bc33246ea35235d4eae Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 19 Sep 2017 11:43:10 -0300 Subject: [PATCH 09/26] lint fixes --- graph/loaders/users.js | 6 ++---- graph/resolvers/root_query.js | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/graph/loaders/users.js b/graph/loaders/users.js index aa74aad29..269512c60 100644 --- a/graph/loaders/users.js +++ b/graph/loaders/users.js @@ -107,7 +107,6 @@ const getUsersByQuery = async ({user, loaders: {Actions}}, {ids, limit, cursor, }; }; - /** * Retrieves the count of users based on the passed in query. * @param {Object} context graph context @@ -129,10 +128,9 @@ const getCountByQuery = async ({loaders: {Actions}}, {action_type}) => { }); } - return UserModel - .find(query) - .count(); + .find(query) + .count(); }; /** diff --git a/graph/resolvers/root_query.js b/graph/resolvers/root_query.js index c42b7e9c2..e4701c901 100644 --- a/graph/resolvers/root_query.js +++ b/graph/resolvers/root_query.js @@ -50,7 +50,6 @@ const RootQuery = { return Comments.getCountByQuery(query); }, - async userCount(_, {query}, {user, loaders: {Users}}) { if (user == null || !user.can(SEARCH_OTHER_USERS)) { return null; From 563a6d23dd0125fbc192518b51722c5665dd6a73 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 19 Sep 2017 21:43:58 +0700 Subject: [PATCH 10/26] First pass adding labels --- .../src/components/CommentLabels.css | 2 + .../src/components/CommentLabels.js | 39 +++++++++++++++++++ .../coral-admin/src/components/FlagLabel.css | 4 ++ .../coral-admin/src/components/FlagLabel.js | 21 ++++++++++ client/coral-admin/src/components/Label.css | 26 +++++++++++++ client/coral-admin/src/components/Label.js | 22 +++++++++++ .../routes/Moderation/components/Comment.js | 12 +++--- 7 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 client/coral-admin/src/components/CommentLabels.css create mode 100644 client/coral-admin/src/components/CommentLabels.js create mode 100644 client/coral-admin/src/components/FlagLabel.css create mode 100644 client/coral-admin/src/components/FlagLabel.js create mode 100644 client/coral-admin/src/components/Label.css create mode 100644 client/coral-admin/src/components/Label.js diff --git a/client/coral-admin/src/components/CommentLabels.css b/client/coral-admin/src/components/CommentLabels.css new file mode 100644 index 000000000..c3a2af639 --- /dev/null +++ b/client/coral-admin/src/components/CommentLabels.css @@ -0,0 +1,2 @@ +.root { +} diff --git a/client/coral-admin/src/components/CommentLabels.js b/client/coral-admin/src/components/CommentLabels.js new file mode 100644 index 000000000..631cf3443 --- /dev/null +++ b/client/coral-admin/src/components/CommentLabels.js @@ -0,0 +1,39 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import styles from './CommentLabels.css'; +import Label from './Label'; +import FlagLabel from './FlagLabel'; +import cn from 'classnames'; + +function isUserFlagged(actions) { + return actions.some((action) => action.__typename === 'FlagAction' && action.user); +} + +function hasSuspectedWords(actions) { + return actions.some((action) => action.__typename === 'FlagAction' && action.reason === 'Matched suspect word filter'); +} + +function hasHistoryFlag(actions) { + return actions.some((action) => action.__typename === 'FlagAction' && action.reason === 'TRUST'); +} + +const CommentLabels = ({className, status, actions, isReply}) => { + return ( +
+ {isReply && } + {status === 'PREMOD' && } + {isUserFlagged(actions) && User} + {hasSuspectedWords(actions) && Suspect} + {hasHistoryFlag(actions) && History} +
+ ); +}; + +CommentLabels.propTypes = { + className: PropTypes.string, + status: PropTypes.string, + actions: PropTypes.array, + isReply: PropTypes.bool, +}; + +export default CommentLabels; diff --git a/client/coral-admin/src/components/FlagLabel.css b/client/coral-admin/src/components/FlagLabel.css new file mode 100644 index 000000000..070e117d1 --- /dev/null +++ b/client/coral-admin/src/components/FlagLabel.css @@ -0,0 +1,4 @@ +.flag { + background: #d03235; +} + diff --git a/client/coral-admin/src/components/FlagLabel.js b/client/coral-admin/src/components/FlagLabel.js new file mode 100644 index 000000000..8a7864c8b --- /dev/null +++ b/client/coral-admin/src/components/FlagLabel.js @@ -0,0 +1,21 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import styles from './FlagLabel.css'; +import Label from './Label'; +import cn from 'classnames'; + +const FlagLabel = ({iconName, children, className}) => { + return ( + + ); +}; + +FlagLabel.propTypes = { + className: PropTypes.string, + children: PropTypes.node.isRequired, + iconName: PropTypes.string, +}; + +export default FlagLabel; diff --git a/client/coral-admin/src/components/Label.css b/client/coral-admin/src/components/Label.css new file mode 100644 index 000000000..fb6bd86ff --- /dev/null +++ b/client/coral-admin/src/components/Label.css @@ -0,0 +1,26 @@ +.root { + display: inline-block; + color: white; + background: grey; + box-sizing: border-box; + padding: 2px 5px; + font-size: 12px; + height: 24px; + letter-spacing: 0.4px; + line-height: 22px; + background: #063B9A; + min-width: 80px; + text-align: center; +} + +.icon { + font-size: 14px; + vertical-align: text-top; + margin: 0; + margin-right: 4px; +} + +.isFlag { + background: #d03235; +} + diff --git a/client/coral-admin/src/components/Label.js b/client/coral-admin/src/components/Label.js new file mode 100644 index 000000000..9d1b736de --- /dev/null +++ b/client/coral-admin/src/components/Label.js @@ -0,0 +1,22 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import styles from './Label.css'; +import {Icon} from 'coral-ui'; +import cn from 'classnames'; + +const Label = ({iconName, children, className, isFlag}) => { + return ( + + {children} + + ); +}; + +Label.propTypes = { + className: PropTypes.string, + isFlag: PropTypes.bool, + children: PropTypes.node.isRequired, + iconName: PropTypes.string, +}; + +export default Label; diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.js b/client/coral-admin/src/routes/Moderation/components/Comment.js index a3edb4196..ce055e644 100644 --- a/client/coral-admin/src/routes/Moderation/components/Comment.js +++ b/client/coral-admin/src/routes/Moderation/components/Comment.js @@ -3,10 +3,9 @@ import PropTypes from 'prop-types'; import {Link} from 'react-router'; import {Icon} from 'coral-ui'; -import ReplyBadge from 'coral-admin/src/components/ReplyBadge'; import FlagBox from 'coral-admin/src/components/FlagBox'; import styles from './styles.css'; -import CommentType from 'coral-admin/src/components/CommentType'; +import CommentLabels from 'coral-admin/src/components/CommentLabels'; import CommentAnimatedEdit from 'coral-admin/src/components/CommentAnimatedEdit'; import Slot from 'coral-framework/components/Slot'; import {getActionSummary} from 'coral-framework/utils'; @@ -16,7 +15,6 @@ import ActionsMenuItem from 'coral-admin/src/components/ActionsMenuItem'; import CommentBodyHighlighter from 'coral-admin/src/components/CommentBodyHighlighter'; import IfHasLink from 'coral-admin/src/components/IfHasLink'; import cn from 'classnames'; -import {getCommentType} from 'coral-admin/src/utils/comment'; import t, {timeago} from 'coral-framework/services/i18n'; @@ -66,7 +64,6 @@ class Comment extends React.Component { const flagActionSummaries = getActionSummary('FlagActionSummary', comment); const flagActions = comment.actions && comment.actions.filter((a) => a.__typename === 'FlagAction'); - const commentType = getCommentType(comment); const selectionStateCSS = selected ? 'mdl-shadow--16dp' : 'mdl-shadow--2dp'; @@ -109,8 +106,11 @@ class Comment extends React.Component { }
- {comment.hasParent && } - + Date: Tue, 19 Sep 2017 22:29:51 +0700 Subject: [PATCH 11/26] Added core labels --- .../src/components/CommentLabels.css | 11 +++++++ .../src/components/CommentLabels.js | 18 ++++++----- .../src/components/CommentType.css | 30 ----------------- .../coral-admin/src/components/CommentType.js | 32 ------------------- client/coral-admin/src/components/Label.css | 4 --- .../coral-admin/src/components/ReplyBadge.js | 11 ------- .../src/components/UserDetailComment.css | 2 +- .../src/components/UserDetailComment.js | 10 ++---- .../src/containers/CommentLabels.js | 21 ++++++++++++ .../src/containers/UserDetailComment.js | 4 +++ .../routes/Moderation/components/Comment.js | 4 +-- .../routes/Moderation/containers/Comment.js | 5 ++- client/coral-admin/src/utils/comment.js | 9 ------ client/coral-ui/components/Badge.css | 20 ------------ client/coral-ui/components/Badge.js | 13 -------- client/coral-ui/index.js | 1 - 16 files changed, 55 insertions(+), 140 deletions(-) delete mode 100644 client/coral-admin/src/components/CommentType.css delete mode 100644 client/coral-admin/src/components/CommentType.js delete mode 100644 client/coral-admin/src/components/ReplyBadge.js create mode 100644 client/coral-admin/src/containers/CommentLabels.js delete mode 100644 client/coral-admin/src/utils/comment.js delete mode 100644 client/coral-ui/components/Badge.css delete mode 100644 client/coral-ui/components/Badge.js diff --git a/client/coral-admin/src/components/CommentLabels.css b/client/coral-admin/src/components/CommentLabels.css index c3a2af639..718cd7dfd 100644 --- a/client/coral-admin/src/components/CommentLabels.css +++ b/client/coral-admin/src/components/CommentLabels.css @@ -1,2 +1,13 @@ .root { + > *:not(:last-child) { + margin-right: 3px; + } +} + +.replyLabel { + background-color: #3D73D5; +} + +.premodLabel { + background-color: #063B9A; } diff --git a/client/coral-admin/src/components/CommentLabels.js b/client/coral-admin/src/components/CommentLabels.js index 631cf3443..333f905ef 100644 --- a/client/coral-admin/src/components/CommentLabels.js +++ b/client/coral-admin/src/components/CommentLabels.js @@ -1,9 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -import styles from './CommentLabels.css'; import Label from './Label'; import FlagLabel from './FlagLabel'; import cn from 'classnames'; +import styles from './CommentLabels.css'; function isUserFlagged(actions) { return actions.some((action) => action.__typename === 'FlagAction' && action.user); @@ -17,11 +17,11 @@ function hasHistoryFlag(actions) { return actions.some((action) => action.__typename === 'FlagAction' && action.reason === 'TRUST'); } -const CommentLabels = ({className, status, actions, isReply}) => { +const CommentLabels = ({comment: {className, status, actions, hasParent}}) => { return (
- {isReply && } - {status === 'PREMOD' && } + {hasParent && } + {status === 'PREMOD' && } {isUserFlagged(actions) && User} {hasSuspectedWords(actions) && Suspect} {hasHistoryFlag(actions) && History} @@ -30,10 +30,12 @@ const CommentLabels = ({className, status, actions, isReply}) => { }; CommentLabels.propTypes = { - className: PropTypes.string, - status: PropTypes.string, - actions: PropTypes.array, - isReply: PropTypes.bool, + comment: PropTypes.shape({ + className: PropTypes.string, + status: PropTypes.string, + actions: PropTypes.array, + hasParent: PropTypes.bool, + }), }; export default CommentLabels; diff --git a/client/coral-admin/src/components/CommentType.css b/client/coral-admin/src/components/CommentType.css deleted file mode 100644 index 969d9511d..000000000 --- a/client/coral-admin/src/components/CommentType.css +++ /dev/null @@ -1,30 +0,0 @@ -.commentType { - display: inline-block; - color: white; - background: grey; - box-sizing: border-box; - padding: 2px 5px; - font-size: 12px; - height: 24px; - letter-spacing: 0.4px; - line-height: 22px; - - > i { - font-size: 14px; - vertical-align: text-top; - margin: 0; - margin-right: 4px; - } - - &.premod { - background: #063B9A; - } - - &.flagged { - background: #d03235; - } - - &.no-type { - display: none; - } -} diff --git a/client/coral-admin/src/components/CommentType.js b/client/coral-admin/src/components/CommentType.js deleted file mode 100644 index 4d1ff7e67..000000000 --- a/client/coral-admin/src/components/CommentType.js +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import styles from './CommentType.css'; -import {Icon} from 'coral-ui'; -import cn from 'classnames'; - -const CommentType = (props) => { - const typeData = getTypeData(props.type); - - return ( - - {typeData.text} - - ); -}; - -const getTypeData = (type) => { - switch (type) { - case 'premod': - return {icon: 'query_builder', text: 'Pre-Mod', className: 'premod'}; - case 'flagged': - return {icon: 'flag', text: 'Flagged', className: 'flagged'}; - default: - return {icon: 'flag', className: 'no-type'}; - } -}; - -CommentType.propTypes = { - type: PropTypes.string.isRequired -}; - -export default CommentType; diff --git a/client/coral-admin/src/components/Label.css b/client/coral-admin/src/components/Label.css index fb6bd86ff..5e7f656ee 100644 --- a/client/coral-admin/src/components/Label.css +++ b/client/coral-admin/src/components/Label.css @@ -8,7 +8,6 @@ height: 24px; letter-spacing: 0.4px; line-height: 22px; - background: #063B9A; min-width: 80px; text-align: center; } @@ -20,7 +19,4 @@ margin-right: 4px; } -.isFlag { - background: #d03235; -} diff --git a/client/coral-admin/src/components/ReplyBadge.js b/client/coral-admin/src/components/ReplyBadge.js deleted file mode 100644 index 5c8bb88ba..000000000 --- a/client/coral-admin/src/components/ReplyBadge.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import {Badge} from 'coral-ui'; -import t from 'coral-framework/services/i18n'; - -const ReplyBadge = () => ( - - {t('modqueue.reply')} - -); - -export default ReplyBadge; diff --git a/client/coral-admin/src/components/UserDetailComment.css b/client/coral-admin/src/components/UserDetailComment.css index 2dfd61cf3..d1e2b007a 100644 --- a/client/coral-admin/src/components/UserDetailComment.css +++ b/client/coral-admin/src/components/UserDetailComment.css @@ -55,7 +55,7 @@ position: relative; } -.badgeBar { +.labels { position: absolute; right: 0px; } diff --git a/client/coral-admin/src/components/UserDetailComment.js b/client/coral-admin/src/components/UserDetailComment.js index fa196dc41..2aec28ab9 100644 --- a/client/coral-admin/src/components/UserDetailComment.js +++ b/client/coral-admin/src/components/UserDetailComment.js @@ -4,16 +4,14 @@ import {Link} from 'react-router'; import {Icon} from 'coral-ui'; import FlagBox from './FlagBox'; -import ReplyBadge from './ReplyBadge'; import styles from './UserDetailComment.css'; -import CommentType from './CommentType'; import {getActionSummary} from 'coral-framework/utils'; import ActionButton from 'coral-admin/src/components/ActionButton'; import CommentBodyHighlighter from 'coral-admin/src/components/CommentBodyHighlighter'; import IfHasLink from 'coral-admin/src/components/IfHasLink'; import cn from 'classnames'; -import {getCommentType} from 'coral-admin/src/utils/comment'; import CommentAnimatedEdit from './CommentAnimatedEdit'; +import CommentLabels from '../containers/CommentLabels'; import t, {timeago} from 'coral-framework/services/i18n'; @@ -35,7 +33,6 @@ class UserDetailComment extends React.Component { const flagActionSummaries = getActionSummary('FlagActionSummary', comment); const flagActions = comment.actions && comment.actions.filter((a) => a.__typename === 'FlagAction'); - const commentType = getCommentType(comment); return (
  • - {comment.hasParent && } - +
    +
  • diff --git a/client/coral-admin/src/containers/CommentLabels.js b/client/coral-admin/src/containers/CommentLabels.js new file mode 100644 index 000000000..ab9639bcd --- /dev/null +++ b/client/coral-admin/src/containers/CommentLabels.js @@ -0,0 +1,21 @@ +import {gql} from 'react-apollo'; +import CommentLabels from '../components/CommentLabels'; +import withFragments from 'coral-framework/hocs/withFragments'; + +export default withFragments({ + comment: gql` + fragment CoralAdmin_CommentLabels_comment on Comment { + hasParent + status + actions { + __typename + ... on FlagAction { + reason + } + user { + id + } + } + } + ` +})(CommentLabels); diff --git a/client/coral-admin/src/containers/UserDetailComment.js b/client/coral-admin/src/containers/UserDetailComment.js index 9f0e3a793..9f0199bce 100644 --- a/client/coral-admin/src/containers/UserDetailComment.js +++ b/client/coral-admin/src/containers/UserDetailComment.js @@ -1,6 +1,8 @@ import {gql} from 'react-apollo'; import UserDetailComment from '../components/UserDetailComment'; import withFragments from 'coral-framework/hocs/withFragments'; +import {getDefinitionName} from 'coral-framework/utils'; +import CommentLabels from './CommentLabels'; export default withFragments({ comment: gql` @@ -35,6 +37,8 @@ export default withFragments({ editing { edited } + ...${getDefinitionName(CommentLabels.fragments.comment)} } + ${CommentLabels.fragments.comment} ` })(UserDetailComment); diff --git a/client/coral-admin/src/routes/Moderation/components/Comment.js b/client/coral-admin/src/routes/Moderation/components/Comment.js index ce055e644..b210d8f23 100644 --- a/client/coral-admin/src/routes/Moderation/components/Comment.js +++ b/client/coral-admin/src/routes/Moderation/components/Comment.js @@ -107,9 +107,7 @@ class Comment extends React.Component { }
    a.__typename === 'FlagAction')) { - commentType = 'flagged'; - } - return commentType; -} diff --git a/client/coral-ui/components/Badge.css b/client/coral-ui/components/Badge.css deleted file mode 100644 index db68ebd58..000000000 --- a/client/coral-ui/components/Badge.css +++ /dev/null @@ -1,20 +0,0 @@ -.badge { - display: inline-block; - color: white; - background: grey; - box-sizing: border-box; - padding: 2px 5px; - font-size: 12px; - height: 24px; - letter-spacing: 0.4px; - line-height: 22px; - background-color: #3D73D5; - margin-right: 4px; -} - -.icon { - font-size: 14px; - vertical-align: text-top; - margin: 0; - margin-right: 4px; -} \ No newline at end of file diff --git a/client/coral-ui/components/Badge.js b/client/coral-ui/components/Badge.js deleted file mode 100644 index c1ba8100b..000000000 --- a/client/coral-ui/components/Badge.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import styles from './Badge.css'; -import Icon from './Icon'; -import cn from 'classnames'; - -const Badge = ({className, children, icon, props}) => ( - - {icon && } - {children} - -); - -export default Badge; diff --git a/client/coral-ui/index.js b/client/coral-ui/index.js index 8f261bcca..8a538d120 100644 --- a/client/coral-ui/index.js +++ b/client/coral-ui/index.js @@ -26,4 +26,3 @@ export {default as Option} from './components/Option'; export {default as SnackBar} from './components/SnackBar'; export {default as TextArea} from './components/TextArea'; export {default as Drawer} from './components/Drawer'; -export {default as Badge} from './components/Badge'; From a96d8c2b3cc7c4aefabf51beb3e922359501b65d Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 19 Sep 2017 12:45:47 -0300 Subject: [PATCH 12/26] Copy, removing log, and adding PropTypes --- .../coral-admin/src/routes/Community/components/Community.js | 2 -- .../src/routes/Community/components/CommunityMenu.js | 5 +++++ graph/typeDefs.graphql | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/client/coral-admin/src/routes/Community/components/Community.js b/client/coral-admin/src/routes/Community/components/Community.js index 94f064ff6..ed0ab728d 100644 --- a/client/coral-admin/src/routes/Community/components/Community.js +++ b/client/coral-admin/src/routes/Community/components/Community.js @@ -74,8 +74,6 @@ export default class Community extends Component { ); } - // console.log('Community Container', this.props); - return (
    diff --git a/client/coral-admin/src/routes/Community/components/CommunityMenu.js b/client/coral-admin/src/routes/Community/components/CommunityMenu.js index 71e01d817..3e13617fe 100644 --- a/client/coral-admin/src/routes/Community/components/CommunityMenu.js +++ b/client/coral-admin/src/routes/Community/components/CommunityMenu.js @@ -2,6 +2,7 @@ import React from 'react'; import styles from './CommunityMenu.css'; import t from 'coral-framework/services/i18n'; import {Link} from 'react-router'; +import PropTypes from 'prop-types'; import CountBadge from '../../../components/CountBadge'; const CommunityMenu = ({flaggedUsernamesCount = 0}) => { @@ -25,4 +26,8 @@ const CommunityMenu = ({flaggedUsernamesCount = 0}) => { ); }; +CommunityMenu.propTypes = { + flaggedUsernamesCount: PropTypes.number, +}; + export default CommunityMenu; diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index e9d850bd3..5b5ab8190 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -848,7 +848,7 @@ type RootQuery { commentCount(query: CommentCountQuery!): Int # Return the count of users satisfied by the query. Note that this edge is - # expensive as it is not batched. Requires the `ADMIN` role. + # expensive as it is not batched. This field is restricted. userCount(query: UserCountQuery!): Int # The currently logged in user based on the request. Requires any logged in From b3cb99925d2e506702c19eac836918c2eb6ba094 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 19 Sep 2017 13:10:14 -0300 Subject: [PATCH 13/26] Adding PropTypes --- .../routes/Community/containers/Community.js | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/client/coral-admin/src/routes/Community/containers/Community.js b/client/coral-admin/src/routes/Community/containers/Community.js index a74128188..2b9dbbe74 100644 --- a/client/coral-admin/src/routes/Community/containers/Community.js +++ b/client/coral-admin/src/routes/Community/containers/Community.js @@ -3,6 +3,7 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import {compose, gql} from 'react-apollo'; import withQuery from 'coral-framework/hocs/withQuery'; +import PropTypes from 'prop-types'; import FlaggedAccounts from '../containers/FlaggedAccounts'; import FlaggedUser from '../containers/FlaggedUser'; @@ -26,13 +27,34 @@ class CommunityContainer extends Component { } render() { - return ; + return ; } } const mapStateToProps = (state) => ({ community: state.community, }); +CommunityContainer.propTypes = { + community: PropTypes.object, + fetchAccounts: PropTypes.func, + hideRejectUsernameDialog: PropTypes.func, + updateSorting: PropTypes.func, + newPage: PropTypes.func, + route: PropTypes.object, + rejectUsername: PropTypes.func, + data: PropTypes.object, + root: PropTypes.object +}; + const withData = withQuery(gql` query TalkAdmin_FlaggedUsernamesCount { flaggedUsernamesCount: userCount(query: { From bccad5585ef32650b12fac8567a0642e22215f08 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 19 Sep 2017 13:10:35 -0300 Subject: [PATCH 14/26] Adding statuses --- graph/loaders/users.js | 10 +++++++++- graph/typeDefs.graphql | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/graph/loaders/users.js b/graph/loaders/users.js index 269512c60..dc58fffad 100644 --- a/graph/loaders/users.js +++ b/graph/loaders/users.js @@ -115,7 +115,7 @@ const getUsersByQuery = async ({user, loaders: {Actions}}, {ids, limit, cursor, * @return {Promise} resolves to the counts of the users from the * query */ -const getCountByQuery = async ({loaders: {Actions}}, {action_type}) => { +const getCountByQuery = async ({loaders: {Actions}}, {action_type, statuses}) => { let query = UserModel.find(); if (action_type) { @@ -128,6 +128,14 @@ const getCountByQuery = async ({loaders: {Actions}}, {action_type}) => { }); } + if (statuses) { + query = query.where({ + status: { + $in: statuses + } + }); + } + return UserModel .find(query) .count(); diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 5b5ab8190..db296e45d 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -348,6 +348,9 @@ input UserCountQuery { # comments returned will only be ones which have at least one action of this # type. action_type: ACTION_TYPE + + # Current status of a user. + statuses: [USER_STATUS] } type EditInfo { From a211f94a91a09ee49d7994673f18e935a5ab05b8 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 19 Sep 2017 13:11:23 -0300 Subject: [PATCH 15/26] Adding status to the FE Query --- .../coral-admin/src/routes/Community/containers/Community.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/coral-admin/src/routes/Community/containers/Community.js b/client/coral-admin/src/routes/Community/containers/Community.js index 2b9dbbe74..783611e6d 100644 --- a/client/coral-admin/src/routes/Community/containers/Community.js +++ b/client/coral-admin/src/routes/Community/containers/Community.js @@ -58,7 +58,8 @@ CommunityContainer.propTypes = { const withData = withQuery(gql` query TalkAdmin_FlaggedUsernamesCount { flaggedUsernamesCount: userCount(query: { - action_type: FLAG + action_type: FLAG, + statuses: [PENDING] }) ...${getDefinitionName(FlaggedAccounts.fragments.root)} ...${getDefinitionName(FlaggedUser.fragments.root)} From a5350c44aea6eeeb9c091756b1ad96efc477a1d9 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 19 Sep 2017 13:14:20 -0300 Subject: [PATCH 16/26] Adding PropTypes --- .../src/routes/Community/components/Community.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/coral-admin/src/routes/Community/components/Community.js b/client/coral-admin/src/routes/Community/components/Community.js index ed0ab728d..4ec881bdf 100644 --- a/client/coral-admin/src/routes/Community/components/Community.js +++ b/client/coral-admin/src/routes/Community/components/Community.js @@ -4,6 +4,7 @@ import CommunityMenu from './CommunityMenu'; import People from './People'; import FlaggedAccounts from '../containers/FlaggedAccounts'; import RejectUsernameDialog from './RejectUsernameDialog'; +import PropTypes from 'prop-types'; export default class Community extends Component { @@ -101,3 +102,14 @@ export default class Community extends Component { } } +Community.propTypes = { + community: PropTypes.object, + fetchAccounts: PropTypes.func, + hideRejectUsernameDialog: PropTypes.func, + updateSorting: PropTypes.func, + newPage: PropTypes.func, + route: PropTypes.object, + rejectUsername: PropTypes.func, + data: PropTypes.object, + root: PropTypes.object +}; From 7a4aae93b420044feb4c7f708fc132595aa9c2a0 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 19 Sep 2017 23:14:34 +0700 Subject: [PATCH 17/26] Introduce adminCommentLabels and implement Toxic comment label --- .../src/components/CommentLabels.css | 14 +++++++++++ .../src/components/CommentLabels.js | 20 +++++++++------- .../src/containers/CommentLabels.js | 6 +++++ .../src => coral-ui}/components/FlagLabel.css | 0 .../src => coral-ui}/components/FlagLabel.js | 0 .../src => coral-ui}/components/Label.css | 0 .../src => coral-ui}/components/Label.js | 5 ++-- client/coral-ui/index.js | 2 ++ .../client/components/ToxicLabel.js | 8 +++++++ .../client/containers/ToxicLabel.js | 24 +++++++++++++++++++ .../client/index.js | 2 ++ 11 files changed, 70 insertions(+), 11 deletions(-) rename client/{coral-admin/src => coral-ui}/components/FlagLabel.css (100%) rename client/{coral-admin/src => coral-ui}/components/FlagLabel.js (100%) rename client/{coral-admin/src => coral-ui}/components/Label.css (100%) rename client/{coral-admin/src => coral-ui}/components/Label.js (71%) create mode 100644 plugins/talk-plugin-toxic-comments/client/components/ToxicLabel.js create mode 100644 plugins/talk-plugin-toxic-comments/client/containers/ToxicLabel.js diff --git a/client/coral-admin/src/components/CommentLabels.css b/client/coral-admin/src/components/CommentLabels.css index 718cd7dfd..b7a5699fe 100644 --- a/client/coral-admin/src/components/CommentLabels.css +++ b/client/coral-admin/src/components/CommentLabels.css @@ -1,4 +1,18 @@ .root { + display: flex; + justify-content: flex-end; +} + +.coreLabels { + > *:not(:last-child) { + margin-right: 3px; + } +} + +.slot { + &:not(:empty) { + padding-left: 3px; + } > *:not(:last-child) { margin-right: 3px; } diff --git a/client/coral-admin/src/components/CommentLabels.js b/client/coral-admin/src/components/CommentLabels.js index 333f905ef..7af7e9a7e 100644 --- a/client/coral-admin/src/components/CommentLabels.js +++ b/client/coral-admin/src/components/CommentLabels.js @@ -1,7 +1,8 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Label from './Label'; -import FlagLabel from './FlagLabel'; +import Label from 'coral-ui/components/Label'; +import Slot from 'coral-framework/components/Slot'; +import FlagLabel from 'coral-ui/components/FlagLabel'; import cn from 'classnames'; import styles from './CommentLabels.css'; @@ -17,14 +18,17 @@ function hasHistoryFlag(actions) { return actions.some((action) => action.__typename === 'FlagAction' && action.reason === 'TRUST'); } -const CommentLabels = ({comment: {className, status, actions, hasParent}}) => { +const CommentLabels = ({comment, comment: {className, status, actions, hasParent}}) => { return (
    - {hasParent && } - {status === 'PREMOD' && } - {isUserFlagged(actions) && User} - {hasSuspectedWords(actions) && Suspect} - {hasHistoryFlag(actions) && History} +
    + {hasParent && } + {status === 'PREMOD' && } + {isUserFlagged(actions) && User} + {hasSuspectedWords(actions) && Suspect} + {hasHistoryFlag(actions) && History} +
    +
    ); }; diff --git a/client/coral-admin/src/containers/CommentLabels.js b/client/coral-admin/src/containers/CommentLabels.js index ab9639bcd..af6a0d963 100644 --- a/client/coral-admin/src/containers/CommentLabels.js +++ b/client/coral-admin/src/containers/CommentLabels.js @@ -1,6 +1,11 @@ import {gql} from 'react-apollo'; import CommentLabels from '../components/CommentLabels'; import withFragments from 'coral-framework/hocs/withFragments'; +import {getSlotFragmentSpreads} from 'coral-framework/utils'; + +const slots = [ + 'adminCommentLabels', +]; export default withFragments({ comment: gql` @@ -16,6 +21,7 @@ export default withFragments({ id } } + ${getSlotFragmentSpreads(slots, 'comment')} } ` })(CommentLabels); diff --git a/client/coral-admin/src/components/FlagLabel.css b/client/coral-ui/components/FlagLabel.css similarity index 100% rename from client/coral-admin/src/components/FlagLabel.css rename to client/coral-ui/components/FlagLabel.css diff --git a/client/coral-admin/src/components/FlagLabel.js b/client/coral-ui/components/FlagLabel.js similarity index 100% rename from client/coral-admin/src/components/FlagLabel.js rename to client/coral-ui/components/FlagLabel.js diff --git a/client/coral-admin/src/components/Label.css b/client/coral-ui/components/Label.css similarity index 100% rename from client/coral-admin/src/components/Label.css rename to client/coral-ui/components/Label.css diff --git a/client/coral-admin/src/components/Label.js b/client/coral-ui/components/Label.js similarity index 71% rename from client/coral-admin/src/components/Label.js rename to client/coral-ui/components/Label.js index 9d1b736de..a73b6183d 100644 --- a/client/coral-admin/src/components/Label.js +++ b/client/coral-ui/components/Label.js @@ -4,9 +4,9 @@ import styles from './Label.css'; import {Icon} from 'coral-ui'; import cn from 'classnames'; -const Label = ({iconName, children, className, isFlag}) => { +const Label = ({iconName, children, className}) => { return ( - + {children} ); @@ -14,7 +14,6 @@ const Label = ({iconName, children, className, isFlag}) => { Label.propTypes = { className: PropTypes.string, - isFlag: PropTypes.bool, children: PropTypes.node.isRequired, iconName: PropTypes.string, }; diff --git a/client/coral-ui/index.js b/client/coral-ui/index.js index 8a538d120..93fe5af64 100644 --- a/client/coral-ui/index.js +++ b/client/coral-ui/index.js @@ -26,3 +26,5 @@ export {default as Option} from './components/Option'; export {default as SnackBar} from './components/SnackBar'; export {default as TextArea} from './components/TextArea'; export {default as Drawer} from './components/Drawer'; +export {default as Label} from './components/Label'; +export {default as FlagLabel} from './components/FlagLabel'; diff --git a/plugins/talk-plugin-toxic-comments/client/components/ToxicLabel.js b/plugins/talk-plugin-toxic-comments/client/components/ToxicLabel.js new file mode 100644 index 000000000..3c17703dd --- /dev/null +++ b/plugins/talk-plugin-toxic-comments/client/components/ToxicLabel.js @@ -0,0 +1,8 @@ +import React from 'react'; +import {FlagLabel} from 'plugin-api/beta/client/components/ui'; + +const ToxicLabel = () => ( + Toxic +); + +export default ToxicLabel; diff --git a/plugins/talk-plugin-toxic-comments/client/containers/ToxicLabel.js b/plugins/talk-plugin-toxic-comments/client/containers/ToxicLabel.js new file mode 100644 index 000000000..0d2e9e14b --- /dev/null +++ b/plugins/talk-plugin-toxic-comments/client/containers/ToxicLabel.js @@ -0,0 +1,24 @@ +import {compose, gql} from 'react-apollo'; +import {withFragments, excludeIf} from 'plugin-api/beta/client/hocs'; +import ToxicLabel from '../components/ToxicLabel'; + +function isToxic(actions) { + return actions.some((action) => action.__typename === 'FlagAction' && action.reason === 'TOXIC_COMMENT'); +} + +const enhance = compose( + withFragments({ + comment: gql` + fragment TalkToxicComments_Comment on Comment { + actions { + __typename + ... on FlagAction { + reason + } + } + }`, + }), + excludeIf(({comment: {actions}}) => !isToxic(actions)), +); + +export default enhance(ToxicLabel); diff --git a/plugins/talk-plugin-toxic-comments/client/index.js b/plugins/talk-plugin-toxic-comments/client/index.js index bb0417606..8a16b8dea 100644 --- a/plugins/talk-plugin-toxic-comments/client/index.js +++ b/plugins/talk-plugin-toxic-comments/client/index.js @@ -1,9 +1,11 @@ import translations from './translations.yml'; import CheckToxicityHook from './containers/CheckToxicityHook'; +import ToxicLabel from './containers/ToxicLabel'; export default { translations, slots: { commentInputDetailArea: [CheckToxicityHook], + adminCommentLabels: [ToxicLabel], }, }; From b9086244e2070540fb24b2b9e0245c0e029a0fca Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 19 Sep 2017 13:24:36 -0300 Subject: [PATCH 18/26] More proptypes --- .../src/routes/Community/components/Community.js | 5 ++++- .../Community/containers/FlaggedAccounts.js | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/client/coral-admin/src/routes/Community/components/Community.js b/client/coral-admin/src/routes/Community/components/Community.js index 4ec881bdf..bf300a166 100644 --- a/client/coral-admin/src/routes/Community/components/Community.js +++ b/client/coral-admin/src/routes/Community/components/Community.js @@ -77,7 +77,10 @@ export default class Community extends Component { return (
    - + { - return this.props.setUserStatus({userId, status: 'APPROVED'}); + return this.props.setUserStatus({ + userId, + status: 'APPROVED' + }); } loadMore = () => { @@ -74,6 +78,16 @@ class FlaggedAccountsContainer extends Component { } } +FlaggedAccountsContainer.propTypes = { + showBanUserDialog: PropTypes.func, + showSuspendUserDialog: PropTypes.func, + showRejectUsernameDialog: PropTypes.func, + viewUserDetail: PropTypes.func, + setUserStatus: PropTypes.func, + data: PropTypes.object, + root: PropTypes.object +}; + const LOAD_MORE_QUERY = gql` query TalkAdmin_LoadMoreFlaggedAccounts($limit: Int, $cursor: Cursor) { users(query:{action_type: FLAG, statuses: [PENDING], limit: $limit, cursor: $cursor}){ From 7a74d5ee836c5e109b08a1d2851821d00405e8ab Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Tue, 19 Sep 2017 13:26:04 -0300 Subject: [PATCH 19/26] missing prop --- client/coral-admin/src/routes/Community/containers/Community.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/coral-admin/src/routes/Community/containers/Community.js b/client/coral-admin/src/routes/Community/containers/Community.js index 783611e6d..445b7f413 100644 --- a/client/coral-admin/src/routes/Community/containers/Community.js +++ b/client/coral-admin/src/routes/Community/containers/Community.js @@ -28,6 +28,7 @@ class CommunityContainer extends Component { render() { return Date: Tue, 19 Sep 2017 23:52:25 +0700 Subject: [PATCH 20/26] Fix SYSTEM_WITHHELD comments missing in all queue and user detail --- .../src/routes/Moderation/queueConfig.js | 1 - graph/loaders/comments.js | 14 ++++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/client/coral-admin/src/routes/Moderation/queueConfig.js b/client/coral-admin/src/routes/Moderation/queueConfig.js index b90979efe..f17c38a09 100644 --- a/client/coral-admin/src/routes/Moderation/queueConfig.js +++ b/client/coral-admin/src/routes/Moderation/queueConfig.js @@ -28,7 +28,6 @@ export default { name: t('modqueue.rejected'), }, all: { - statuses: ['NONE', 'PREMOD', 'ACCEPTED', 'REJECTED'], icon: 'question_answer', name: t('modqueue.all'), }, diff --git a/graph/loaders/comments.js b/graph/loaders/comments.js index 386d33fd1..e17877098 100644 --- a/graph/loaders/comments.js +++ b/graph/loaders/comments.js @@ -281,12 +281,14 @@ const getCommentsByQuery = async (ctx, {ids, statuses, asset_id, parent_id, auth // Only administrators can search for comments with statuses that are not // `null`, or `'ACCEPTED'`. - if (ctx.user != null && ctx.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS) && statuses && statuses.length > 0) { - comments = comments.where({ - status: { - $in: statuses - } - }); + if (ctx.user != null && ctx.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) { + if (statuses && statuses.length > 0) { + comments = comments.where({ + status: { + $in: statuses + } + }); + } } else { comments = comments.where({ status: { From b482976c582f82bd0223229653b4e2766d562f01 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 20 Sep 2017 02:39:31 +0700 Subject: [PATCH 21/26] Allow querying for all statuses --- .../Moderation/containers/Moderation.js | 16 +++++++----- graph/loaders/comments.js | 26 ++++++++++++------- graph/resolvers/asset.js | 1 + graph/resolvers/comment.js | 1 + graph/typeDefs.graphql | 15 +++++++---- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/client/coral-admin/src/routes/Moderation/containers/Moderation.js b/client/coral-admin/src/routes/Moderation/containers/Moderation.js index 2ff6b6732..baf6b8dfd 100644 --- a/client/coral-admin/src/routes/Moderation/containers/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/containers/Moderation.js @@ -170,7 +170,8 @@ class ModerationContainer extends Component { cursor: this.props.root[tab].endCursor, sortOrder: this.props.data.variables.sortOrder, asset_id: this.props.data.variables.asset_id, - statuses: this.props.queueConfig[tab].statuses, + statuses: this.props.queueConfig[tab].statuses || null, + tags: this.props.queueConfig[tab].tags || null, action_type: this.props.queueConfig[tab].action_type, }; return this.props.data.fetchMore({ @@ -214,7 +215,7 @@ class ModerationContainer extends Component { return ; } - const premodEnabled = assetId ? isPremod(asset.settings.moderation) : + const premodEnabled = assetId ? isPremod(asset.settings.moderation) : isPremod(settings.moderation); const currentQueueConfig = Object.assign({}, this.props.queueConfig); @@ -293,8 +294,8 @@ const COMMENT_REJECTED_SUBSCRIPTION = gql` `; const LOAD_MORE_QUERY = gql` - query CoralAdmin_Moderation_LoadMore($limit: Int = 10, $cursor: Cursor, $sortOrder: SORT_ORDER, $asset_id: ID, $statuses:[COMMENT_STATUS!], $action_type: ACTION_TYPE) { - comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sortOrder: $sortOrder, action_type: $action_type}) { + query CoralAdmin_Moderation_LoadMore($limit: Int = 10, $cursor: Cursor, $sortOrder: SORT_ORDER, $asset_id: ID, $tags:[String!], $statuses:[COMMENT_STATUS!], $action_type: ACTION_TYPE) { + comments(query: {limit: $limit, cursor: $cursor, asset_id: $asset_id, statuses: $statuses, sortOrder: $sortOrder, action_type: $action_type, tags: $tags}) { nodes { ...${getDefinitionName(Comment.fragments.comment)} } @@ -319,10 +320,10 @@ const commentConnectionFragment = gql` `; const withModQueueQuery = withQuery(({queueConfig}) => gql` - query CoralAdmin_Moderation($asset_id: ID, $sortOrder: SORT_ORDER, $allAssets: Boolean!) { + query CoralAdmin_Moderation($asset_id: ID, $sortOrder: SORT_ORDER, $allAssets: Boolean!, $nullStatuses: [COMMENT_STATUS!]) { ${Object.keys(queueConfig).map((queue) => ` ${queue}: comments(query: { - ${queueConfig[queue].statuses ? `statuses: [${queueConfig[queue].statuses.join(', ')}],` : ''} + statuses: ${queueConfig[queue].statuses ? `[${queueConfig[queue].statuses.join(', ')}],` : '$nullStatuses'} ${queueConfig[queue].tags ? `tags: ["${queueConfig[queue].tags.join('", "')}"],` : ''} ${queueConfig[queue].action_type ? `action_type: ${queueConfig[queue].action_type}` : ''} asset_id: $asset_id, @@ -333,7 +334,7 @@ const withModQueueQuery = withQuery(({queueConfig}) => gql` `)} ${Object.keys(queueConfig).map((queue) => ` ${queue}Count: commentCount(query: { - ${queueConfig[queue].statuses ? `statuses: [${queueConfig[queue].statuses.join(', ')}],` : ''} + statuses: ${queueConfig[queue].statuses ? `[${queueConfig[queue].statuses.join(', ')}],` : '$nullStatuses'} ${queueConfig[queue].tags ? `tags: ["${queueConfig[queue].tags.join('", "')}"],` : ''} ${queueConfig[queue].action_type ? `action_type: ${queueConfig[queue].action_type}` : ''} asset_id: $asset_id, @@ -361,6 +362,7 @@ const withModQueueQuery = withQuery(({queueConfig}) => gql` asset_id: id, sortOrder: props.moderation.sortOrder, allAssets: id === null, + nullStatuses: null, }, fetchPolicy: 'network-only' }; diff --git a/graph/loaders/comments.js b/graph/loaders/comments.js index e17877098..adfa71eb4 100644 --- a/graph/loaders/comments.js +++ b/graph/loaders/comments.js @@ -91,12 +91,19 @@ const getParentCountsByAssetID = (context, asset_ids) => { const getCommentCountByQuery = (context, {ids, statuses, asset_id, parent_id, author_id, tags, action_type}) => { let query = CommentModel.find(); - if (ids) { - query = query.where({id: {$in: ids}}); + if ( + (context.user != null && context.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) || + statuses && statuses.every((status) => ['NONE', 'ACCEPTED'].includes(status)) + ) { + if (statuses) { + query = query.where({status: {$in: statuses}}); + } + } else { + return null; } - if (statuses) { - query = query.where({status: {$in: statuses}}); + if (ids) { + query = query.where({id: {$in: ids}}); } if (asset_id != null) { @@ -281,7 +288,10 @@ const getCommentsByQuery = async (ctx, {ids, statuses, asset_id, parent_id, auth // Only administrators can search for comments with statuses that are not // `null`, or `'ACCEPTED'`. - if (ctx.user != null && ctx.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) { + if ( + (ctx.user != null && ctx.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) || + statuses && statuses.every((status) => ['NONE', 'ACCEPTED'].includes(status)) + ) { if (statuses && statuses.length > 0) { comments = comments.where({ status: { @@ -290,11 +300,7 @@ const getCommentsByQuery = async (ctx, {ids, statuses, asset_id, parent_id, auth }); } } else { - comments = comments.where({ - status: { - $in: ['NONE', 'ACCEPTED'] - } - }); + return null; } if (ctx.user != null && ctx.user.can(SEARCH_OTHERS_COMMENTS) && action_type) { diff --git a/graph/resolvers/asset.js b/graph/resolvers/asset.js index b5fd92d34..2680f9f76 100644 --- a/graph/resolvers/asset.js +++ b/graph/resolvers/asset.js @@ -23,6 +23,7 @@ const Asset = { // Include the asset id in the search. query.asset_id = id; + query.statuses = ['NONE', 'ACCEPTED']; return Comments.getByQuery(query); }, diff --git a/graph/resolvers/comment.js b/graph/resolvers/comment.js index cd1e93141..8e063ad3c 100644 --- a/graph/resolvers/comment.js +++ b/graph/resolvers/comment.js @@ -26,6 +26,7 @@ const Comment = { query.asset_id = asset_id; query.parent_id = id; + query.statuses = ['NONE', 'ACCEPTED']; return Comments.getByQuery(query); }, diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index f36874639..5b3fe7ef9 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -264,8 +264,9 @@ input CommentsQuery { # Author of the comments author_id: ID - # Current status of a comment. Requires the `ADMIN` role. - statuses: [COMMENT_STATUS!] + # Current status of a comment. + # This field is restricted. + statuses: [COMMENT_STATUS!] = [NONE, ACCEPTED] # Asset that a comment is on. asset_id: ID @@ -317,8 +318,9 @@ input RepliesQuery { # methods. input CommentCountQuery { - # Current status of a comment. Requires the `ADMIN` role. - statuses: [COMMENT_STATUS!] + # Current status of a comment. + # This field is restricted. + statuses: [COMMENT_STATUS!] = [NONE, ACCEPTED] # Asset that a comment is on. asset_id: ID @@ -688,7 +690,7 @@ type Asset { # The comments that are attached to the asset. When `deep` is true, the # comments returned will be at all depths. - comments(query: CommentsQuery = {}, deep: Boolean = false): CommentConnection! + comments(query: CommentsQuery = {}, deep: Boolean = false): CommentConnection # A Comment from the Asset by comment's ID comment(id: ID!): Comment @@ -875,6 +877,9 @@ type CreateCommentResponse implements Response { # The comment that was created. comment: Comment + # Flags that was assigned during creation of the comment. + flags: [FlagAction] + # An array of errors relating to the mutation that occurred. errors: [UserError!] } From 5f7c4f82e72ed89cc83e8a1c2f8f13ba4400b6ac Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 20 Sep 2017 02:50:43 +0700 Subject: [PATCH 22/26] Return flags when creating comment, restrict access to actions in comment resolver --- client/coral-embed-stream/src/graphql/index.js | 14 +++++--------- client/talk-plugin-commentbox/CommentBox.js | 11 ++++++----- graph/resolvers/root_mutation.js | 12 +++++++++--- .../client/components/CheckToxicityHook.js | 4 ++-- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index 82d6ca9e3..d7af76d82 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -64,6 +64,10 @@ export default { hasNextPage } } + flags { + reason + message + } } fragment CoralEmbedStream_CreateCommentResponse_Comment on Comment { @@ -77,14 +81,6 @@ export default { title url } - actions { - __typename - id - ... on FlagAction { - reason - message - } - } tags { tag { name @@ -124,6 +120,7 @@ export default { createComment: { __typename: 'CreateCommentResponse', errors: null, + flags: [], comment: { __typename: 'Comment', user: { @@ -133,7 +130,6 @@ export default { }, created_at: new Date().toISOString(), body, - actions: [], action_summaries: [], tags: tags.map((tag) => ({ tag: { diff --git a/client/talk-plugin-commentbox/CommentBox.js b/client/talk-plugin-commentbox/CommentBox.js index 409ad86ec..49c4df0eb 100644 --- a/client/talk-plugin-commentbox/CommentBox.js +++ b/client/talk-plugin-commentbox/CommentBox.js @@ -13,18 +13,18 @@ export const name = 'talk-plugin-commentbox'; const notifyReasons = ['LINKS', 'TRUST']; -function shouldNotify(actions = []) { - return actions.some(({reason}) => notifyReasons.includes(reason)); +function shouldNotify(flags = []) { + return flags.some(({reason}) => notifyReasons.includes(reason)); } // Given a newly posted comment's status, show a notification to the user // if needed -export const notifyForNewCommentStatus = (notify, comment) => { +export const notifyForNewCommentStatus = (notify, comment, flags) => { if (comment.status === 'REJECTED') { notify('error', t('comment_box.comment_post_banned_word')); } else if ( comment.status === 'PREMOD' || - comment.status === 'SYSTEM_WITHHELD' && shouldNotify(comment.actions)) { + comment.status === 'SYSTEM_WITHHELD' && shouldNotify(flags)) { notify('success', t('comment_box.comment_post_notif_premod')); } }; @@ -78,11 +78,12 @@ class CommentBox extends React.Component { .then(({data}) => { this.setState({loadingState: 'success', body: ''}); const postedComment = data.createComment.comment; + const flags = data.createComment.flags; // Execute postSubmit Hooks this.state.hooks.postSubmit.forEach((hook) => hook(data)); - notifyForNewCommentStatus(notify, postedComment); + notifyForNewCommentStatus(notify, postedComment, flags); if (commentPostedHandler) { commentPostedHandler(); diff --git a/graph/resolvers/root_mutation.js b/graph/resolvers/root_mutation.js index df67b8e8a..c7794303b 100644 --- a/graph/resolvers/root_mutation.js +++ b/graph/resolvers/root_mutation.js @@ -1,7 +1,13 @@ const RootMutation = { - createComment: async (_, {input}, {mutators: {Comment}}) => ({ - comment: await Comment.create(input), - }), + createComment: async (_, {input}, {mutators: {Comment}, loaders: {Actions}}) => { + const comment = await Comment.create(input); + + // Retrieve flags that was assigned to comment. + const actions = await Actions.getByID.load(comment.id); + const flags = actions.filter(({action_type}) => action_type === 'FLAG'); + + return {comment, flags}; + }, editComment: async (_, {id, asset_id, edit: {body}}, {mutators: {Comment}}) => ({ comment: await Comment.edit({id, asset_id, edit: {body}}), }), diff --git a/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js b/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js index 8572bdd5a..44e2e89a7 100644 --- a/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js +++ b/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js @@ -23,8 +23,8 @@ export default class CheckToxicityHook extends React.Component { }); this.toxicityPostHook = this.props.registerHook('postSubmit', (result) => { - const actions = result.createComment.comment && result.createComment.comment.actions; - if (actions && actions.some(({reason}) => reason === 'TOXIC_COMMENT')) { + const flags = result.createComment.flags; + if (flags && flags.some(({reason}) => reason === 'TOXIC_COMMENT')) { this.props.notify('error', t('talk-plugin-toxic-comments.still_toxic')); } From 42954fb84ab4b9e7651e66eeafe0da76a369fa07 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 20 Sep 2017 04:17:15 +0700 Subject: [PATCH 23/26] Return all actions in CreateCommentResponse --- client/coral-embed-stream/src/graphql/index.js | 11 +++++++---- client/talk-plugin-commentbox/CommentBox.js | 12 ++++++------ graph/resolvers/root_mutation.js | 5 ++--- graph/typeDefs.graphql | 6 +++--- .../client/components/CheckToxicityHook.js | 4 ++-- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index d7af76d82..20ed8a1d6 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -64,9 +64,12 @@ export default { hasNextPage } } - flags { - reason - message + actions { + __typename + ... on FlagAction { + reason + message + } } } @@ -120,7 +123,7 @@ export default { createComment: { __typename: 'CreateCommentResponse', errors: null, - flags: [], + actions: [], comment: { __typename: 'Comment', user: { diff --git a/client/talk-plugin-commentbox/CommentBox.js b/client/talk-plugin-commentbox/CommentBox.js index 49c4df0eb..b210f4e79 100644 --- a/client/talk-plugin-commentbox/CommentBox.js +++ b/client/talk-plugin-commentbox/CommentBox.js @@ -13,18 +13,18 @@ export const name = 'talk-plugin-commentbox'; const notifyReasons = ['LINKS', 'TRUST']; -function shouldNotify(flags = []) { - return flags.some(({reason}) => notifyReasons.includes(reason)); +function shouldNotify(actions = []) { + return actions.some(({__typename, reason}) => __typename === 'FlagAction' && notifyReasons.includes(reason)); } // Given a newly posted comment's status, show a notification to the user // if needed -export const notifyForNewCommentStatus = (notify, comment, flags) => { +export const notifyForNewCommentStatus = (notify, comment, actions) => { if (comment.status === 'REJECTED') { notify('error', t('comment_box.comment_post_banned_word')); } else if ( comment.status === 'PREMOD' || - comment.status === 'SYSTEM_WITHHELD' && shouldNotify(flags)) { + comment.status === 'SYSTEM_WITHHELD' && shouldNotify(actions)) { notify('success', t('comment_box.comment_post_notif_premod')); } }; @@ -78,12 +78,12 @@ class CommentBox extends React.Component { .then(({data}) => { this.setState({loadingState: 'success', body: ''}); const postedComment = data.createComment.comment; - const flags = data.createComment.flags; + const actions = data.createComment.actions; // Execute postSubmit Hooks this.state.hooks.postSubmit.forEach((hook) => hook(data)); - notifyForNewCommentStatus(notify, postedComment, flags); + notifyForNewCommentStatus(notify, postedComment, actions); if (commentPostedHandler) { commentPostedHandler(); diff --git a/graph/resolvers/root_mutation.js b/graph/resolvers/root_mutation.js index c7794303b..17235a59e 100644 --- a/graph/resolvers/root_mutation.js +++ b/graph/resolvers/root_mutation.js @@ -2,11 +2,10 @@ const RootMutation = { createComment: async (_, {input}, {mutators: {Comment}, loaders: {Actions}}) => { const comment = await Comment.create(input); - // Retrieve flags that was assigned to comment. + // Retrieve actions that was assigned to comment. const actions = await Actions.getByID.load(comment.id); - const flags = actions.filter(({action_type}) => action_type === 'FLAG'); - return {comment, flags}; + return {comment, actions}; }, editComment: async (_, {id, asset_id, edit: {body}}, {mutators: {Comment}}) => ({ comment: await Comment.edit({id, asset_id, edit: {body}}), diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 5b3fe7ef9..71928f49e 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -261,7 +261,7 @@ enum ACTION_TYPE { # CommentsQuery allows the ability to query comments by a specific methods. input CommentsQuery { - # Author of the comments + # Author of the commente author_id: ID # Current status of a comment. @@ -877,8 +877,8 @@ type CreateCommentResponse implements Response { # The comment that was created. comment: Comment - # Flags that was assigned during creation of the comment. - flags: [FlagAction] + # Actions that was assigned during creation of the comment. + actions: [Action] # An array of errors relating to the mutation that occurred. errors: [UserError!] diff --git a/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js b/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js index 44e2e89a7..e4bd68097 100644 --- a/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js +++ b/plugins/talk-plugin-toxic-comments/client/components/CheckToxicityHook.js @@ -23,8 +23,8 @@ export default class CheckToxicityHook extends React.Component { }); this.toxicityPostHook = this.props.registerHook('postSubmit', (result) => { - const flags = result.createComment.flags; - if (flags && flags.some(({reason}) => reason === 'TOXIC_COMMENT')) { + const actions = result.createComment.actions; + if (actions && actions.some(({__typename, reason}) => __typename === 'FlagAction' && reason === 'TOXIC_COMMENT')) { this.props.notify('error', t('talk-plugin-toxic-comments.still_toxic')); } From c3e24a045131a09a15ae5f396ddf5cd960feeae1 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 20 Sep 2017 04:29:03 +0700 Subject: [PATCH 24/26] Apply suggenstions --- graph/loaders/comments.js | 34 ++++++++++++++++------------------ graph/mutators/comment.js | 2 +- graph/resolvers/asset.js | 1 - 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/graph/loaders/comments.js b/graph/loaders/comments.js index adfa71eb4..2eb0811a6 100644 --- a/graph/loaders/comments.js +++ b/graph/loaders/comments.js @@ -91,17 +91,19 @@ const getParentCountsByAssetID = (context, asset_ids) => { const getCommentCountByQuery = (context, {ids, statuses, asset_id, parent_id, author_id, tags, action_type}) => { let query = CommentModel.find(); + // If user queries for statuses other than NONE and/or ACCEPTED statuses, it needs + // special priviledges. if ( - (context.user != null && context.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) || - statuses && statuses.every((status) => ['NONE', 'ACCEPTED'].includes(status)) + statuses && statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status)) && + (context.user == null || !context.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) ) { - if (statuses) { - query = query.where({status: {$in: statuses}}); - } - } else { return null; } + if (statuses) { + query = query.where({status: {$in: statuses}}); + } + if (ids) { query = query.where({id: {$in: ids}}); } @@ -286,23 +288,19 @@ const executeWithSort = async (ctx, query, {cursor, sortOrder, sortBy, limit}) = const getCommentsByQuery = async (ctx, {ids, statuses, asset_id, parent_id, author_id, limit, cursor, sortOrder, sortBy, excludeIgnored, tags, action_type}) => { let comments = CommentModel.find(); - // Only administrators can search for comments with statuses that are not - // `null`, or `'ACCEPTED'`. + // If user queries for statuses other than NONE and/or ACCEPTED statuses, it needs + // special priviledges. if ( - (ctx.user != null && ctx.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) || - statuses && statuses.every((status) => ['NONE', 'ACCEPTED'].includes(status)) + statuses && statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status)) && + (ctx.user == null || !ctx.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) ) { - if (statuses && statuses.length > 0) { - comments = comments.where({ - status: { - $in: statuses - } - }); - } - } else { return null; } + if (statuses) { + comments = comments.where({status: {$in: statuses}}); + } + if (ctx.user != null && ctx.user.can(SEARCH_OTHERS_COMMENTS) && action_type) { comments = comments.where({ [`action_counts.${sc(action_type.toLowerCase())}`]: { diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js index 2aee766a5..a1315d847 100644 --- a/graph/mutators/comment.js +++ b/graph/mutators/comment.js @@ -398,7 +398,7 @@ const moderationPhases = [ * @param {String} body body of the comment * @param {String} [asset_id] asset for the comment * @param {Object} [wordlist={}] the results of the wordlist scan - * @return {Object} resolves to the comment's status and actions + * @return {Promise} resolves to the comment's status and actions */ const resolveCommentModeration = async (context, comment) => { diff --git a/graph/resolvers/asset.js b/graph/resolvers/asset.js index 2680f9f76..b5fd92d34 100644 --- a/graph/resolvers/asset.js +++ b/graph/resolvers/asset.js @@ -23,7 +23,6 @@ const Asset = { // Include the asset id in the search. query.asset_id = id; - query.statuses = ['NONE', 'ACCEPTED']; return Comments.getByQuery(query); }, From 2d96163b76d4f5fb014e75711443dce8ed956344 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 20 Sep 2017 04:35:14 +0700 Subject: [PATCH 25/26] Disallow querying for all comments unpriviledgly --- graph/loaders/comments.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graph/loaders/comments.js b/graph/loaders/comments.js index 2eb0811a6..d79b5dc74 100644 --- a/graph/loaders/comments.js +++ b/graph/loaders/comments.js @@ -94,7 +94,7 @@ const getCommentCountByQuery = (context, {ids, statuses, asset_id, parent_id, au // If user queries for statuses other than NONE and/or ACCEPTED statuses, it needs // special priviledges. if ( - statuses && statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status)) && + !statuses || statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status)) && (context.user == null || !context.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) ) { return null; @@ -291,7 +291,7 @@ const getCommentsByQuery = async (ctx, {ids, statuses, asset_id, parent_id, auth // If user queries for statuses other than NONE and/or ACCEPTED statuses, it needs // special priviledges. if ( - statuses && statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status)) && + !statuses || statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status)) && (ctx.user == null || !ctx.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) ) { return null; From 961111c3f7f26fdda4e89cb00023833dcd6b3b71 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 20 Sep 2017 04:36:58 +0700 Subject: [PATCH 26/26] Allow priviledged user to query for all statuses --- graph/loaders/comments.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graph/loaders/comments.js b/graph/loaders/comments.js index d79b5dc74..b6a347b8e 100644 --- a/graph/loaders/comments.js +++ b/graph/loaders/comments.js @@ -94,7 +94,7 @@ const getCommentCountByQuery = (context, {ids, statuses, asset_id, parent_id, au // If user queries for statuses other than NONE and/or ACCEPTED statuses, it needs // special priviledges. if ( - !statuses || statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status)) && + (!statuses || statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status))) && (context.user == null || !context.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) ) { return null; @@ -291,7 +291,7 @@ const getCommentsByQuery = async (ctx, {ids, statuses, asset_id, parent_id, auth // If user queries for statuses other than NONE and/or ACCEPTED statuses, it needs // special priviledges. if ( - !statuses || statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status)) && + (!statuses || statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status))) && (ctx.user == null || !ctx.user.can(SEARCH_NON_NULL_OR_ACCEPTED_COMMENTS)) ) { return null;