From 95580f5ffe4d0796f7f886e8757074fe04e73d47 Mon Sep 17 00:00:00 2001 From: Vicky Chijwani Date: Thu, 5 Oct 2017 05:32:37 +0530 Subject: [PATCH 01/14] Add Slack notifications plugin for new comments. --- .gitignore | 1 + .../.eslintrc.json | 3 ++ .../talk-plugin-slack-notifications/index.js | 5 +++ .../package.json | 9 ++++ .../server/config.js | 11 +++++ .../server/hooks.js | 44 +++++++++++++++++++ 6 files changed, 73 insertions(+) create mode 100644 plugins/talk-plugin-slack-notifications/.eslintrc.json create mode 100644 plugins/talk-plugin-slack-notifications/index.js create mode 100644 plugins/talk-plugin-slack-notifications/package.json create mode 100644 plugins/talk-plugin-slack-notifications/server/config.js create mode 100644 plugins/talk-plugin-slack-notifications/server/hooks.js diff --git a/.gitignore b/.gitignore index 9c55f2708..0ab845e4e 100644 --- a/.gitignore +++ b/.gitignore @@ -45,5 +45,6 @@ plugins/* !plugins/talk-plugin-deep-reply-count !plugins/talk-plugin-subscriber !plugins/talk-plugin-flag-details +!plugins/talk-plugin-slack-notifications **/node_modules/* diff --git a/plugins/talk-plugin-slack-notifications/.eslintrc.json b/plugins/talk-plugin-slack-notifications/.eslintrc.json new file mode 100644 index 000000000..78f7c2397 --- /dev/null +++ b/plugins/talk-plugin-slack-notifications/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "@coralproject/eslint-config-talk" +} diff --git a/plugins/talk-plugin-slack-notifications/index.js b/plugins/talk-plugin-slack-notifications/index.js new file mode 100644 index 000000000..1f2a73ff0 --- /dev/null +++ b/plugins/talk-plugin-slack-notifications/index.js @@ -0,0 +1,5 @@ +const hooks = require('./server/hooks'); + +module.exports = { + hooks, +}; diff --git a/plugins/talk-plugin-slack-notifications/package.json b/plugins/talk-plugin-slack-notifications/package.json new file mode 100644 index 000000000..6792e77fa --- /dev/null +++ b/plugins/talk-plugin-slack-notifications/package.json @@ -0,0 +1,9 @@ +{ + "name": "@coralproject/talk-plugin-slack-notifications", + "pluginName": "talk-plugin-slack-notifications", + "version": "0.0.1", + "description": "Posts new comments to Slack via a webhook", + "main": "index.js", + "author": "The Coral Project Team ", + "license": "Apache-2.0" +} diff --git a/plugins/talk-plugin-slack-notifications/server/config.js b/plugins/talk-plugin-slack-notifications/server/config.js new file mode 100644 index 000000000..7571565b0 --- /dev/null +++ b/plugins/talk-plugin-slack-notifications/server/config.js @@ -0,0 +1,11 @@ +const config = { + SLACK_WEBHOOK_URL: process.env.TALK_SLACK_WEBHOOK_URL, + SLACK_WEBHOOK_TIMEOUT: process.env.TALK_SLACK_WEBHOOK_TIMEOUT || 5000, +}; + +if (process.env.NODE_ENV !== 'test' && !config.SLACK_WEBHOOK_URL) { + // TODO this error should point users to Talk's Slack app once that's in place + throw new Error('Please set the TALK_SLACK_WEBHOOK_URL environment variable to use the slack-notifications plugin.'); +} + +module.exports = config; diff --git a/plugins/talk-plugin-slack-notifications/server/hooks.js b/plugins/talk-plugin-slack-notifications/server/hooks.js new file mode 100644 index 000000000..a2f3ca743 --- /dev/null +++ b/plugins/talk-plugin-slack-notifications/server/hooks.js @@ -0,0 +1,44 @@ +const fetch = require('node-fetch'); +const {SLACK_WEBHOOK_URL, SLACK_WEBHOOK_TIMEOUT} = require('./config'); +const debug = require('debug')('talk:plugin:slack-notifications'); + +// We don't add the hooks during _test_ as the Slack API is not available. +if (process.env.NODE_ENV === 'test') { + return null; +} + +module.exports = { + RootMutation: { + createComment: { + async post(_, {input}, context, _info, result) { + debug(`Posting notification to Slack webhook: ${SLACK_WEBHOOK_URL}`); + const { + comment: { + body: text, + created_at: createdAt + } + } = result; + const username = context.user.username; + const response = await fetch(SLACK_WEBHOOK_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + timeout: SLACK_WEBHOOK_TIMEOUT, + body: JSON.stringify({ + attachments: [{ + text: text, + footer: `Comment by ${username}`, + ts: Math.floor(Date.parse(createdAt) / 1000), + }] + }), + }); + if (!response.ok) { + const responseText = await response.text(); + console.trace(`Posting to Slack failed with HTTP code ${response.status} and body '${responseText}'`); + } + return result; + }, + }, + }, +}; From 85f9000a64f5d461a6150928c375b78b4d904d19 Mon Sep 17 00:00:00 2001 From: tylerbrewer2 Date: Wed, 4 Oct 2017 19:38:47 -0400 Subject: [PATCH 02/14] fix admin navigation width --- client/coral-admin/src/components/ui/Header.css | 7 +++---- client/coral-admin/src/components/ui/Layout.css | 1 - .../src/routes/Community/components/FlaggedAccounts.css | 2 ++ .../src/routes/Configure/components/Configure.css | 2 ++ .../src/routes/Dashboard/components/Dashboard.css | 2 ++ .../coral-admin/src/routes/Stories/components/Stories.css | 2 ++ 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/client/coral-admin/src/components/ui/Header.css b/client/coral-admin/src/components/ui/Header.css index f85a7cdf9..1bb08b8a2 100644 --- a/client/coral-admin/src/components/ui/Header.css +++ b/client/coral-admin/src/components/ui/Header.css @@ -16,19 +16,18 @@ } .header { - background-color: transparent; box-shadow: none; min-height: 58px; display: block; + background-color: #696969; + box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12); } .header > div { - background-color: #696969; position: relative; padding: 0; - min-width: 1280px; + width: 1280px; margin: 0 auto; - box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12); height: 58px; } diff --git a/client/coral-admin/src/components/ui/Layout.css b/client/coral-admin/src/components/ui/Layout.css index a3cc7b7b6..924c6ced7 100644 --- a/client/coral-admin/src/components/ui/Layout.css +++ b/client/coral-admin/src/components/ui/Layout.css @@ -1,5 +1,4 @@ .layout { - max-width: 1280px; margin: 0 auto; background-color: #FAFAFA; } diff --git a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css index 4e9600cbd..a41bda485 100644 --- a/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css +++ b/client/coral-admin/src/routes/Community/components/FlaggedAccounts.css @@ -2,6 +2,8 @@ padding: 10px; display: flex; padding-bottom: 200px; + max-width: 1280px; + margin: 0 auto; } .mainFlaggedContent { diff --git a/client/coral-admin/src/routes/Configure/components/Configure.css b/client/coral-admin/src/routes/Configure/components/Configure.css index 0b7319132..eae75caba 100644 --- a/client/coral-admin/src/routes/Configure/components/Configure.css +++ b/client/coral-admin/src/routes/Configure/components/Configure.css @@ -3,6 +3,8 @@ */ .container { + max-width: 1280px; + margin: 0 auto; display: flex; h3 { diff --git a/client/coral-admin/src/routes/Dashboard/components/Dashboard.css b/client/coral-admin/src/routes/Dashboard/components/Dashboard.css index 586ff6c08..aee7e702b 100644 --- a/client/coral-admin/src/routes/Dashboard/components/Dashboard.css +++ b/client/coral-admin/src/routes/Dashboard/components/Dashboard.css @@ -4,6 +4,8 @@ .Dashboard { display: flex; + max-width: 1280px; + margin: 0 auto; } .heading { diff --git a/client/coral-admin/src/routes/Stories/components/Stories.css b/client/coral-admin/src/routes/Stories/components/Stories.css index d8a75a4ce..be6b6f921 100644 --- a/client/coral-admin/src/routes/Stories/components/Stories.css +++ b/client/coral-admin/src/routes/Stories/components/Stories.css @@ -1,6 +1,8 @@ .container { padding: 10px; display: flex; + max-width: 1280px; + margin: 0 auto; } .leftColumn { From b3486ce61427a2b708f78f1f52e156e1b6a8eebe Mon Sep 17 00:00:00 2001 From: Rishabh04-02 Date: Thu, 5 Oct 2017 10:44:12 +0530 Subject: [PATCH 03/14] displaying time in X minutes Y seconds --- .../src/components/CountdownSeconds.js | 18 ++++++++++++++---- locales/en.yml | 2 ++ locales/es.yml | 2 ++ locales/fr.yml | 2 ++ locales/pt_BR.yml | 2 ++ 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/client/coral-embed-stream/src/components/CountdownSeconds.js b/client/coral-embed-stream/src/components/CountdownSeconds.js index 789b674b5..abcc1e398 100644 --- a/client/coral-embed-stream/src/components/CountdownSeconds.js +++ b/client/coral-embed-stream/src/components/CountdownSeconds.js @@ -35,16 +35,26 @@ export class CountdownSeconds extends React.Component { const {until, classNameForMsRemaining} = this.props; const msRemaining = until - now; const secRemaining = msRemaining / 1000; - const wholeSecRemaining = Math.floor(secRemaining); - const plural = secRemaining !== 1; - const units = t(plural ? 'edit_comment.seconds_plural' : 'edit_comment.second'); + const minRemaining = secRemaining / 60; + const secToMinRemaining = secRemaining % 60; + const wholeMinRemaining = Math.floor(minRemaining); + const wholeSecRemaining = Math.floor(secToMinRemaining); + const secUnit = t(wholeSecRemaining !== 1 ? 'edit_comment.seconds_plural' : 'edit_comment.second'); + const minUnit = t(wholeMinRemaining !== 1 ? 'edit_comment.minutes_plural' : 'edit_comment.minute'); let classFromProp; if (typeof classNameForMsRemaining === 'function') { classFromProp = classNameForMsRemaining(msRemaining); } + if (wholeMinRemaining > 0) { + return ( + + {`${wholeMinRemaining} ${minUnit} ${wholeSecRemaining} ${secUnit}`} + + ); + } return ( - {`${wholeSecRemaining} ${units}`} + {`${wholeSecRemaining} ${secUnit}`} ); } diff --git a/locales/en.yml b/locales/en.yml index a979739b5..836239fc4 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -172,6 +172,8 @@ en: edit_window_timer_prefix: "Edit Window: " second: "second" seconds_plural: "seconds" + minute: "minute" + minutes_plural: "minutes" email: confirm: has_been_requested: "A email confirmation has been requested for the following account:" diff --git a/locales/es.yml b/locales/es.yml index e9844667d..e21fc274c 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -171,6 +171,8 @@ es: edit_window_timer_prefix: "Ventana Edición:" second: "segundo" seconds_plural: "segundos" + minute: "minuto" + minutes_plural: "minutos" email: confirm: has_been_requested: "Un correo de confirmación ha sido pedido para la siguiente cuenta:" diff --git a/locales/fr.yml b/locales/fr.yml index f8ff403e7..f703c87f1 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -140,6 +140,8 @@ fr: edit_window_timer_prefix: "Fenêtre d'édition :" second: "seconde" seconds_plural: "secondes" + minute: "minute" + minutes_plural: "minutes" unexpected_error: "Erreur inattendue lors de l'enregistrement des modifications. Désolé !" embedlink: copy: "Copier dans le presse-papier" diff --git a/locales/pt_BR.yml b/locales/pt_BR.yml index 3f2c92628..c8a58f77a 100644 --- a/locales/pt_BR.yml +++ b/locales/pt_BR.yml @@ -163,6 +163,8 @@ pt_BR: edit_window_timer_prefix: "Para editar:" second: "segundo" seconds_plural: "segundos" + minute: "minuto" + minutes_plural: "minutos" email: confirm: has_been_requested: "Uma confirmação de e-mail foi solicitada para a seguinte conta:" From f330c385e552a33cfd19d22d6ec7c22d1ffbb68f Mon Sep 17 00:00:00 2001 From: Kim Gardner Date: Thu, 5 Oct 2017 12:29:27 +0100 Subject: [PATCH 04/14] Clean up README --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 919093750..057b29cf1 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,14 @@ # Talk [![CircleCI](https://circleci.com/gh/coralproject/talk.svg?style=svg)](https://circleci.com/gh/coralproject/talk) + [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://dashboard.heroku.com/new?template=https%3A%2F%2Fgithub.com%2Fcoralproject%2Ftalk&env[TALK_FACEBOOK_APP_ID]=ignore&env[TALK_FACEBOOK_APP_SECRET]=ignore) Online comments are broken. Our open-source Talk tool rethinks how moderation, comment display, and conversation function, creating the opportunity for safer, smarter discussions around your work. [Read more about Talk here](https://coralproject.net/products/talk.html). -## Documentation +Built with <3 by The Coral Project & Mozilla. -Developer Documentation & Setup Guides https://coralproject.github.io/talk/. +## Getting Started + +Check out our Docs: https://coralproject.github.io/talk/ ## Relevant Links From f1bad9e8f20a6ebdba8ec25172a976ccb54adaa5 Mon Sep 17 00:00:00 2001 From: Rishabh04-02 Date: Thu, 5 Oct 2017 23:07:51 +0530 Subject: [PATCH 05/14] modifications - displaying time modifications in displaying like X minutes Y seconds --- .../src/components/CountdownSeconds.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/client/coral-embed-stream/src/components/CountdownSeconds.js b/client/coral-embed-stream/src/components/CountdownSeconds.js index abcc1e398..46b6b1fbf 100644 --- a/client/coral-embed-stream/src/components/CountdownSeconds.js +++ b/client/coral-embed-stream/src/components/CountdownSeconds.js @@ -45,16 +45,13 @@ export class CountdownSeconds extends React.Component { if (typeof classNameForMsRemaining === 'function') { classFromProp = classNameForMsRemaining(msRemaining); } - if (wholeMinRemaining > 0) { - return ( - - {`${wholeMinRemaining} ${minUnit} ${wholeSecRemaining} ${secUnit}`} - - ); - } + const text = wholeMinRemaining > 0 + ? `${wholeMinRemaining} ${minUnit} ${wholeSecRemaining} ${secUnit}` + : `${wholeSecRemaining} ${secUnit}`; + return ( - {`${wholeSecRemaining} ${secUnit}`} + {text} ); } From f164ad80138e9ab336cf44cb2341f495df0b7d91 Mon Sep 17 00:00:00 2001 From: amorimgustavo Date: Thu, 5 Oct 2017 17:10:42 -0300 Subject: [PATCH 06/14] Update pt_BR.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hello Folks, Just edited some (several actually :) messages in your pt_BR.yml for your consideration. You may consider using: - "Banido" instead of "proibido" - "Perfil" or "Usuario" instead of "Bio" - "Opinião" instead of "Feedback" - "Denunciar" instead of "Reportar" - "Usuários(as)" instead of "Comentaristas" - "Listas" instead of "Hilos" or "Filas" - "Conversas" instead of "Histórias" Hope you like those suggestions. Cheers. Gustavo --- locales/pt_BR.yml | 197 ++++++++++++++++++++++++---------------------- 1 file changed, 103 insertions(+), 94 deletions(-) diff --git a/locales/pt_BR.yml b/locales/pt_BR.yml index 3f2c92628..baa7742a5 100644 --- a/locales/pt_BR.yml +++ b/locales/pt_BR.yml @@ -1,122 +1,131 @@ pt_BR: your_account_has_been_suspended: Sua conta foi temporariamente suspensa. your_account_has_been_banned: Sua conta foi banida. - your_username_has_been_rejected: Sua conta foi suspensa porque seu nome de usuário foi considerado inapropriado. Para restaurar sua conta, insira um novo nome de usuário. + your_username_has_been_rejected: Sua conta foi rejeitada porque seu nome de usuário foi considerado inapropriado. Para restaurar sua conta, insira um novo nome de usuário. embed_comments_tab: Comentários bandialog: - are_you_sure: "Tem certeza de que gostaria de proibir a {0}?" - ban_user: "Proibir o usuário?" - banned_user: "Usuário proibido" + are_you_sure: "Tem certeza de que gostaria de banir {0}?" + ban_user: "Deseja banir o usuário?" + banned_user: "Usuário banido" cancel: "Cancelar" note: "Nota: {0}" - note_reject_comment: "Proibir esse usuário também colocará esse comentário na fila rejeitada." - note_ban_user: "Proibir este usuário não os permitirá editar comentários ou remover qualquer coisa." - yes_ban_user: "Sim, proiber o usuário" - bio_offensive: "Essa bio é ofensiva" + note_reject_comment: "Banir esse usuário também colocará esse comentário na fila rejeitada." + note_ban_user: "Banir este usuário não os permitirá editar comentários ou remover qualquer coisa." + yes_ban_user: "Sim, banir o usuário" + bio_offensive: "Esse perfil é ofensiva" cancel: "Cancelar" characters_remaining: "caracteres restantes" comment: anon: "Anônimo" - ban_user: "Proibir o usuário" - comment: "Publicar um comentário" + ban_user: "Banir o usuário" + comment: "Escreva um comentário..." edited: Editado - flagged: "marcado" + flagged: "Marcado" view_context: "Veja o contexto" comment_box: - post: "Publicar" + post: "Enviar" cancel: "Cancelar" reply: "Responder" - comment: "Publicar um comentário" + comment: "Escreva um comentário..." name: "Nome" - comment_post_notif: "Seu comentário foi postado." - comment_post_notif_premod: "Obrigado por publicar seu comentário. Nossa equipe de moderação analisará seu comentário em breve." - comment_post_banned_word: "O seu comentário contém uma ou mais palavras que não são permitidas, portanto, não serão publicadas. Se você acha que esta mensagem está incorreta, contate nossa equipe de moderação." + comment_post_notif: "Seu comentário foi enviado." + comment_post_notif_premod: "Obrigado por compartilhar sua opinião. Nossa equipe de moderação analisará seu comentário em breve." + comment_post_banned_word: "Seu comentário não foi publicado porque contém conteúdo inadequado." characters_remaining: "caracteres restantes" comment_offensive: "Este comentário é ofensivo" comment_singular: Comentário comment_plural: Comentários - comment_post_banned_word: "O seu comentário contém uma ou mais palavras que não são permitidas, portanto, não serão publicadas. Se você acha que esta mensagem está incorret a, contate nossa equipe de moderação." + comment_post_banned_word: "Seu comentário não foi publicado porque contém conteúdo inadequado." comment_post_notif: "Seu comentário foi postado." - comment_post_notif_premod: "Obrigado por publicar seu comentário. Nossa equipe de moderação analisará seu comentário em breve." + comment_post_notif_premod: "Obrigado por compartilhar sua opinião. Nossa equipe de moderação analisará seu comentário em breve." common: - copy: 'Copie' + copy: 'Copiar' error: 'Ocorreu um erro.' + reply: 'Resposta' + replies: 'Respostas' + reaction: 'Reação' + reactions: 'Reações' + story: 'Conversas' community: account_creation_date: "Data de criação da conta" active: Ativo admin: Administrador - ads_marketing: "Isso parece anúncio/marketing" - are_you_sure: "Você tem certeza de que gostaria de proibir a {0}?" - ban_user: "Proibir o usuário?" - banned: Proibida - banned_user: "Usuário proibido" + ads_marketing: "Este comentário parece anúncio" + are_you_sure: "Você tem certeza de que gostaria de banir {0}?" + ban_user: "Banir o usuário?" + banned: Banido + banned_user: "Usuário banido" cancel: Cancelar - flaggedaccounts: "Nomes de usuários marcados" + dont_like_username: "Descurtir usuário" + flaggedaccounts: "Usuários marcados" flags: Marcadas impersonating: "Representação" loading: "Carregando resultados" moderator: Moderador - newsroom_role: "Papel de empresa" - no_flagged_accounts: "A fila de nomes de usuários marcados está atualmente vazia." + newsroom_role: "Papel da empresa" + no_flagged_accounts: "A fila de usuários marcados está atualmente vazia." no_results: "Nenhum usuário encontrado com esse nome de usuário ou e-mail. Eles estão se escondendo!" + offensive: "Ofensivo" + other: Outros people: Pessoas role: "Selecione um papel..." select_status: "Selecione um status..." - staff: "Funcionários" - status: Status + spam_ads: "Spam/Ads" + staff: "Equipe" + status: Situação username_and_email: "Nome de usuário e email" - yes_ban_user: "Sim, proibir esse usuário" + yes_ban_user: "Sim, banir este usuário" configure: apply: Aplicar - banned_word_text: "Os comentários que contenham essas palavras ou frases (não sensíveis a maiúsculas e minúsculas) serão automaticamente removidos do hilo de comentários. Digite uma palavra e pressione Enter ou Tab para adicionar. Opcionalmente cole uma lista separada por vírgulas." + banned_word_text: "Os comentários que contenham essas palavras ou frases (não sensíveis a maiúsculas e minúsculas) serão automaticamente removidos da lista de comentários. Digite uma palavra e pressione Enter ou Tab para adicionar ou cole uma lista separada por vírgulas." banned_words_title: "Lista de palavras proibidas" close: "Fechar" - close_after: "Feche os comentários após" - close_stream: "Feche hilo de comentários" - close_stream_configuration: "Esse hilo de comentários está fechado no momento. Ao abrir esse hilo de comentários, novos comentários podem ser enviados e exibidos." - closed_comments_desc: "Escreva uma mensagem a ser exibida quando o seu hilo de comentários for fechado e deixando de aceitar comentários." - closed_comments_label: "Escreve uma mensagem..." + close_after: "Fechar comentários após" + close_stream: "Fechar comentários" + close_stream_configuration: "Comentários fechados no momento. Ao abrir comentários, novos comentários podem ser enviados e exibidos." + closed_comments_desc: "Escreva uma mensagem a ser exibida quando os comentários estiverem fechados." + closed_comments_label: "Escreva uma mensagem..." closed_stream_settings: "Mensagem de comentários fechados" comment_count_error: "Por favor insira um número válido." - comment_count_header: "Limite o comprimento do comentário" + comment_count_header: "Limite o tamanho do comentário" comment_count_text_post: caracteres comment_count_text_pre: "Os comentários serão limitados a" comment_settings: Configurações - comment_stream: "Hilo de comentários" - comment_stream_will_close: "O hilo de comentários será fechado" - community: Comunidade + comment_stream: "Lista de comentários" + comment_stream_will_close: "Comentários fechados" + community: Usuários configure: Configurar copy_and_paste: "Copie e cole o código abaixo em seu CMS para incorporar sua caixa de comentários em seus artigos." - custom_css_url: "URL personalizado do CSS" - custom_css_url_desc: "URL de uma folha de estilo CSS que irá substituir os estilos padrão dos comentários embutidos. Pode ser interno ou externo." + custom_css_url: "URL para CSS customizado" + custom_css_url_desc: "URL de uma folha de estilo CSS para substituir os estilos padrão dos comentários embutidos. Pode ser interno ou externo." dashboard: Painel de controle days: Dias - description: "Como administrador, você pode personalizar as configurações do hilo de comentários para esta história:" - domain_list_text: "Insira os domínios que você gostaria de permitir para o Talk, como o seu site de preparação ou site de produção (ej. localhost:3000 staging.domain.com domain.com)." + description: "Como administrador, você pode personalizar as configurações da lista de comentários para esta história:" + domain_list_text: "Insira os domínios que você gostaria de permitir para o Talk, como seus ambientes de desenvolvimento, teste ou produção (ej. localhost:3000 staging.domain.com domain.com)." domain_list_title: "Domínios permitidos" edit_comment_timeframe_heading: "Período de tempo para editar comentários" - edit_comment_timeframe_text_pre: "Os comentadores terão" + edit_comment_timeframe_text_pre: "Os usuários terão" edit_comment_timeframe_text_post: "segundos para editar seus comentários." embed_comment_stream: "Comentários embutidos" - enable_premod_links_text: "Os moderadores devem aprovar qualquer comentário contendo um link antes de ser publicado." + enable_premod_links_text: "Demandar aprovação de moderadores antes de publicar comentários com links." enable_pre_moderation: "Habilitar pré-moderação" - enable_pre_moderation_text: "Os moderadores devem aprovar qualquer comentário antes de ser publicado." + enable_pre_moderation_text: "Demandar aprovação de moderadores para todos os comentários." enable_premod_links: "Os moderadores devem aprovar qualquer comentário antes de ser publicado." enable_premod: "Habilitar pré-moderação" - enable_premod_description: "Moderators must approve any comment before it is published." - enable_premod_links_description: "Os moderadores devem aprovar qualquer comentário antes de ser publicado." + enable_premod_description: "Demandar aprovação de moderadores para todos os comentários." + enable_premod_links_description: "Demandar aprovação de moderadores antes de publicar comentários com links." enable_questionbox: "Faça uma pergunta aos leitores" - enable_questionbox_description: "Esta pergunta aparecerá no topo deste hilo de comentários. Pergunte aos leitores sobre um determinado problema no artigo ou coloca questões de discussão, etc." + enable_questionbox_description: "Esta pergunta aparecerá no topo da lista de comentários. Pergunte aos leitores sobre um determinado problema no artigo, abra o assunto para discussão, etc." hours: Horas - include_comment_stream: "Incluir descrição de hilo de comentários para leitores" + include_comment_stream: "Incluir descrição da lista comentários para leitores" include_comment_stream_desc: "Escreva uma mensagem para adicionar ao topo do seu hilo de comentários. Posicionar um tópico, incluem diretrizes comunitárias, etc." include_text: "Inclua seu texto aqui:" include_question_here: "Escreva sua pergunta aqui:" moderate: Moderar moderation_settings: "Configurações de moderação" - open: "Abra" - open_stream: "Abra hilo de comentários" - open_stream_configuration: "Esse hilo de comentários está aberto no momento. Ao fechar este hilo de comentários, nenhum novo comentário pode ser enviado e todos os comentários anteriores ainda serão exibidos." + open: "Abrir" + open_stream: "Abrir comentários" + open_stream_configuration: "Comentários abertos no momento. Ao fechar esta lista de comentários, nenhum novo comentário poderá ser enviado. Mas os comentários anteriores ainda serão exibidos." require_email_verification: "Requerer verificação de email" require_email_verification_text: "Novos usuários devem verificar seu e-mail antes de comentar" save_changes: "Salvar alterações" @@ -125,7 +134,7 @@ pt_BR: stories: Histórias stream_settings: "Configurações de comentários" suspect_word_title: "Lista de palavras suspeitas" - suspect_word_text: "Os comentários que contenham essas palavras ou frases (não sensíveis a maiúsculas e minúsculas) serão destacados no hilo de comentários. Digite uma palavra e pressione Enter ou Tab para adicionar. Opcionalmente cole uma lista separada por vírgulas." + suspect_word_text: "Os comentários que contenham essas palavras ou frases (não sensíveis a maiúsculas e minúsculas) serão automaticamente removidos da lista de comentários. Digite uma palavra e pressione Enter ou Tab para adicionar ou cole uma lista separada por vírgulas." tech_settings: "Configurações técnicas" title: "Configurar comentários" weeks: Semanas @@ -135,7 +144,7 @@ pt_BR: check_the_form: "Formulário inválido. Verifique as entradas do formulário." continue: "Continue com o mesmo nome de usuário do Facebook" error_create: "Erro ao alterar o nome de usuário" - fake_comment_body: "Este é um exemplo de comentário. Os leitores podem compartilhar seus pensamentos e opiniões com as salas de redação na seção de comentários." + fake_comment_body: "Este é um exemplo de comentário. Os leitores podem compartilhar seus pensamentos e opiniões com a redação na seção de comentários." fake_comment_date: "1 minuto atrás" if_you_dont_change_your_name: "Se você não alterar seu nome de usuário nesta etapa, seu nome de exibição do Facebook aparecerá ao lado de todos os seus comentários." required_field: "Requerido" @@ -145,30 +154,30 @@ pt_BR: write_your_username: "Edite seu nome de usuário" your_username: "Nosso nome de usuário aparece em todos os comentários que você publica." dashboard: - auto_update: "Data automatically updates every five minutes or when you Reload." + auto_update: "Atualizado autimaticamente a cada minutos ou após recarregamento." comment_count: comentários - flags: Marcadas - most_flags: "Histórias com a maioria das marcadas" - most_conversations: "Histórias com a maioria das conversas" + flags: Marcações + most_flags: "Conversas com mais comentários marcados" + most_conversations: "Conversas com mais comentários" next_update: "{0} minutos até a próxima atualização." no_activity: "Não houve nenhum comentário nos últimos cinco minutos." - no_flags: "Não houve nenhuma marcada em nenhum lugar nos últimos 5 minutos. Hip Hip Uha!" - no_likes: "Não houve gostos nos últimos 5 minutos." + no_flags: "Não houve nenhum comentário marcado em nenhum lugar nos últimos 5 minutos. Hip Hip Uha!" + no_likes: "Não houve reações nos últimos 5 minutos." done: Feito edit_comment: body_input_label: "Edite este comentário" save_button: "Salvar alterações" edit_window_expired: "Você não pode mais editar esse comentário. A janela de tempo para fazê-lo expirou. Por que não publicar outro?" edit_window_expired_close: "Fechar" - edit_window_timer_prefix: "Para editar:" + edit_window_timer_prefix: "Tempo para editar:" second: "segundo" seconds_plural: "segundos" email: confirm: has_been_requested: "Uma confirmação de e-mail foi solicitada para a seguinte conta:" - to_confirm: "Para confirmar a conta, visite o seguinte link: " + to_confirm: "Para confirmar a conta, visite este link: " confirm_email: "Confirme e-mail" - if_you_did_not: "Se você não solicitou isso, você pode ignorar este e-mail com segurança." + if_you_did_not: "Se você não solicitou isso, você pode ignorar este e-mail." subject: "Confirmação de e-mail" password_reset: we_received_a_request: "Recebemos um pedido para redefinir sua senha. Se você não solicitou esta alteração, pode ignorar este e-mail." @@ -177,7 +186,7 @@ pt_BR: embedlink: copy: "Copiar para área de transferência" error: - COMMENT_TOO_SHORT: "Seu comentário deve conter palavras." + COMMENT_TOO_SHORT: "Seu comentário precisar ter mais de um caracter. Revise seu comentário e envie novamente" NOT_AUTHORIZED: "Você não está autorizado a executar esta ação." NO_SPECIAL_CHARACTERS: "Nomes de usuários podem conter números de letras e _ somente" PASSWORD_LENGTH: "A senha é muito curta" @@ -189,7 +198,7 @@ pt_BR: SAME_USERNAME_PROVIDED: "Você deve enviar um nome de usuário diferente." EMAIL_IN_USE: "Endereço de e-mail já está em uso." EMAIL_REQUIRED: "É necessário o endereço de e-mail." - LOGIN_MAXIMUM_EXCEEDED: "Você fez muitas tentativas de senha mal sucedidas. Por favor, espere." + LOGIN_MAXIMUM_EXCEEDED: "Você fez muitas tentativas de senha mal sucedidas. Espere alguns instantes." PASSWORD_REQUIRED: "Você deve inserir uma senha." COMMENTING_CLOSED: "Os comentários já estão fechados." NOT_FOUND: "Recurso não encontrado" @@ -208,8 +217,8 @@ pt_BR: flag_reason: "Motivo do relatório (opcional)" flag_username: "Informe o nome de usuário" framework: - banned_account_header: "Sua conta está atualmente proibida." - banned_account_body: "Isso significa que você não pode gostar, informar ou escrever comentários." + banned_account_header: "Sua conta está atualmente banida." + banned_account_body: "Isso significa que você não pode curtir, informar ou escrever comentários." comment: comentário comment_is_ignored: "Este comentário está oculto porque você ignorou esse usuário." comments: comentários @@ -241,7 +250,7 @@ pt_BR: account: "contas marcadas" actions: Ações all: todos - all_streams: "Todos os hilos" + all_streams: "Todas as listas" notify_edited: '{0} editou o comentário "{1}"' notify_accepted: '{0} aprovou o comentário "{1}"' notify_rejected: '{0} rejeitou o comentário "{1}"' @@ -252,17 +261,17 @@ pt_BR: billion: B close: Fechar dont_like_username: "Eu não gosto desse nome de usuário" - empty_queue: "Não há mais comentários para moderar! Você está todo apanhado. Vá tomar um ☕️" + empty_queue: "Não há mais comentários para moderar! Aproveite para relaxar e esticar as pernas" flagged: marcado impersonating: Representação less_detail: "Menos detalhes" - likes: gostos + likes: Curtidas million: M - mod_faster: "Moderado mais rápido com atalhos de teclado" + mod_faster: "Modere mais rápido com atalhos de teclado" moderate: "Moderar →" more_detail: "Mais detalhes" new: "Novo" - newest_first: "Mais novo primeiro" + newest_first: "Mais novos primeiro" navigation: Navegação next_comment: "Vá para o próximo comentário" offensive: Ofensiva @@ -272,7 +281,7 @@ pt_BR: prev_comment: "Vá para o comentário anterior" reject: "Rejeitar" rejected: "Rejeitado" - select_stream: "Selecione hilo" + select_stream: "Selecione a lista" shift_key: "⇧" shortcuts: "Atalhos" show_shortcuts: "Ver atalhos" @@ -280,13 +289,13 @@ pt_BR: spam_ads: Spam/Anuncios thismenu: "Abra este menu" thousand: k - try_these: "Tente esse" + try_these: "Tente este" view_more_shortcuts: "Ver mais atalhos" my_comment_history: "Histórico de comentários" name: Nome no_agree_comment: "Eu não concordo com este comentário" - no_like_bio: "Eu não gosto dessa biografia" - no_like_username: "Eu não gosto desse nome de usuário" + no_like_bio: "Eu não gosto dessa descrição de perfil" + no_like_username: "Eu não gosto deste nome de usuário" other: Outro permalink: Link personal_info: "Este comentário revela informações de identificação pessoal" @@ -294,10 +303,10 @@ pt_BR: profile: Perfil profile_settings: "Configurações de perfil" reply: Responder - report: Reportar - report_notif: "Obrigado por reportar este comentário. Nossa equipe de moderação foi notificada e irá revisá-la em breve." - report_notif_remove: "Seu marcado foi removido." - reported: Reportado + report: Denunciar + report_notif: "Obrigado por denunciar este comentário. Nossa equipe de moderação foi notificada e irá revisá-la em breve." + report_notif_remove: "O comentário que você denunciou foi removido." + reported: Denunciado settings: 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" @@ -316,30 +325,30 @@ pt_BR: all: Todos article: História closed: Fechado - empty_result: "Nenhum recurso corresponde a esta pesquisa. Talvez tente alargar a sua pesquisa?" - filter_streams: "Filtra os hilos" + empty_result: "Nenhum resultado encontrado para esta pesquisa. Que tal refazer a pesquisa com termos diferentes?" + filter_streams: "Filtra as listas" newest: O mais novo oldest: O mais velho open: Aberto pubdate: "Data de publicação" search: Pesquisar sort_by: "Ordenar por" - status: "Status do hilo" - stream_status: "Status do hilo" + status: "Situação dos comentários" + stream_status: "Situação dos comentários" suspenduser: title_suspend: "Suspender usuário" - description_suspend: "Você está suspendendo {0}. Este comentário irá para a fila Rejeitada, e {0} não poderá gostar, reportar, responder ou publicar até que o tempo de suspensão seja concluído." + description_suspend: "Você está suspendendo {0}. Este comentário irá para a fila Rejeitada, e {0} não poderá gostar, denunciar, responder ou publicar até que o tempo de suspensão seja concluído." select_duration: "Selecione a duração da suspensão" one_hour: "1 hora" hours: "{0} horas" days: "{0} dias" cancel: "Cancelar" suspend_user: "Suspender usuário" - email_message_suspend: "Querido/a {0},\n\nDe acordo com as diretrizes da comunidade de {1}, sua conta foi temporariamente suspensa. Durante a suspensão, você não poderá comentar, reportar ou se envolver com outros comentaristas. Por favor, volte para a conversa {2}." + email_message_suspend: "Querido/a {0},\n\nDe acordo com as diretrizes da comunidade de {1}, sua conta foi temporariamente suspensa. Durante a suspensão, você não poderá comentar, denunciar ou se responder outros usuários(as). Por favor, volte para a conversa {2}." title_notify: "Notificar o usuário da suspensão temporária" notify_suspend_until: "O usuário {0} foi temporariamente suspenso. Esta suspensão terminará automaticamente {1}." description_notify: "Suspender este usuário temporariamente desativará sua conta e esconderá todos os seus comentários no site" - write_message: "Escrive uma mensagem" + write_message: "Escrever uma mensagem" send: Enviar reject_username: username: nome de usuário @@ -350,14 +359,14 @@ pt_BR: title_reject: "Percebemos que você rejeitou um nome de usuário" suspend_user: "Suspender usuário" yes_suspend: "Sim suspender" - email_message_reject: "Outro membro da comunidade recentemente sinalizou seu nome de usuário para revisão. Por causa do seu conteúdo, seu nome de usuário foi rejeitado. Isso significa que você não pode mais comentar, gostar, ou reportar conteúdo até que você reescreva seu nome de usuário. Por favor envie-nos um e-mail se tiver dúvidas ou preocupações." + email_message_reject: "Outro membro da comunidade recentemente sinalizou seu nome de usuário para revisão. Por causa do seu conteúdo, seu nome de usuário foi rejeitado. Isso significa que você não pode mais comentar, gostar, ou denunciar conteúdo até que você reescreva seu nome de usuário. Por favor envie-nos um e-mail se tiver dúvidas ou preocupações." write_message: "Escreve uma mensagem" send: Enviar - thank_you: "Nós valorizamos sua segurança e feedback. Um moderador irá rever o seu relatório." + thank_you: "Nós valorizamos sua segurança e opinião. Um moderador irá rever o seu relatório." user: - bio_flags: "Marcadas para esta biografia" - user_bio: "Biografia do usuário" - username_flags: "Marcadas para este nome de usuário" + bio_flags: "Marcadas para este perfil" + user_bio: "Perfil do usuário" + username_flags: "Marcadas para este usuário" user_impersonating: "Este usuário está representando" user_no_comment: "Você nunca deixou um comentário. Participe da conversa!" username_offensive: "Esse nome de usuário é ofensivo" From bf0901e77fa7c8fe3d313b3c52d191f2dda35f92 Mon Sep 17 00:00:00 2001 From: Vicky Chijwani Date: Fri, 6 Oct 2017 02:15:28 +0530 Subject: [PATCH 07/14] Wrap Slack call in process.nextTick() to avoid blocking comment creation. --- .../server/hooks.js | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/plugins/talk-plugin-slack-notifications/server/hooks.js b/plugins/talk-plugin-slack-notifications/server/hooks.js index a2f3ca743..bda9dc4f5 100644 --- a/plugins/talk-plugin-slack-notifications/server/hooks.js +++ b/plugins/talk-plugin-slack-notifications/server/hooks.js @@ -19,24 +19,26 @@ module.exports = { } } = result; const username = context.user.username; - const response = await fetch(SLACK_WEBHOOK_URL, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - timeout: SLACK_WEBHOOK_TIMEOUT, - body: JSON.stringify({ - attachments: [{ - text: text, - footer: `Comment by ${username}`, - ts: Math.floor(Date.parse(createdAt) / 1000), - }] - }), + process.nextTick(async () => { + const response = await fetch(SLACK_WEBHOOK_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + timeout: SLACK_WEBHOOK_TIMEOUT, + body: JSON.stringify({ + attachments: [{ + text: text, + footer: `Comment by ${username}`, + ts: Math.floor(Date.parse(createdAt) / 1000), + }] + }), + }); + if (!response.ok) { + const responseText = await response.text(); + console.trace(`Posting to Slack failed with HTTP code ${response.status} and body '${responseText}'`); + } }); - if (!response.ok) { - const responseText = await response.text(); - console.trace(`Posting to Slack failed with HTTP code ${response.status} and body '${responseText}'`); - } return result; }, }, From c0bb5e5cb20741d4ea211e455bf9baed8475bc9e Mon Sep 17 00:00:00 2001 From: Gerardo Galvez Date: Thu, 5 Oct 2017 16:57:46 -0500 Subject: [PATCH 08/14] Add moderator shortcuts to open search and switch between queues. --- .../src/components/ModerationKeysModal.js | 2 + .../Moderation/components/Moderation.js | 43 +++++++++++++++---- locales/en.yml | 2 + 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/client/coral-admin/src/components/ModerationKeysModal.js b/client/coral-admin/src/components/ModerationKeysModal.js index abdffa2b1..45304992c 100644 --- a/client/coral-admin/src/components/ModerationKeysModal.js +++ b/client/coral-admin/src/components/ModerationKeysModal.js @@ -10,6 +10,8 @@ const shortcuts = [ shortcuts: { 'j': 'modqueue.next_comment', 'k': 'modqueue.prev_comment', + 'ctrl+f': 'modqueue.toggle_search', + 't': 'modqueue.next_queue', 's': 'modqueue.singleview', '?': 'modqueue.thismenu' } diff --git a/client/coral-admin/src/routes/Moderation/components/Moderation.js b/client/coral-admin/src/routes/Moderation/components/Moderation.js index 668d013b0..cbee0330c 100644 --- a/client/coral-admin/src/routes/Moderation/components/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/components/Moderation.js @@ -26,6 +26,8 @@ class Moderation extends Component { key('s', () => singleView()); key('shift+/', () => toggleModal(true)); key('esc', () => toggleModal(false)); + key('ctrl+f', () => this.openSearch()); + key('t', () => this.nextQueue()); key('j', () => this.select(true)); key('k', () => this.select(false)); key('f', () => this.moderate(false)); @@ -36,6 +38,26 @@ class Moderation extends Component { this.props.toggleModal(false); } + nextQueue = () => { + const queueConfig = this.props.queueConfig; + const activeTab = this.props.activeTab; + const assetId = this.props.data.variables.asset_id; + const menuItems = Object.keys(queueConfig).map((queue) => ({ + key: queue + })); + + let activeTabIndex, nextQueueIndex; + for (let i = 0; i < menuItems.length; i++) { + if (menuItems[i].key === activeTab){ + activeTabIndex = i; + break; + } + } + + nextQueueIndex = (activeTabIndex === menuItems.length - 1) ? 0 : activeTabIndex + 1; + this.props.router.push(this.props.getModPath(menuItems[nextQueueIndex].key, assetId)); + } + closeSearch = () => { const {toggleStorySearch} = this.props; toggleStorySearch(false); @@ -53,14 +75,17 @@ class Moderation extends Component { const {acceptComment, rejectComment} = this.props; const {selectedCommentId} = this.state; - const comments = this.getComments(); - const commentIdx = comments.findIndex((comment) => comment.id === selectedCommentId); - const comment = comments[commentIdx]; - - if (accept) { - comment.status !== 'ACCEPTED' && acceptComment({commentId: comment.id}); - } else { - comment.status !== 'REJECTED' && rejectComment({commentId: comment.id}); + // Accept or reject only if there's a selected comment + if(selectedCommentId != null){ + const comments = this.getComments(); + const commentIdx = comments.findIndex((comment) => comment.id === selectedCommentId); + const comment = comments[commentIdx]; + + if (accept) { + comment.status !== 'ACCEPTED' && acceptComment({commentId: comment.id}); + } else { + comment.status !== 'REJECTED' && rejectComment({commentId: comment.id}); + } } } @@ -147,6 +172,8 @@ class Moderation extends Component { key.unbind('s'); key.unbind('shift+/'); key.unbind('esc'); + key.unbind('ctrl+f'); + key.unbind('t'); key.unbind('j'); key.unbind('k'); key.unbind('f'); diff --git a/locales/en.yml b/locales/en.yml index a979739b5..1a934b695 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -275,6 +275,8 @@ en: newest_first: "Newest First" navigation: Navigation next_comment: "Go to the next comment" + toggle_search: "Open search" + next_queue: "Switch queues" oldest_first: "Oldest First" premod: pre-mod prev_comment: "Go to the previous comment" From 6c95502b4eca32af1c30b123b76fa533e49008f1 Mon Sep 17 00:00:00 2001 From: Lance Ruegger Date: Thu, 5 Oct 2017 17:36:38 -0700 Subject: [PATCH 09/14] Change DOM input to coral-ui TextField to inherit styling, add prop-types validation, convert from using ref to using state. --- .../client/components/ForgotContent.js | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/plugins/talk-plugin-auth/client/components/ForgotContent.js b/plugins/talk-plugin-auth/client/components/ForgotContent.js index 916c3f5b3..49692abbb 100644 --- a/plugins/talk-plugin-auth/client/components/ForgotContent.js +++ b/plugins/talk-plugin-auth/client/components/ForgotContent.js @@ -1,14 +1,27 @@ import React from 'react'; +import PropTypes from 'prop-types'; import styles from './styles.css'; -import {Button} from 'plugin-api/beta/client/components/ui'; +import {Button, TextField} from 'plugin-api/beta/client/components/ui'; import t from 'coral-framework/services/i18n'; class ForgotContent extends React.Component { + constructor() { + super(); + this.state = { + value: '', + }; + } + handleSubmit = (e) => { e.preventDefault(); - this.props.fetchForgotPassword(this.emailInput.value); + this.props.fetchForgotPassword(this.state.value); }; + handleChangEmail = (e) => { + const {value} = e.target; + this.setState({value}); + } + render() { const {changeView, auth} = this.props; const {passwordRequestSuccess, passwordRequestFailure} = auth; @@ -20,13 +33,13 @@ class ForgotContent extends React.Component {
- - (this.emailInput = input)} - type="text" +