Compare commits

..
100 Commits
Author SHA1 Message Date
Wyatt Johnson bb54009567 Websocket Regression (#1971)
* fix: addresses websocket connection issues

- reverted upgrade to subscriptions-transport-ws
- removed unnecessary websocket resets
- improved websocket reconnection logic

* Update package.json
2018-10-09 17:47:07 +00:00
vittoboa c126d217ad Fix spanish translation (#1977) 2018-10-09 15:35:22 +00:00
Wyatt Johnson d0eca26d5b Body Count (#1969)
* fix: adjusted beheviour of body count

* fix: updated translation @okbel
2018-10-05 18:03:02 +00:00
Nat Welch b742923897 Toxic Comments Plugin logging (#1970)
* Add debug as a dep to toxic-comments

* Add debug logging to toxic-comments plugin

* fix: cleaned up a bit
2018-10-05 17:40:32 +00:00
Wyatt Johnson 1781b926d9 fix: tab container css (#1959) 2018-10-04 20:27:38 +00:00
Wyatt Johnson 7b97a8fca2 Passport Fix (#1955)
* fix: Fixed bug in passport access

* fix: resolved issues with postMessage and static urls
2018-10-02 16:21:45 +00:00
Kim Gardner 6bff2de371 Merge pull request #1954 from coralproject/static-uri
Static URI + Social Callback bug
2018-10-01 19:08:48 -04:00
Wyatt Johnson 05283b08f7 chore: version bump 2018-10-01 16:58:58 -06:00
Wyatt Johnson 838687a3ae fix: added support for static uri in CSP header for social callback 2018-10-01 16:58:14 -06:00
immber 7b0213299a Updated quickstart (#1898)
* udpated quickstart

* fixed typo and removed quote from quickstart

* fixed broken link in readme

* fixed broken anchor tags

* fix: fixed anchor links
2018-09-20 21:24:44 -06:00
Kim Gardner f4dbc7a24d Merge pull request #1893 from coralproject/fixes
Shorten Spanish phrases for UI purposes
2018-09-19 16:37:56 -04:00
Kim Gardner 161e8b4117 Shorten Spanish phrases for UI purposes 2018-09-19 16:30:17 -04:00
Kim Gardner 6f3535984c Add Spanish translations for toxic-comments (#1891) 2018-09-19 10:22:34 -06:00
Kim Gardner e3557c1f7a Fix en and es translations (#1890) 2018-09-19 10:07:33 -06:00
Kim Gardner 692b6af09d Fix bug with translations for Google auth (#1886)
* Fix bug with translations for Google auth

* Typo
2018-09-18 17:46:55 -06:00
Kim Gardner 25932e10d6 Update translations for ignore-user plugin (#1887) 2018-09-18 17:33:40 -06:00
Kim Gardner ec13a001a2 Notifications plugins es translations (#1888)
* Es translation

* More translations for es

* Category and digest translations

* Typo

* Typo

* Fixes
2018-09-18 17:22:39 -06:00
Kim Gardner 702a616e6c More profile-data translations (#1889) 2018-09-18 17:17:38 -06:00
Kiwi a3dd208fbd Add static className to featured tooltip (#1885) 2018-09-18 15:45:29 -06:00
Kim Gardner 55c3406990 Add more missing es translations (#1883)
* Add more missing es translations

* Typo
2018-09-17 17:09:04 -06:00
Kim Gardner 197fbc33e0 Bump version 4.6.3 (#1871)
* Bump version 4.6.3

* Update index.js
2018-09-17 15:12:30 -06:00
Kim Gardner 008a4fd7d5 Es typo (#1880) 2018-09-17 14:53:36 -06:00
Kim Gardner 8c5665095f Adds Spanish translations for profile data (#1872) 2018-09-17 19:33:27 +00:00
Kim Gardner 647b2ea0c8 Add missing Spanish translations for local-auth (#1879)
* Add missing Spanish translations for local-auth

* fix: yaml fix
2018-09-17 18:27:30 +00:00
Kim Gardner e61f88b98d Shorten es.yml translation to "Configurar" (#1878) 2018-09-17 17:26:53 +00:00
Kim Gardner b024421bbb Merge pull request #1875 from coralproject/http-error
404 Errors
2018-09-14 16:25:02 -04:00
Wyatt Johnson ce95467145 fix: don't log an error for http 404, let the logger do it 2018-09-14 11:48:48 -06:00
Kim Gardner 1564b380b1 Merge pull request #1870 from coralproject/tag-fix
Tag Fix
2018-09-13 23:38:40 -04:00
Kim Gardner 593b8959c5 Merge branch 'master' into tag-fix 2018-09-13 22:36:57 -04:00
Kim Gardner 3fda3b4b7c Merge pull request #1868 from coralproject/upgrade-ws
Upgraded websocket package
2018-09-13 22:36:36 -04:00
Wyatt Johnson 7e680038d6 fix: added tests 2018-09-13 11:21:40 -06:00
Wyatt Johnson 71b8bb5d60 fix: fixes #1867 2018-09-13 11:10:45 -06:00
Wyatt Johnson cfbafdeb5c feat: upgraded websocket package 2018-09-13 07:46:53 -06:00
Wyatt Johnson c19d4a433a Improved Comment URL Handling (#1860)
* feat: replaces PR 1819

- fixes #1851

* fix: set query using new syntax

* review: fixes for missed framework changes

* fix: linting
2018-09-11 22:44:23 +00:00
Kiwi 13cc80bd35 Merge pull request #1841 from hjanuschka/toggle_comment_date
moderation: toggle comment date from timeago to full date
2018-09-11 16:09:15 +02:00
Helmut Januschka 1eea2daeae adress feedback 2018-09-10 21:34:07 +02:00
Kim Gardner 5b29e7d351 Merge branch 'master' into toggle_comment_date 2018-09-10 10:53:34 -04:00
Kiwi 3af39d99e4 Merge pull request #1840 from hjanuschka/word_break
fix long urls breaking moderation box
2018-09-07 22:01:40 +02:00
Kiwi 1abf988366 Merge branch 'master' into word_break 2018-09-07 21:51:37 +02:00
Helmut Januschka 205f943ab5 Update Comment.css 2018-09-07 19:13:36 +02:00
Helmut Januschka 879683da4f Merge branch 'toggle_comment_date' of github.com:hjanuschka/talk into toggle_comment_date 2018-09-07 13:38:52 +02:00
Helmut Januschka 974e884432 adress feedback 2018-09-07 13:36:55 +02:00
Helmut Januschka 846319f1a9 Merge branch 'word_break' of github.com:hjanuschka/talk into word_break 2018-09-07 13:12:49 +02:00
Helmut Januschka e5237ae805 adopt feedback 2018-09-07 13:12:01 +02:00
Wyatt Johnson b9d88251e6 External User Fix (#1846)
* fix: return user if user was not initially found

* fix: added tests, added wasUpserted boolean
2018-09-06 15:22:58 +00:00
Wyatt Johnson 0d720626d4 Merge branch 'master' into toggle_comment_date 2018-09-04 17:59:55 +00:00
Wyatt Johnson 50b2c6aa88 Merge branch 'master' into word_break 2018-09-04 15:04:50 +00:00
Thomaz de Oliveira dos Reis 382019d907 Change proxy-agent to support http/https through http/https/socks/pac proxy on scraper (#1839) 2018-09-04 15:01:10 +00:00
Helmut Januschka 97103739c2 toggle comment date 2018-09-03 09:04:33 +02:00
Helmut Januschka 9cf13691ad fix long urls breaking moderation box 2018-09-03 08:20:56 +02:00
Kim Gardner e6107173fb Merge pull request #1831 from coralproject/small-fix
Add missing translation
2018-08-29 12:02:34 -04:00
Kim Gardner 1d9ec7a779 Formatting 2018-08-29 11:46:41 -04:00
Kim Gardner 0c345fe913 Add missing translation 2018-08-29 11:44:56 -04:00
Kim Gardner b6f06f1e89 Merge pull request #1805 from danilogila/translations_ptbr
Adding more translations in some missing pages
2018-08-29 11:39:47 -04:00
Kim Gardner aea3a7449e Merge branch 'master' into translations_ptbr 2018-08-29 10:23:08 -04:00
Kim Gardner 4db73ae73a Merge pull request #1804 from hjanuschka/patch-2
support translation in permalink share button
2018-08-29 10:21:34 -04:00
Kim Gardner 2af7acd3db Merge branch 'master' into patch-2 2018-08-28 16:30:25 -04:00
Kim Gardner da9847b88f Merge pull request #1822 from hjanuschka/patch-3
add german translation for sort-upvoted
2018-08-28 13:53:46 -04:00
Kim Gardner 5b9f9f1b0d Merge branch 'master' into translations_ptbr 2018-08-28 13:53:19 -04:00
Helmut Januschka 5427f295f6 add german translation for sort-upvoted 2018-08-28 15:48:50 +02:00
Wyatt Johnson 620273769f Update package.json (#1818) 2018-08-24 12:22:23 -06:00
Wyatt Johnson dff3d79569 Subscription Optimizations (#1817)
* fix: optimized user extraction during subscription operations

* fix: fixed query

* feat: added user refeshing batching
2018-08-24 16:14:18 +00:00
Suriyaa ✌️️ 0904453e41 Remove Node Security Platform Badge (#1813) 2018-08-22 16:04:58 +00:00
Helmut Januschka 90eafb557c fix language detection in IE11 and Edge (#1810) 2018-08-21 17:52:02 +00:00
Danilo.Santana bcb294c625 adding more translations in some missing pages 2018-08-16 11:27:31 -03:00
Helmut Januschka b070a7cc52 cs fixes 2018-08-16 14:25:48 +02:00
Helmut Januschka e48d89a607 Update en.yml 2018-08-16 14:03:57 +02:00
Helmut Januschka cb57e5c1e1 add DE translation 2018-08-16 14:03:23 +02:00
Helmut Januschka bf363621d3 support translation in button 2018-08-16 14:02:07 +02:00
Kim Gardner 4dbd1e0bb3 Add version tip to docs (#1790) 2018-08-08 17:03:23 +00:00
Kim Gardner cbda970437 Merge pull request #1789 from coralproject/release-4-6-1
Bump version 4.6.1
2018-08-08 17:42:19 +01:00
Wyatt Johnson eecd814e53 Merge branch 'master' into release-4-6-1 2018-08-08 16:31:42 +00:00
Kim Gardner fb4b6330da 4.6.1 Release 2018-08-08 17:30:01 +01:00
Kim Gardner eb24b5c027 Merge pull request #1785 from coralproject/comment-edit-moderation
Comment Editing Passthrough
2018-08-08 16:44:17 +01:00
Kim Gardner 3346f5de77 Merge branch 'master' into comment-edit-moderation 2018-08-08 14:39:45 +01:00
Kim Gardner 1a69cea9f4 Merge pull request #1786 from coralproject/lowercasedSearch
Case sensitive displayName Issue
2018-08-08 14:38:43 +01:00
Wyatt Johnson d763f0b8f6 fix: added lowercased/case searches for displayName 2018-08-07 16:31:51 -06:00
Wyatt Johnson 6bb98a00dc fix: added support for comment edit mod passthrough 2018-08-07 13:56:23 -06:00
Leandro 2edb8e8d0a use config variable scheduledDeletionDelayHours to schedule deletion time on hoc (#1775) 2018-08-01 21:48:11 +00:00
Kim Gardner c3e162dd56 Merge pull request #1770 from icco/patch-1
Fix small typo in integrating/configuring-admin.md
2018-08-01 16:14:37 +01:00
Wyatt Johnson 3cdd061afd Merge branch 'master' into patch-1 2018-07-31 18:25:05 +00:00
Kim Gardner aaaf37b8db Small typo fix (#1772) 2018-07-31 18:24:52 +00:00
Nat Welch 7af5f04e45 Fix small typo in integrating/configuring-admin.md 2018-07-30 11:42:01 -04:00
Kim Gardner 65a166b860 Merge pull request #1767 from coralproject/token-fix
Token Handling
2018-07-27 21:25:07 +01:00
Kim Gardner eb4bcb71c4 Merge branch 'master' into token-fix 2018-07-27 21:12:52 +01:00
Wyatt Johnson 6a04b66d84 fix: token handling
- Fixes #1728
2018-07-27 14:08:53 -06:00
Kim Gardner 30ae9a82c1 Merge pull request #1764 from coralproject/user-fix
Username Status Fix
2018-07-27 20:50:57 +01:00
Wyatt Johnson ec06f2a392 chore: update 4.6.0 2018-07-27 13:37:46 -06:00
Wyatt Johnson f461f0884b Merge branch 'master' into user-fix 2018-07-27 19:29:05 +00:00
Wyatt Johnson d55c329f76 chore: version bump 2018-07-27 13:27:17 -06:00
Wyatt Johnson 0f63e076a6 fix: username status fix 2018-07-27 13:25:58 -06:00
Fabian Marz 1012b6b28b Fixed UI component path reference. (#1761) 2018-07-26 22:09:03 +00:00
Fabian Marz 421352c75c Fixed code markup for action name. (#1746) 2018-07-13 16:19:00 +00:00
Kim Gardner a170c107e2 Add FAQ and Troubleshooting docs (#1742)
* Add FAQ and Troubleshooting docs

* Add to nav

* Fix links

* Fix css formatting and fix rel links
2018-07-12 19:05:08 +00:00
Kim Gardner 104617d644 Merge pull request #1739 from memz/ar-translations-update-1
updated translation files with new Arabic translations
2018-07-09 17:33:37 -04:00
Kim Gardner 5b1773ba26 Merge branch 'master' into ar-translations-update-1 2018-07-09 16:33:31 -04:00
Nat Welch cd0aeca87c Add suggested index to comments (#1740) 2018-07-09 16:20:05 +00:00
Melhem Fawaz 5c65702032 updated translation files with new Arabic translations 2018-07-09 17:07:02 +04:00
Kim Gardner 66445aae59 Merge pull request #1737 from coralproject/username-status
findOrCreateExternalUser Deprecation
2018-07-06 17:18:32 -04:00
Wyatt Johnson 23156f3d5e feat: added deprecations, docs 2018-07-06 14:28:25 -06:00
105 changed files with 4190 additions and 468 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
# Talk · [![CircleCI](https://circleci.com/gh/coralproject/talk.svg?style=svg)](https://circleci.com/gh/coralproject/talk) · [![NSP Status](https://nodesecurity.io/orgs/coralproject/projects/7bd7d26c-47ed-4a5f-8c4a-b919bf1c2946/badge)](https://nodesecurity.io/orgs/coralproject/projects/7bd7d26c-47ed-4a5f-8c4a-b919bf1c2946) · [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md#pull-requests)
# Talk · [![CircleCI](https://circleci.com/gh/coralproject/talk.svg?style=svg)](https://circleci.com/gh/coralproject/talk) · [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md#pull-requests)
Online comments are broken. Our open-source commenting platform, Talk, 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/talk).
@@ -14,7 +14,7 @@ Learn more about Talk, including a deep dive into features for commenters and mo
## Pre-Launch Guide
Youve installed Talk on your server, and youre preparing to launch it on your site. The real community work starts now, before you go live. You have a unique opportunity pre-launch to set your community up for success. Read our [Talk Community Guide](https://blog.coralproject.net/youve-installed-talk-now-what/).
Youve installed Talk on your server, and youre preparing to launch it on your site. The real community work starts now, before you go live. You have a unique opportunity pre-launch to set your community up for success. Read our [Talk Community Guide](https://coralproject.net/blog/youve-installed-talk-now-what/).
## Advanced Usage
+1 -1
View File
@@ -1,4 +1,4 @@
import queryString from 'query-string';
import queryString from 'querystringify';
import {
FETCH_USERS_REQUEST,
+1 -1
View File
@@ -1,4 +1,4 @@
import queryString from 'query-string';
import queryString from 'querystringify';
import {
FETCH_ASSETS_REQUEST,
@@ -1,12 +1,10 @@
import React from 'react';
import styles from './Forbidden.css';
import t from 'coral-framework/services/i18n';
const Forbidden = () => (
<div className={styles.container}>
<p className={styles.copy}>
This page is for team use only. Please contact an administrator if you
want to join this team.
</p>
<p className={styles.copy}>{t('error.PAGE_NOT_AVAILABLE_ROLE')}</p>
</div>
);
@@ -30,7 +30,7 @@ class ForgotPassword extends React.Component {
href="#"
onClick={this.handleSignInLink}
>
Sign in
{t('login.sign_in')}
</a>
<Success />
</div>
@@ -48,7 +48,7 @@ class ForgotPassword extends React.Component {
onChange={this.handleEmailChange}
/>
<Button type="submit" cStyle="black" full>
{t('login.reset_password')}
{t('login.reset_password_send_button')}
</Button>
<p className={styles.cta}>
{t('login.go_back')}{' '}
@@ -14,7 +14,9 @@ import ApproveButton from './ApproveButton';
import RejectButton from 'coral-admin/src/components/RejectButton';
import CommentDeletedTombstone from './CommentDeletedTombstone';
import t, { timeago } from 'coral-framework/services/i18n';
import { buildCommentURL } from 'coral-framework/utils/url';
import TimeAgo from 'coral-framework/components/TimeAgo';
import t from 'coral-framework/services/i18n';
class UserDetailComment extends React.Component {
approve = () =>
@@ -73,9 +75,7 @@ class UserDetailComment extends React.Component {
checked={selected}
onChange={e => toggleSelect(e.target.value, e.target.checked)}
/>
<span className={styles.created}>
{timeago(comment.created_at)}
</span>
<TimeAgo className={styles.created} datetime={comment.created_at} />
{comment.editing && comment.editing.edited ? (
<span>
&nbsp;<span className={styles.editedMarker}>
@@ -112,7 +112,7 @@ class UserDetailComment extends React.Component {
/>
<a
className={styles.external}
href={`${comment.asset.url}?commentId=${comment.id}`}
href={buildCommentURL(comment.asset.url, comment.id)}
target="_blank"
rel="noopener noreferrer"
>
@@ -176,7 +176,7 @@ class People extends React.Component {
})
}
>
Suspend User
{t('modqueue.suspend')}
</ActionsMenuItem>
)}
@@ -195,7 +195,7 @@ class People extends React.Component {
})
}
>
Ban User
{t('modqueue.ban_user_actions')}
</ActionsMenuItem>
)}
</ActionsMenu>
@@ -53,7 +53,7 @@ class EmbedLink extends Component {
{t('embedlink.copy')}
</Button>
<div className={styles.copiedText}>
{this.state.copied && 'Copied!'}
{this.state.copied && t('embedlink.copied')}
</div>
</div>
</ConfigureCard>
@@ -10,7 +10,9 @@ const Wordlist = ({ suspectWords, bannedWords, onChangeWordlist }) => (
<p>{t('configure.banned_word_text')}</p>
<TagsInput
value={bannedWords}
inputProps={{ placeholder: 'word or phrase' }}
inputProps={{
placeholder: t('configure.suspect_or_forbidden_words_placeholder'),
}}
onChange={tags => onChangeWordlist('banned', tags)}
/>
</ConfigureCard>
@@ -18,7 +20,9 @@ const Wordlist = ({ suspectWords, bannedWords, onChangeWordlist }) => (
<p>{t('configure.suspect_word_text')}</p>
<TagsInput
value={suspectWords}
inputProps={{ placeholder: 'word or phrase' }}
inputProps={{
placeholder: t('configure.suspect_or_forbidden_words_placeholder'),
}}
onChange={tags => onChangeWordlist('suspect', tags)}
/>
</ConfigureCard>
@@ -61,6 +61,7 @@
line-height: 1.5;
font-weight: 300;
margin-bottom: 8px;
overflow-wrap: break-word;
}
.body {
@@ -188,4 +189,4 @@
.commentContentFooter {
flex: 1;
}
}
@@ -14,8 +14,10 @@ import cn from 'classnames';
import ApproveButton from 'coral-admin/src/components/ApproveButton';
import RejectButton from 'coral-admin/src/components/RejectButton';
import CommentDeletedTombstone from '../../../components/CommentDeletedTombstone';
import TimeAgo from 'coral-framework/components/TimeAgo';
import t, { timeago } from 'coral-framework/services/i18n';
import { buildCommentURL } from 'coral-framework/utils/url';
import t from 'coral-framework/services/i18n';
class Comment extends React.Component {
ref = null;
@@ -126,9 +128,10 @@ class Comment extends React.Component {
{comment.user.username}
</span>
<span className={styles.created}>
{timeago(comment.created_at)}
</span>
<TimeAgo
className={styles.created}
datetime={comment.created_at}
/>
{comment.editing && comment.editing.edited ? (
<span>
&nbsp;<span className={styles.editedMarker}>
@@ -168,7 +171,7 @@ class Comment extends React.Component {
<div className={styles.commentContentFooter}>
<a
className={styles.external}
href={`${comment.asset.url}?commentId=${comment.id}`}
href={buildCommentURL(comment.asset.url, comment.id)}
target="_blank"
rel="noopener noreferrer"
>
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './Story.css';
import t from 'coral-framework/services/i18n';
const formatDate = date => {
const d = new Date(date);
@@ -14,7 +15,9 @@ const Story = ({ author, title, createdAt, open, id, goToStory }) => {
<div className={styles.meta}>
<span className={styles.author}>By {author}</span>
<span className={styles.createdAt}>{formatDate(createdAt)}</span>
<span className={styles.status}>{open ? 'Open' : 'Closed'}</span>
<span className={styles.status}>
{open ? t('streams.open') : t('streams.closed')}
</span>
</div>
</li>
);
+1 -1
View File
@@ -4,7 +4,7 @@ import { createPostMessage } from 'coral-framework/services/postMessage';
document.addEventListener('DOMContentLoaded', () => {
const staticConfig = getStaticConfiguration();
const { STATIC_ORIGIN: origin } = staticConfig;
const { BASE_ORIGIN: origin } = staticConfig;
const postMessage = createPostMessage(origin);
// Get the auth element and parse it as JSON by decoding it.
@@ -1,6 +1,6 @@
import * as actions from '../constants/stream';
import { buildUrl } from 'coral-framework/utils/url';
import queryString from 'query-string';
import queryString from 'querystringify';
export const setActiveReplyBox = id => ({
type: actions.SET_ACTIVE_REPLY_BOX,
@@ -14,12 +14,13 @@ export const setSort = ({ sortOrder, sortBy }) => ({
});
export const viewAllComments = () => (dispatch, _, { pym }) => {
const search = queryString.stringify({
...queryString.parse(location.search),
comment_id: undefined,
});
const query = queryString.parse(location.search);
// remove the comment_id url param
delete query.comment_id;
const search = queryString.stringify(query);
const url = buildUrl({ ...location, search });
try {
@@ -9,6 +9,7 @@ import cn from 'classnames';
import { getTotalReactionsCount } from 'coral-framework/utils';
import t from 'coral-framework/services/i18n';
import { buildCommentURL } from 'coral-framework/utils/url';
class Comment extends React.Component {
render() {
@@ -79,9 +80,10 @@ class Comment extends React.Component {
<li>
<a
className={styles.viewLink}
href={`${this.props.comment.asset.url}?commentId=${
href={buildCommentURL(
this.props.comment.asset.url,
this.props.comment.id
}`}
)}
target="_parent"
>
<Icon name="open_in_new" className={styles.iconView} />
@@ -38,5 +38,4 @@
position: relative;
margin-top: 28px;
padding-bottom: 50px;
min-height: 600px;
}
+6 -5
View File
@@ -1,4 +1,4 @@
import queryString from 'query-string';
import queryString from 'querystringify';
import pym from 'pym.js';
import EventEmitter from 'eventemitter2';
import { buildUrl } from 'coral-framework/utils/url';
@@ -94,12 +94,13 @@ export default class Stream {
// Remove the permalink comment id from the hash.
this.pym.onMessage('coral-view-all-comments', () => {
const search = queryString.stringify({
...queryString.parse(location.search),
commentId: undefined,
});
const query = queryString.parse(location.search);
// Remove the commentId url param.
delete query.commentId;
const search = queryString.stringify(query);
const url = buildUrl({ ...location, search });
// Change the url.
+16 -4
View File
@@ -17,19 +17,23 @@ export const checkLogin = () => (
if (!result.user) {
cleanAuthData(localStorage);
dispatch(checkLoginSuccess(null));
client.resetWebsocket();
return;
}
// Reset the websocket.
client.resetWebsocket();
dispatch(checkLoginSuccess(result.user));
pym.sendMessage('coral-auth-changed', JSON.stringify(result.user));
// We don't need to reset the websocket here because if the request
// returned that there was a user (which is the case here), then the
// original request has already succeeded, or a previous call to a token
// set handler has already reset it.
})
.catch(error => {
if (error.status && error.status === 401 && localStorage) {
// Unauthorized.
cleanAuthData(localStorage);
client.resetWebsocket();
} else {
console.error(error);
}
@@ -49,7 +53,11 @@ const checkLoginSuccess = user => ({
user,
});
export const setAuthToken = token => (dispatch, _, { localStorage }) => {
export const setAuthToken = token => (
dispatch,
_,
{ localStorage, client }
) => {
localStorage.setItem('exp', jwtDecode(token).exp);
localStorage.setItem('token', token);
@@ -57,6 +65,9 @@ export const setAuthToken = token => (dispatch, _, { localStorage }) => {
// may not be able to persist the auth token any other way. Keep it in redux!
dispatch({ type: actions.SET_AUTH_TOKEN, token });
// Now that we set a token, let's reset the subscriptions.
client.resetWebsocket();
dispatch(checkLogin());
};
@@ -79,6 +90,7 @@ export const handleSuccessfulLogin = (user, token) => (
);
}
// Now that we just set a token, set the token!
client.resetWebsocket();
dispatch({
@@ -1,13 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
import { timeago } from 'coral-framework/services/i18n';
import cn from 'classnames';
import styles from './CommentTimestamp.css';
import TimeAgo from 'coral-framework/components/TimeAgo';
const CommentTimestamp = ({ className, created_at }) => (
<div className={cn(className, styles.timestamp, 'talk-comment-timestamp')}>
{timeago(created_at)}
</div>
<TimeAgo
className={cn(className, styles.timestamp, 'talk-comment-timestamp')}
datetime={created_at}
/>
);
CommentTimestamp.propTypes = {
@@ -0,0 +1,3 @@
.timeago {
cursor: pointer;
}
@@ -0,0 +1,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import { timeago } from 'coral-framework/services/i18n';
import cn from 'classnames';
import styles from './TimeAgo.css';
class TimeAgo extends React.Component {
constructor(props) {
super(props);
this.state = { timeago: true };
}
toggleDate = () => this.setState({ timeago: !this.state.timeago });
render() {
var displayTime = this.state.timeago
? timeago(this.props.datetime)
: new Date(this.props.datetime).toLocaleString();
var titleDate = !this.state.timeago
? timeago(this.props.datetime)
: new Date(this.props.datetime).toLocaleString();
return (
<span
onClick={this.toggleDate}
className={cn(
this.props.className,
styles.timeago,
'talk-comment-timeago'
)}
title={titleDate}
>
{displayTime}
</span>
);
}
}
TimeAgo.propTypes = {
datetime: PropTypes.string,
className: PropTypes.string,
};
export default TimeAgo;
+2 -2
View File
@@ -1,7 +1,7 @@
import url from 'url';
import parse from 'url-parse';
const base = document.querySelector('base');
const baseUrl = (base && url.parse(base.href)) || {};
const baseUrl = (base && parse(base.href)) || {};
export const BASE_URL = base.href;
export const BASE_PATH = baseUrl.pathname;
+9 -2
View File
@@ -3,9 +3,11 @@ import {
ENABLE_PLUGINS_DEBUG,
DISABLE_PLUGINS_DEBUG,
} from '../constants/config';
import { LOGOUT } from '../constants/auth';
import { LOGOUT, SET_AUTH_TOKEN } from '../constants/auth';
const initialState = {};
const initialState = {
auth_token: null,
};
export default function config(state = initialState, action) {
switch (action.type) {
@@ -30,6 +32,11 @@ export default function config(state = initialState, action) {
...state,
auth_token: null,
};
case SET_AUTH_TOKEN:
return {
...state,
auth_token: action.token || null,
};
case MERGE_CONFIG:
return {
...state,
+5 -9
View File
@@ -42,17 +42,13 @@ const getAuthToken = (store, storage) => {
let state = store.getState();
if (state.config && state.config.auth_token) {
// if an auth_token exists in config, use it.
return state.config.auth_token;
// If the embed is called with `embed.login(token)`, and the browser is not
// capable of storing the token in localStorage, then we would have
// persisted it to the redux state.
return state.config.auth_token || state.auth.token;
} else if (!bowser.safari && !bowser.ios && storage) {
// Use local storage auth tokens where there's a stable api.
return storage.getItem('token');
} else if (state.auth && state.auth.token) {
// Use the redux token state if the remaining methods fall out. If the embed
// is called with `embed.login(token)`, and the browser is not capable of
// storing the token in localStorage, then we would have persisted it to the
// redux state.
return state.auth.token;
}
return null;
@@ -140,7 +136,7 @@ export async function createContext({
});
const staticConfig = getStaticConfiguration();
let { LIVE_URI: liveUri, STATIC_ORIGIN: origin } = staticConfig;
let { LIVE_URI: liveUri, BASE_ORIGIN: origin } = staticConfig;
if (liveUri == null) {
// The protocol must match the origin protocol, secure/insecure.
const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
+15 -13
View File
@@ -88,21 +88,23 @@ export function createClient(options = {}) {
});
client.resetWebsocket = () => {
// Close socket connection which will also unregister subscriptions on the server-side.
wsClient.close();
if (wsClient.client) {
// Close socket connection which will also unregister subscriptions on the server-side.
wsClient.close(true);
// Reconnect to the server.
wsClient.connect();
// Reconnect to the server.
wsClient.connect();
// Reregister all subscriptions (uses non public api).
// See: https://github.com/apollographql/subscriptions-transport-ws/issues/171
Object.keys(wsClient.operations).forEach(id => {
wsClient.sendMessage(
id,
MessageTypes.GQL_START,
wsClient.operations[id].options
);
});
// Re-register all subscriptions (uses non public api).
// See: https://github.com/apollographql/subscriptions-transport-ws/issues/171
Object.keys(wsClient.operations).forEach(id => {
wsClient.sendMessage(
id,
MessageTypes.GQL_START,
wsClient.operations[id].options
);
});
}
};
return client;
+13 -7
View File
@@ -75,10 +75,15 @@ let TIMEAGO_INSTANCE;
// detectLanguage will try to get the locale from storage if available,
// otherwise will try to get it from the navigator, otherwise, it will fallback
// to the default language.
const detectLanguage = () =>
first(
const detectLanguage = () => {
var browserLanguages = navigator.languages;
//IE11 and MS-EDGE do not provide navigator.languages
if (!browserLanguages) {
browserLanguages = [navigator.language];
}
return first(
negotiateLanguages(
navigator.languages,
browserLanguages,
whitelistedLanguages || supportedLocales,
{
defaultLocale,
@@ -86,6 +91,7 @@ const detectLanguage = () =>
}
)
);
};
export function setupTranslations() {
// locale
@@ -100,10 +106,10 @@ export function setupTranslations() {
ta.register('da', daTA);
ta.register('de', deTA);
ta.register('fr', frTA);
ta.register('nl-NL', nlTA);
ta.register('pt-BR', pt_BRTA);
ta.register('zh-CN', zh_CNTA);
ta.register('zh-TW', zh_TWTA);
ta.register('nl_NL', nlTA);
ta.register('pt_BR', pt_BRTA);
ta.register('zh_CN', zh_CNTA);
ta.register('zh_TW', zh_TWTA);
TIMEAGO_INSTANCE = ta();
}
+1
View File
@@ -6,6 +6,7 @@ import { capitalize } from 'coral-framework/helpers/strings';
import assignWith from 'lodash/assignWith';
import mapValues from 'lodash/mapValues';
export * from 'coral-framework/helpers/strings';
export * from './url';
export const getTotalActionCount = (type, comment) => {
return comment.action_summaries
+9
View File
@@ -1,3 +1,5 @@
import parse from 'url-parse';
export function buildUrl(
{ protocol, hostname, port, pathname, search, hash } = window.location
) {
@@ -10,3 +12,10 @@ export function buildUrl(
port ? `:${port}` : ''
}${pathname}${search}${hash}`;
}
export function buildCommentURL(assetURL, commentID) {
const u = parse(assetURL, true);
u.query.commentId = commentID;
u.set('query', u.query);
return u.href;
}
+9 -1
View File
@@ -102,7 +102,7 @@ sidebar:
children:
- title: Authentication
url: /integrating/authentication/
- title: Asset Managment
- title: Asset Management
url: /integrating/asset-management/
- title: Configuring the Comment Stream
url: /integrating/configuring-comment-stream/
@@ -164,10 +164,18 @@ sidebar:
url: /when-youve-installed-talk/
- title: Migrating
children:
- title: Migrating from v3.x.x
url: /migration/3/
- title: Migrating to v4.0.0
url: /migration/4/
- title: Migrating to v4.1.0
url: /migration/4.1/
- title: FAQ & Troubleshooting
children:
- title: FAQ
url: /faq/
- title: Troubleshooting Tips
url: /troubleshooting-tips/
- title: Contact
url: /contact/
+27 -27
View File
@@ -22,13 +22,10 @@ to persist data. The following versions are supported:
- MongoDB 3.2+
- Redis 3.2.5+
An optional dependency for Talk is
[Docker](https://www.docker.com/community-edition#/download).
It is used during development to set up the database and can be
used to [install via Docker](#installation-from-docker). We have tested Talk
and this documentation with versions 17.06.2+.
You can run Talk (and its dependencies) locally or from [Docker](https://www.docker.com/community-edition#/download) containers. Docker is used in the local example below for the database and cache, however it is possible to run Talk without Docker by configuring your own MongoDB and Redis instances. We have tested Talk
and this documentation with Docker versions 17.06.2+.
Another optional dependency for Talk is
An optional dependency for Talk is
[Docker Compose](https://docs.docker.com/compose/install/). It
can be used to setup your environment easily for testing. We have tested Talk
and this documentation with versions 1.14.0+.
@@ -38,8 +35,9 @@ and this documentation with versions 1.14.0+.
### Installation from Docker
To use Talk without major customization you can run the application using our
provided docker image. The following is a `docker-compose.yml` file that can
be used to setup Talk:
provided docker image.
Start by making a new directory and create a file called `docker-compose.yml` and copy the following:
```yml
# For details on the syntax of docker-compose.yml files, check out:
@@ -79,7 +77,7 @@ volumes:
external: false
```
This is the bare minimum needed to run the demo, for more configuration
The environment variables listed above are the bare minimum needed to run the demo, for more configuration
variables, check out the [Configuration](/talk/configuration/) section.
@@ -104,8 +102,7 @@ Creating talk_1 ...
Creating talk_1 ... done
```
And when you run `docker-compose ps`, you should see something like:
Once everything has completed, run `docker-compose ps`, and you should see something like:
```
Name Command State Ports
@@ -116,8 +113,8 @@ talk_1 yarn start Up 0.0.0.0:3000->3000/tcp
```
Continue onto the [Running](#running) section for details on how to complete the
installation and get started using Talk.
You now have a Talk instance up and running! Continue on to the [Setup](#setup) section for details on how to complete the
initial setup and get started using Talk.
### Installation from Source
@@ -178,32 +175,35 @@ You can now start the application by running:
yarn watch:server
```
Continue onto the [Running](#running) section for details on how to complete the
Continue onto the [Setup](#setup) section for details on how to complete the
installation and get started using Talk.
## Running
## Setup
### Create Admin Account
You can now navigate to
With Talk running, you can now navigate to
[http://127.0.0.1:3000/admin/install](http://127.0.0.1:3000/admin/install)
and go through the admin installation. There you will be prompted to create your
first admin account, and specify the domain whitelist for domains that are
allowed to have the comment box on.
and walk through the initial setup steps.
* First, enter your **Organization Name** and **Organization Contact Email**. This will appear in emails when inviting new team members.
* Next, create your Admin user. You can specify an **Email Address**, **Username**, and **Password**
* Finally, enter your list of **Permitted Domains**, read [here](/talk/configuring-talk/#permitted-domains) about whitelisting domains
_During development, ensure you whitelist 127.0.0.1:3000 otherwise the
[http://127.0.0.1:3000/](http://127.0.0.1:3000/) page will not
load._
Once you've completed the installation, you can visit
[http://127.0.0.1:3000/](http://127.0.0.1:3000/) where you can
view our development area where we test out features in Talk where you can write
comments and see them in the admin interface where you can do moderation and
reconfigure the user experience.
Once the setup wizard has been completed you can log into Talk ([http://127.0.0.1:3000/](http://127.0.0.1:3000/)) using the email address and password for the Admin user account that you just created.
## Demo
From here you can test out features in Talk, see comments in the admin interface where you can do moderation, and configure the user experience.
In the next step you'll create some user comments to moderate.
### Demo Embedded Comments
If you've followed the documentation above, you'll now have a running copy of
Talk. To demonstrate what your own self-hosted copy of Talk can do, below
you'll find a demo that can be used to test the copy that is running now on your
Talk. To demonstrate what your own self-hosted copy of Talk can do, we created the demo below
that can be used to test the copy that is running now on your
machine.
In order for the demo to work, you must add
+70
View File
@@ -0,0 +1,70 @@
---
title: Migrating from v3.x.x
permalink: /migration/3/
---
## Deprecation Notices
It was previously recommended to use the user service function:
```js
Users.findOrCreateExternalUser(...);
```
If you are developing a social plugin, you should migrate this function to:
```js
Users.upsertSocialUser(...);
```
If you are developing an external auth integration (where the integration
provides) a custom displayName, you should migrate to:
```js
Users.upsertExternalUser(...);
```
## Troubleshooting Username Status
You may be affected by a side-effect of the above mentioned deprecated function
`Users.findOrCreateExternalUser(...);` if the following are true:
1. You have upgraded from Talk `< 3` to `>= 4` and have completed a database
migration
2. You have used a custom auth plugin in the past
3. You have disabled or not included the `talk-plugin-auth` as a `client` plugin
4. You have received reports that some users can not comment, and are instead
given a message `You are not authorized to perform this action.`
If this is the case, you can execute the following one time MongoDB query to
repair the affected users.
```js
db.users.update(
{
"status.username.status": {
$in: ["UNSET", "CHANGED"]
}
},
{
$set: {
"status.username.status": "SET"
},
$push: {
"status.username.history": {
status: "SET",
assigned_by: null,
created_at: ISODate()
}
}
},
{
multi: true
}
);
```
**Note: You must resolve and/or update your custom auth code to resolve the
above mentioned deprecation notices _before_ running the above mentioned MongoDB
query**
+58 -6
View File
@@ -3,15 +3,67 @@ title: FAQ
permalink: /faq/
---
## How can I get help integrating Talk into my newsroom?
## How can I get help, submit a bug, or suggest a feature?
We're here to help with newsrooms of all sizes. Email our Integration Engineer
([jeff@mozillafoundation.org](mailto:jeff@mozillafoundation.org)) to set up a meeting.
There are a few avenues to get in touch with us and others in the community for help.
## How do I request a feature or submit a bug?
To log a bug or request a feature, submit a Support ticket ([support@coralproject.net](mailto:support@coralproject.net)) and someone from our team will get back to you.
The best way is to [submit a Github issue](https://github.com/coralproject/talk/issues). Make sure you give plenty of details, our Core Team can usually respond within a few hours on weekdays.
You can also request help on Github by [submitting an issue](https://github.com/coralproject/talk/issues). This also increases your chances of having someone from the community respond to help.
And you can also search our [Coral Community](https://community.coralproject.net) to see if your issue has been solved, or to get tips from the community.
## How can our dev team contribute to Talk?
We are lucky to work with newsroom dev teams and individual contributors who span the world, and come from newsrooms of all sizes. You can read our [Contribution Guide](https://github.com/coralproject/talk/blob/master/CONTRIBUTING.md) to get started, but feel free to reach out to us via Github, or get in touch directly with Jeff via jeff@mozillafoundation.org.
We are lucky to work with newsroom dev teams and individual contributors who span the world, and come from newsrooms of all sizes. You can read our [Contribution Guide](https://github.com/coralproject/talk/blob/master/CONTRIBUTING.md) to get started, but feel free to reach out to us via Github too.
## What if we want to add a feature you don't have?
Talk is open source, so you're free to develop additional functionality and [submit a pull request](https://github.com/coralproject.net/talk).
## Do you have GDPR features?
Yes! Please read our [GDPR documentation](/talk/integrating/gdpr/) for more information and instructions to get started.
## Can I import my existing comments?
Yes! We have a community-supported [import framework](https://github.com/coralproject/talk-importer) that you can use to migrate your existing comments.
## What support is available?
Our team is small, so it's difficult for us to provide support packages. However, you can always email us at [support@coralproject.net](mailto:support@coralproject.net), and we can help answer your questions. In some cases, we can provide premium support packages either with our team, or through partners. You can inquire about this via the support email address above.
## Is there a hosted version I can purchase by monthly subscription?
Yes! We are happy to announce that as of July 2018, we provide a SaaS version of Talk, called the Coral Cloud. For a monthly subscription, you get your own hosted Talk instance to embed on your news and blog articles. [Reach out to us](mailto:support@coralproject.net) if you're interested in this option.
## Where is our data when we use Talk?
If you are hosting Talk on your own:
* Your data is stored in a MongoDB database that you provide
* The Coral Team doesnt have any access to your data
If you are using Coral Cloud Hosting for Talk:
* Your data is stored in a dedicated MongDB database that is provisioned for your in the Cloud
* Your data is completely isolated from other customers data
* The Coral Team and its third party database hosting providers use strict access controls and auditing to protected your data from unauthorized access by team members
## Does Talk have any automated moderation features to protect against spam and trolling?
Talk features a couple of plugins that provide advanced moderation:
* The [Toxic Comments plugin](/talk/plugin/talk-plugin-toxic-comments) integrates with the [Perspective API from Google](https://www.perspectiveapi.com/) to detect the likelihood of toxicity of comments in real-time
* The [Akismet plugin](/talk/plugin/talk-plugin-akismet) detects and blocks spam comments
## How much can I customize Talk?
* The CSS of the Talk comment stream can be customized by [adding your own CSS via an external stylesheet](/talk/integrating/styling-css/)
* The functionality of Talk can be [extended through the plugin framework](/talk/plugins/)
## How much does Talk cost?
* The Talk software is freely available under the Apache 2.0 open source license
* Associated costs are those for the infrastructure required to run Talk (i.e. cloud hosting fee or bare-metal server costs)
* The Coral Project offers a SaaS version of Talk. Please [get in touch with us](mailto:support@coralproject.net) to discuss pricing for your custom requirements.
+40
View File
@@ -0,0 +1,40 @@
---
title: Troubleshooting Tips
permalink: /troubleshooting-tips/
---
## How do I find out what version I'm running?
If you visit https://<YOUR TALK INSTANCE>/api/v1, it will return the version you're running and the hash for the latest commit.
## I've installed Talk but I can't see the comment stream appear on my articles
* Make sure you've adding the correct domains to your Permitted Domains in Configure > Tech Settings
* Make sure you've correctly added the embed via your CMS to your article pages
* Check the console for any errors and you can file a bug via [support](mailto:support@coralproject.net) if you can't resolve the issue
## My commenters are reporting they can't see the comment stream, but I'm able to
* If this seems to be isolated to one commenter, it could be related to 3rd party cookies
* Post 4.x Talk doesn't require 3rd party cookies to be enabled. Check the version of Talk you're using and you might consider upgrading.
* A quick fix in the meantime is to ask them to allow 3rd party cookies
* You could also try asking them to clear their browser cache
## My commenters are reporting that they cannot login to Talk
If you're using your own custom auth plugin:
* Review the code and your server logs to ensure your plugin is working correctly. Check [our auth docs](/talk/integrating/authentication/) for more tips.
* Ensure that your JWT token settings, especially expiry, is being set correctly. You can troubleshoot JWT related issues with the [JWT Debugger](https://jwt.io/).
* See if you can isolate if it's a particular group of users that are experiencing this issue, e.g. mods, admins, subscribers? Confirm they have the appropriate permissions to comment.
* Note if this is a new issue that happened after an upgrade - did you read the [migration docs](/talk/migration/3/) and the [release notes](https://github.com/coralproject/talk/releases)? This might help you resolve the issue
* Confirm that users who are affected have the correct `username.status`. If users have status `UNSET`, this is related to a bug with upgrading from 3.x to 4.x that has affected some organizations. Read more here about [upgrading from 3.x to 4.x](/talk/migration/3/).
* If you're still experiencing issues, log a [support ticket](mailto:support@coralproject.net) so we can help diagnose the issue
If you're using `talk-plugin-auth`:
* See if you can isolate if it's a particular group of users that are experiencing this issue, e.g. mods, admins, subscribers? Confirm they have the appropriate permissions to comment.
* Note if this is a new issue that happened after an upgrade - did you read the [migration docs](/talk/migration/3/) and the [release notes](https://github.com/coralproject/talk/releases)? This might help you resolve the issue.
* If you're still experiencing issues, log a [support ticket](mailto:support@coralproject.net) so we can help diagnose the issue
+2 -2
View File
@@ -22,7 +22,7 @@ Feel free to check all the utilities here: `talk/plugin-api`.
#### Stream
* `setSort`
* `showSignInDialog``
* `showSignInDialog`
### Import
```
@@ -264,7 +264,7 @@ Coral UI is a set of components to help you build your UI. This powers our core.
### Import
```js
import {Button} from 'plugin-api/beta/components/ui';
import {Button} from 'plugin-api/beta/client/components/ui';
```
### Components
+3 -3
View File
@@ -144,9 +144,9 @@ Some queries you may notice seem to return `null` or an error of
route that requires authorization. You can perform authorization a few ways in
Talk:
1. As a [Bearer Token](#Bearer-Token)
2. As a [Query Parameter](#Query-Parameter)
3. As a [Cookie](#Cookie)
1. As a [Bearer Token](#bearer-token)
2. As a [Query Parameter](#query-parameter)
3. As a [Cookie](#cookie)
Essentially, you need to get access to a JWT token that you can use to authorize
your requests. Generating one is simple, you can use the CLI tools in Talk to do
+1 -1
View File
@@ -319,7 +319,7 @@ module.exports = {
let user;
try {
const { id, provider, displayName } = profile;
user = await UsersService.findOrCreateExternalUser(
user = await UsersService.upsertSocialUser(
req.context,
id,
provider,
+5 -5
View File
@@ -18,11 +18,11 @@ By default, Talk will use "Lazy Asset Creation" to dynamically generate Assets
in Talk in order to make it easier for lighter installations. In order to have
more strict control over this flow, we will create a plugin that will:
1. Disable "Lazy Asset Creation" by [Overriding a Resolver](#Overriding-a-Resolver).
2. Create Assets from our CMS by [Creating a New Asset Route](#Creating-a-New-Asset-Route).
3. Facilitate updates from our CMS to keep Talk in sync by [Creating an Asset Update Route](#Creating-an-Asset-Update-Route).
1. Disable "Lazy Asset Creation" by [Overriding a Resolver](#overriding-a-resolver).
2. Create Assets from our CMS by [Creating a New Asset Route](#creating-a-new-asset-route).
3. Facilitate updates from our CMS to keep Talk in sync by [Creating an Asset Update Route](#creating-an-asset-update-route).
We will then modify our embed so that we can [Target the Asset](#Target-the-Asset).
We will then modify our embed so that we can [Target the Asset](#target-the-asset).
But first we should grab our basic plugin structure:
@@ -248,7 +248,7 @@ module.exports = router => {
};
```
As you can see from the previous step of [Creating a New Asset Route](#Creating-a-New-Asset-Route)
As you can see from the previous step of [Creating a New Asset Route](#creating-a-New-Asset-Route)
, we have added the new `PUT` route to the router. This is a simple addition
that allows your CMS to call into Talk when the asset has updated it's title,
it's url (or really anything in the [AssetSchema](https://github.com/coralproject/talk/blob/master/models/asset.js)) to keep the Talk Admin and links up to date.
+1 -1
View File
@@ -7,7 +7,7 @@ Using plugins and configuration variables, you can modify the way the Admin look
### Creating a Custom Moderation Queue
Talk can support custom pluggable mod queues, meaning you can write a plugin that has some logic and determines which comments should appear there. This works by adding a field modQueues` in the `index.js` of your client side plugin, like so:
Talk can support custom pluggable mod queues, meaning you can write a plugin that has some logic and determines which comments should appear there. This works by adding a field `modQueues` in the `index.js` of your client side plugin, like so:
```
modQueues: {
+297
View File
@@ -8,3 +8,300 @@ You can add your own stylesheet in Admin > Configure > Tech Settings.
If you would like to change the styling of any elements in Talk, we provide global classnames with the prefix `talk-`. The easiest way to find the classname for the element you're looking for is to use the web inspector, and then update your stylesheet accordingly.
Plugins also have their own stylesheets located in the client directory.
Here is an example stylesheet that we use on our [Coral Blog](https://coralproject.net/blog):
```css
/*
* You can use this stylesheet as a place to get started
* for styling your own version of Talk!
* Author: Sam Hankins, Coral Project, 2018
* License: Apache 2.0
*/
* {
/* font-family: inherit; */
}
html, body {
width:auto;
height:auto;
}
body {
font-family: Helvetica, 'Helvetica Neue', Verdana, sans-serif;
width: 100%;
font-size: 14px;
margin: 0px;
padding: 0px 0px 100px 0px;
height: auto !important;
}
#talk-embed-stream-container {
padding: 4px;
}
.expandForSignin {
min-height: 600px;
}
.coralButton {
margin: 5px 10px 5px 0px;
background: none;
padding: 0px;
border: none;
font-size: inherit;
}
.coralButton:hover {
border-radius: 2px;
color: #767676;
}
.coralButton i {
margin-right: 3px;
}
.coralHr {
border: 0;
height: 0;
border-top: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
}
.screen-reader-text {
clip: rect(1px, 1px, 1px, 1px);
height: 1px;
width: 1px;
overflow: hidden;
position: absolute !important;
}
/* Notification styles */
#coral-notif {
position: fixed;
border: 0;
background: rgb(105,105,105);
color: white;
border-radius: 2px;
font-weight: bold;
}
/* Info Box Styles */
.talk-plugin-infobox-info {
top: 0;
border: 0;
background: #DEEDFF;
color: #2a2a2a;
width: 100%;
text-align: left;
padding: 10px;
margin-bottom: 10px;
display: block;
box-sizing: border-box;
border-radius: 2px;
}
.talk-plugin-infobox-info em{
font-style: italic;
}
.talk-plugin-infobox-info strong{
font-weight: bold;
}
.talk-plugin-infobox-info blockquote{
border-left: solid 2px #2a2a2a;
padding-left: 10px;
}
.talk-plugin-infobox-info a{
color: #2a2a2a;
}
/* Question Box Styles */
.talk-stream-comments-container {
position: relative;
}
/* Comment styles */
.comment {
margin-bottom: 10px;
position: relative;
}
.talk-plugin-commentcontent-text {
margin-bottom: 7px;
font-size: 16px;
font-weight: 100;
line-height: 1.3;
}
/* Tag Labels */
.talk-plugin-tag-label {
background-color: #4C1066;
color: white;
display: inline-block;
border-radius: 2px;
font-size: 12px;
padding: 5px 6px;
}
/* Comment Action Styles */
.commentActionsRight, .replyActionsRight {
display: flex;
justify-content: flex-end;
}
.commentActionsLeft, .replyActionsLeft {
display: flex;
justify-content: flex-start;
float: left;
}
button.comment__action-button,
.comment__action-button button {
cursor: pointer;
}
button.comment__action-button[disabled],
.comment__action-button[disabled] button {
cursor: inherit;
}
.comment__action-button--nowrap {
white-space: nowrap;
}
.likedButton {
color: rgb(0,134,227);
}
.flaggedIcon {
color: #F00;
}
/* Flag Styles */
.talk-plugin-flags-popup-form {
margin-bottom: 10px;
}
.talk-plugin-flags-popup-header {
font-weight: bold;
font-size: 1rem;
margin-bottom: 10px;
}
.talk-plugin-flags-popup-radio {
margin:5px;
}
.talk-plugin-flags-popup-radio-label {
margin:5px;
font-weight: 400;
font-size: .9rem;
}
.talk-plugin-flags-popup-counter {
float: left;
margin-top: 21px;
color: #999;
}
.talk-plugin-flags-popup-button {
float: right;
margin-top: 10px;
}
.talk-plugin-flags-reason-text {
margin-left: 20px;
margin-top: 5px;
width: 75%;
font-size: 16px;
border: 1px solid #ccc;
max-width: calc(100% - 40px);
}
/* Close comments */
.close-comments-message {
box-sizing: border-box;
width: 100%;
height: 100px;
}
.close-comments-confirm-wrapper {
float: right;
}
.close-comments-alert {
background-color: #d65344;
color: white;
font-size: 1.33rem;
padding: 5px;
}
.close-comments-alert i.material-icons {
font-size: 16px !important;
}
/* Load More */
.talk-load-more {
text-align: center;
}
.talk-load-more button {
width: 100%;
text-align: center;
color: #FFF;
background-color: #2376D8;
border-radius: 2px;
cursor: pointer;
padding: 10px;
border-radius: 2px;
line-height: 1em;
text-transform: capitalize;
display: inline-block;
}
.talk-load-more:hover button {
background-color: #4399FF;
}
.talk-new-comments {
width: 100%;
text-align: center;
margin: 4px 0;
}
.talk-load-more-replies {
width: 100%;
padding-left: 20px;
box-sizing: border-box;
}
.talk-load-more-replies .talk-load-more-button {
background-color: transparent;
color: #979797;
border: #979797 solid 1px;
border-radius: 2px;
}
.talk-load-more-replies .talk-load-more:hover button {
background-color: #979797;
color: white;
}
.hidden {
visibility: hidden;
display: none;
}
```
+26 -1
View File
@@ -231,6 +231,15 @@ class ErrContainsProfanity extends TalkError {
}
}
class ErrHTTPNotFound extends TalkError {
constructor() {
super('http not found', {
translation_key: 'NOT_FOUND',
status: 404,
});
}
}
class ErrNotFound extends TalkError {
constructor() {
super('not found', {
@@ -334,6 +343,20 @@ class ErrCommentTooShort extends TalkError {
}
}
// ErrCommentTooLong is returned when the comment is too long.
class ErrCommentTooLong extends TalkError {
constructor(length, allowed) {
super(
'Comment was too long',
{
translation_key: 'COMMENT_TOO_LONG',
status: 400,
},
{ length, allowed }
);
}
}
// ErrAssetURLAlreadyExists is returned when a rename operation is requested
// but an asset already exists with the new url.
class ErrAssetURLAlreadyExists extends TalkError {
@@ -404,13 +427,15 @@ module.exports = {
ErrAssetURLAlreadyExists,
ErrAuthentication,
ErrCannotIgnoreStaff,
ErrCommentTooShort,
ErrCommentingDisabled,
ErrCommentTooLong,
ErrCommentTooShort,
ErrContainsProfanity,
ErrEditWindowHasEnded,
ErrEmailAlreadyVerified,
ErrEmailTaken,
ErrEmailVerificationToken,
ErrHTTPNotFound,
ErrInstallLock,
ErrInvalidAssetURL,
ErrLoginAttemptMaximumExceeded,
+6 -6
View File
@@ -84,10 +84,10 @@ const getUsersByQuery = async (
if (value.length > 0) {
// Lowercase the search term and escape any regex characters.
value = escapeRegExp(value).toLowerCase();
value = escapeRegExp(value);
// Compile the prefix search regex.
const $regex = new RegExp(`^${value}`);
const lowercasedRegex = new RegExp(`^${value.toLowerCase()}`);
const notLowercasedRegex = new RegExp(`^${value}`);
// Merge in the regex params.
query.merge({
@@ -95,7 +95,7 @@ const getUsersByQuery = async (
// Search by a prefix match on the username.
{
lowercaseUsername: {
$regex,
$regex: lowercasedRegex,
},
},
@@ -104,7 +104,7 @@ const getUsersByQuery = async (
profiles: {
$elemMatch: {
id: {
$regex,
$regex: lowercasedRegex,
},
provider: 'local',
},
@@ -114,7 +114,7 @@ const getUsersByQuery = async (
// Search by the displayName metadata field.
{
'metadata.displayName': {
$regex,
$regex: notLowercasedRegex,
},
},
],
+17 -2
View File
@@ -293,7 +293,16 @@ const setStatus = async (ctx, { id, status }) => {
*/
const editComment = async (
ctx,
{ id, asset_id, edit: { body, metadata = {} } }
{
id,
asset_id,
edit: {
body,
metadata = {},
status: commentStatus,
actions: commentActions = [],
},
}
) => {
const {
connectors: {
@@ -303,7 +312,13 @@ const editComment = async (
// Build up the new comment we're setting. We need to check this with
// moderation now.
let comment = { id, asset_id, body };
let comment = {
id,
asset_id,
body,
status: commentStatus,
actions: commentActions,
};
// Determine the new status of the comment.
const { actions, status } = await Moderation.process(ctx, comment);
+87 -12
View File
@@ -1,11 +1,14 @@
const { SubscriptionManager } = require('graphql-subscriptions');
const { SubscriptionServer } = require('subscriptions-transport-ws');
const debug = require('debug')('talk:graph:subscriptions');
const DataLoader = require('dataloader');
const { getPubsub } = require('./pubsub');
const schema = require('../schema');
const Context = require('../context');
const plugins = require('../../services/plugins');
const User = require('../../models/user');
const { singleJoinBy } = require('../loaders/util');
const { deserializeUser } = require('../../services/subscriptions');
const setupFunctions = require('./setupFunctions');
@@ -59,31 +62,103 @@ const onConnect = async (connectionParams, connection) => {
}`;
}
try {
// Pull the user off of the upgrade request.
const hydratedRequest = await deserializeUser(connection.upgradeReq);
// Update the connections upgrade request, as we'll use that to verify that
// the user is allowed each operation.
connection.upgradeReq = hydratedRequest;
} catch (err) {
console.error(err);
}
// Call all the hooks.
await Promise.all(
hooks.onConnect.map(hook => hook(connectionParams, connection))
);
};
const onOperation = (parsedMessage, baseParams, connection) => {
// Cache the upgrade request.
let upgradeReq = connection.upgradeReq;
/**
* batchedUserRefresher will get users based on ID for websocket user refresh
* operations to reduce load related to user refreshing.
*/
const batchedUserRefresher = new DataLoader(
userIDs => {
debug(`OPERATION: refreshing ${userIDs.length} users.`);
return User.find({ id: { $in: userIDs } }).then(
singleJoinBy(userIDs, 'id')
);
},
{
// Disable the cache, as this dataloader is long lived, and the point of
// using this dataloader is to batch refetch operations rather than caching
// then as we normally would.
cache: false,
}
);
// Attach the context per request.
baseParams.context = async () => {
let req;
const contextGenerator = req => {
// Pull the user(?) off the request.
const { user, jwt } = req;
try {
req = await deserializeUser(upgradeReq);
debug(`user ${req.user ? 'was' : 'was not'} on websocket request`);
} catch (e) {
console.error(e);
if (!user || !jwt) {
// There is no valid user on the request, let it continue as is then.
return async () => new Context(req);
}
return new Context({});
// Provide a flag that can be used to short circuit invalid requests.
let expiredLogin = false;
async function refreshUser() {
// Check to see if this request has been short circuited.
if (expiredLogin) {
// It has, let's exit here.
return null;
}
// Validate that the JWT for this user has not expired.
const { exp = false } = jwt;
if (exp && exp < Date.now() / 1000) {
// Mark that this token has expired, don't bother performing this syscall
// again to check the time.
expiredLogin = true;
return null;
}
try {
// Let's refresh the user from the database, as they may have changed.
const refreshedUser = await batchedUserRefresher.load(user.id);
if (!refreshedUser) {
return null;
}
return refreshedUser;
} catch (err) {
return null;
}
}
// Return the context builder function that'll use the passed context to
// generate future contexts.
return async () => {
// Refresh the user (potentially null).
const refreshedUser = await refreshUser();
// Attach the refreshedUser to the request.
req.user = refreshedUser;
// Return the new context.
return new Context(req);
};
};
const onOperation = async (parsedMessage, baseParams, connection) => {
// Pull the upgrade request off of the connection.
const upgradeReq = connection.upgradeReq;
// Attach the context handler to the request.
baseParams.context = contextGenerator(upgradeReq);
return baseParams;
};
+2 -2
View File
@@ -7,7 +7,7 @@ const fetch = require('node-fetch');
const { merge } = require('lodash');
const { version } = require('../../package.json');
const { SCRAPER_HEADERS, SCRAPER_PROXY_URL } = require('../../config');
const HttpsProxyAgent = require('https-proxy-agent');
const ProxyAgent = require('proxy-agent');
// Load the scraper with the rules.
const metascraper = require('metascraper').load([
@@ -37,7 +37,7 @@ const headers = merge(
);
// Add proxy configuration if exists.
const agent = SCRAPER_PROXY_URL ? new HttpsProxyAgent(SCRAPER_PROXY_URL) : null;
const agent = SCRAPER_PROXY_URL ? new ProxyAgent(SCRAPER_PROXY_URL) : null;
/**
* Scrapes the given asset for metadata.
+228 -7
View File
@@ -1,4 +1,7 @@
ar:
admin_sidebar:
sort_comments: 'فرز التعليقات'
view_options: 'عرض الخيارات'
already_flagged_username: 'لقد سبق لك وضع علامة باسم المستخدم هذا.'
bandialog:
are_you_sure: 'أنت متأكد أنك تريد حظر {0}؟'
@@ -35,6 +38,9 @@ ar:
name: الاسم
post: نشر
reply: رد
comment_history_blank:
info: 'سوف يظهر تاريخ تعليقاتك هنا'
title: 'لم تكتب أي تعليقات'
comment_offensive: 'هذا التعليق مسيء'
comment_plural: تعليقات
comment_post_banned_word: 'تعليقك يحتوي على كلمة أو أكثر غير مسموح بها، لذا لن يتم نشره. إذا كنت تعتقد أن هذه الرسالة خطأ، رجاء الاتصال بفريق الإشراف لدينا.'
@@ -80,6 +86,7 @@ ar:
username_and_email: 'اسم المستخدم والبريد الإلكتروني'
yes_ban_user: 'نعم إحظر المستخدم'
configure:
access_message: 'You must be an administrator to access config settings. Please find the nearest Admin and ask them to level you up!'
apply: طبق
banned_word_text: 'التعليقات التي تحتوي على هذه الكلمات أو العبارات سيتم حذفها آليا من جدول التعليقات. اطبع كلمة واضغط Enter أو Tab لزيادة الكلمة. اختياريا الصق قائمة مقسمة بالفصلات.'
banned_words_title: 'قائمة بالكلمات المحظورة'
@@ -104,11 +111,14 @@ ar:
custom_css_url_desc: 'رابط CSS الذي سيتجاوز أنماط جدول التعليقات المضمن. يمكن أن يكون داخلي أو خارجي.'
days: أيام
description: 'كإداري يمكنك تعديل إعدادات جدول التعليقات لهذه القصة:'
disable_commenting_desc: 'اكتب رسالة سيتم عرضها أثناء إلغاء تفعيل التعليقات.'
disable_commenting_title: 'إلغاء تفعيل التعليقات على مستوى الموقع'
domain_list_text: 'أدخل عناوين النطاقات التي سوف تسمح فيها لTalk.. مثلا بيئات التدريج و الإنتاج (مثلا localhost:3000 staging.domain.com domain.com).'
domain_list_title: 'النطاقات المسموح بها'
edit_comment_timeframe_heading: 'عدل الإطار الزمني للتعليق'
edit_comment_timeframe_text_post: 'ثوان لتحرير تعليقاتهم.'
edit_comment_timeframe_text_pre: 'سيكون لدى المعلقين'
edit_info: 'تحرير المعلومات'
embed_comment_stream: 'تضمين الجدول'
enable_pre_moderation: 'تمكين الإشراف المسبق'
enable_pre_moderation_text: 'يجب على المشرفين الموافقة على أي تعليق قبل نشره.'
@@ -129,9 +139,23 @@ ar:
open: مفتوح
open_stream: 'افتح الجدول'
open_stream_configuration: 'جدول التعليقات هذا مفتوح. بإغلاق هذا الجدول لن يتم قبول تعليقات جديدة، وستبقى التعليقات القديمة ظاهرة.'
organization_contact_email: 'بريد المنظمة الالكتروني'
organization_info_copy: 'نستخدم هذه المعلومات في إشعارات البريد الإلكتروني التي يتم إنشاؤها بواسطة Talk. يعمل هذا على توصيل الرسائل إلى مؤسستك ، ويوفر طريقة للمستخدمين للاتصال بك إذا كانت لديهم مشكلة في حسابهم.'
organization_info_copy_2: 'We recommend creating a generic email account (eg. community@yournewsroom.com) for this purpose. This means it can remain consistent over time, and doesn''t expose a name that users could target if their account were blocked.'
organization_information: 'معلومات المنظمة'
organization_name: 'اسم المنظمة'
product_guide_link: 'دليل المنتج'
report_bug_or_feedback: 'Report a bug or give feedback'
require_email_verification: 'يلزم التحقق من البريد الإلكتروني'
require_email_verification_text: 'يجب على المستخدمين الجدد التحقق من بريدهم الإلكتروني قبل التعليق'
save: حفظ
save_changes: 'احفظ التعديلات'
save_changes_dialog:
cancel: إلغاء
copy: 'لقد أجريت تغييرًا واحدًا أو أكثر بدون حفظ. هل تريد حفظ التغييرات أو تجاهلها؟'
discard: تجاهل
save_settings: 'احفظ التغييرات'
unsaved_changes: 'التغييرات غير المحفوظة'
shortcuts: اختصارات
sign_out: خروج
stories: قصص
@@ -140,11 +164,13 @@ ar:
suspect_word_title: 'قائمة الكلمات المشبوهة'
tech_settings: 'إعدادات تقنية'
title: 'تهيئة جدول التعليق'
view_last_version: 'عرض أحدث إصدار'
weeks: أسابيع
wordlist: 'الكلمات المحظورة'
confirm_email:
click_to_confirm: 'إضغط في الأسفل لتأكيد البريد الألكتروني'
confirm: تأكيد
email_confirmation: 'تأكيد البريد الإلكتروني'
continue: واصل
createdisplay:
check_the_form: 'استمارة غير صالحة. يرجى التحقق من الحقول'
@@ -180,9 +206,13 @@ ar:
if_you_did_not: 'إذا لم تطلب ذلك، يمكنك تجاهل هذه الرسالة الإلكترونية.'
subject: 'تأكيد البريد الإلكتروني'
to_confirm: 'لتأكيد الحساب، يرجى زيارة الرابط التالي:'
password_change:
body: "تم تغيير كلمة المرور على حسابك. \n\n إذا لم تطلب هذا التغيير ، فيرجى الاتصال بنا على {0}."
subject: '{0} تغيير كلمة السر'
password_reset:
if_you_did: 'اذا فعلت،'
please_click: 'الرجاء النقر هنا لإعادة تعيين كلمة المرور'
subject: 'إعادة ضبط كلمة المرور'
we_received_a_request: 'لقد تلقينا طلبا لإعادة تعيين كلمة المرور. إذا لم تطلب هذا التغيير، فيمكنك تجاهل هذه الرسالة الإلكترونية.'
suspended:
subject: 'تم تعليق حسابك'
@@ -191,28 +221,36 @@ ar:
copy: 'نسخ إلى الحافظة'
error:
ALREADY_EXISTS: 'المورد موجود من قبل'
AUTHENTICATION: 'حدث خطأ أثناء محاولة مصادقة حسابك.'
CANNOT_IGNORE_STAFF: 'لا يمكن تجاهل الموظفين.'
COMMENT_PARENT_NOT_VISIBLE: 'التعليق الذي ترد عليه تمت إزالته أو غير موجود.'
COMMENT_TOO_SHORT: 'يجب أن تكون التعليقات أكثر من حرف واحد، يرجى مراجعة تعليقك وإعادة المحاولة.'
COMMENT_TOO_LONG: 'يتجاوز النص الحد الأقصى للطول المسموح'
COMMENTING_CLOSED: 'تم إغلاق فاعلية التعليق'
COMMENTING_DISABLED: 'التعليق معطّل حاليًا على هذا الموقع'
confirm_password: 'كلمات المرور غير متطابقة. يرجى التحقق مرة أخرى'
DELETION_NOT_SCHEDULED: 'لم يكن من المقرر الحذف'
EDIT_USERNAME_NOT_AUTHORIZED: 'ليس لديك إذن بتحديث اسم المستخدم الخاص بك.'
EDIT_WINDOW_ENDED: 'لم يعد بإمكانك تحرير هذا التعليق. نافذة الوقت للقيام بذلك قد انتهت صلاحيتها.'
email: 'ليس بريدا إلكترونيا صالحا'
EMAIL_ALREADY_VERIFIED: 'عنوان البريد الإلكتروني تم التحقق منه.'
EMAIL_IN_USE: 'البريد الالكتروني قيد الاستخدام'
email_not_verified: 'عنوان البريد الإلكتروني {0} لم يتم التحقق منه.'
EMAIL_NOT_VERIFIED: 'لم يتم التحقق من عنوان البريد الإلكتروني'
email_password: 'مجموعة البريد الإلكتروني و / أو كلمة المرور غير صحيحة.'
EMAIL_REQUIRED: 'مطلوب عنوان البريد الإلكتروني'
EMAIL_VERIFICATION_TOKEN_INVALID: 'رمز التحقق من البريد الإلكتروني غير صالح.'
INCORRECT_PASSWORD: 'كلمة سر خاطئة'
INVALID_ASSET_URL: 'رابط المادة غير صالح'
LOGIN_MAXIMUM_EXCEEDED: 'لقد أجريت العديد من محاولات إدخال كلمة المرور غير الناجحة. أرجو الإنتظار.'
network_error: 'فشل الاتصال بالخادم. تحقق من اتصالك بالإنترنت وحاول مرة أخرى.'
NO_SPECIAL_CHARACTERS: 'يمكن أن تحتوي أسماء المستخدمين على أحرف, أرقام و _ فقط'
NOT_AUTHORIZED: 'غير مصرح لك بتنفيذ هذا الإجراء.'
NOT_FOUND: 'المورد غير موجود'
organization_contact_email: 'البريد الإلكتروني للمؤسسة غير صالح.'
organization_name: 'يجب أن يحتوي اسم المؤسسة على أحرف أو أرقام فقط.'
password: 'يجب أن تكون كلمة المرور 8 أحرف على الأقل'
PASSWORD_INCORRECT: 'تم إدخال كلمة المرور الحالية بشكل غير صحيح'
PASSWORD_LENGTH: 'كلمة المرور قصيرة جدا'
PASSWORD_REQUIRED: 'يجب إدخال كلمة مرور'
PASSWORD_RESET_TOKEN_INVALID: 'رابط إعادة تعيين كلمة المرور غير صالح.'
@@ -227,6 +265,13 @@ ar:
USERNAME_REQUIRED: 'يجب إدخال اسم مستخدم'
flag_comment: 'الإبلاغ عن تعليق'
flag_reason: 'سبب الإبلاغ (اختياري)'
flag_reasons:
username:
impersonating: 'هذا المستخدم ينتحل شخصية'
nolike: 'أنا لا أحب اسم المستخدم هذا'
offensive: 'اسم المستخدم هذا مسيء'
other: آخر
spam: 'هذا يشبه الإعلان / التسويق'
flag_username: 'بلغ عن اسم المستخدم'
flagged_usernames:
notify_approved: '{0} وافق على اسم المستخدم {1}'
@@ -237,13 +282,13 @@ ar:
reasons:
comment:
banned_word: 'كلمة محظورة'
body_count: 'يتجاوز النص الحد الأقصى للطول المسموح'
comment_noagree: أعارض
comment_offensive: مسيء
comment_other: آخر
comment_spam: 'غير مرغوب فيه'
links: رابط
suspect_word: 'كلمة مشتبهة'
trust: Karma
user:
username_impersonating: 'إنتحال شخصية'
username_nolike: 'لم يعجبنى'
@@ -256,6 +301,7 @@ ar:
changed_name:
msg: 'يتم مراجعة تغيير اسم المستخدم من قبل فريق الإشراف لدينا.'
comment: تعليق
comment_is_deleted: 'حذف هذا المعلق حسابه.'
comment_is_hidden: 'هذا التعليق غير متاح.'
comment_is_ignored: 'هذا التعليق مخفي لأنك تجاهلت هذا المستخدم.'
comment_is_rejected: 'لقد رفضت هذا التعليق.'
@@ -280,10 +326,92 @@ ar:
view_more_comments: 'عرض مزيد من التعليقات'
view_reply: 'عرض الرد'
from_settings_page: 'من صفحة الملف الشخصي يمكنك مشاهدة سجل التعليقات.'
install:
add_organization:
description: 'Please tell us the name of your organization. This will appear in emails when inviting new team members.'
label: 'Organization Name'
save: حفظ
create:
confirm_password: 'تأكيد كلمة المرور'
email: 'عنوان البريد الإلكتروني'
organization_contact_email: 'Organization Contact Email'
password: كلمه المرور
save: حفظ
username: اسم المستخدم
final:
close: 'Close this Installer'
description: 'Thanks for installing Talk! We sent an email to verify your email address. While you finish setting up the account, you can start engaging with your readers now.'
launch: 'Launch Talk'
initial:
description: 'Let''s set up your Talk community in just a few short steps.'
submit: 'Get Started'
permitted_domains:
description: 'Enter the domains you would like to permit for Talk, e.g. your local, staging and production environments (ex. localhost:3000, staging.domain.com, domain.com).'
submit: 'Finish install'
title: 'Permitted domains'
like: إعجاب
loading_results: 'جار تحميل النتائج'
login:
email_address: 'البريد الإلكتروني'
forgot_password: 'نسيت كلمه المرور؟'
go_back: 'عُد'
sign_in: 'تسجيل الدخول'
sign_in_button: 'تسجيل الدخول'
sign_in_message: 'تسجيل الدخول للتفاعل مع مجتمعك.'
password: كلمه المرور
request_passowrd: 'اطلب واحدة جديده.'
team_sign_in: 'تسجيل الدخول لفريق العمل'
marketing: 'هذا يشبه الإعلان / التسويق'
moderate_all_streams: 'Moderate comments on All Stories'
moderate_this_stream: 'أشرف على هذا الجدول'
modqueue:
account: 'account flags'
actions: Actions
all: all
all_streams: 'All Streams'
approve: Approve
approved: Approved
ban_user: Ban
billion: B
close: Close
empty_queue: 'No more comments to moderate! You''re all caught up. Go have some ☕️'
flagged: flagged
jump_to_queue: 'Jump to specific queue'
less_detail: 'Less detail'
likes: likes
million: M
mod_faster: 'Moderate faster with keyboard shortcuts'
moderate: 'Moderate →'
more_detail: 'More detail'
navigation: Navigation
new: New
newest_first: 'Newest First'
next_comment: 'Go to the next comment'
next_queue: 'Switch queues'
notify_accepted: '{0} accepted comment "{1}"'
notify_edited: '{0} edited comment "{1}"'
notify_flagged: '{0} flagged comment "{1}"'
notify_rejected: '{0} rejected comment "{1}"'
notify_reset: '{0} reset status of comment "{1}"'
oldest_first: 'Oldest First'
premod: pre-mod
prev_comment: 'Go to the previous comment'
reject: Reject
rejected: Rejected
reply: Reply
reported: reported
select_stream: 'Select Stream'
shift_key:
shortcuts: Shortcuts
show_shortcuts: 'Show Shortcuts'
singleview: 'Zen mode'
sort: Sort
system_withheld: 'System Withheld'
thismenu: 'Open this menu'
thousand: k
toggle_search: 'Open search'
try_these: 'Try these'
view_more_shortcuts: 'View more shortcuts'
my_comment_history: 'سجل التعليقات'
name: اسم
no_agree_comment: 'لا أوافق على هذا التعليق'
@@ -292,6 +420,7 @@ ar:
other: آخر
password_reset:
change_password: 'تغيير كلمة السر'
change_password_help: 'يرجى إدخال كلمة مرور جديدة لاستخدامها لتسجيل الدخول. اجعلها آمنة!'
confirm_new_password: 'تأكيد كلمة السر الجديدة'
mail_sent: 'إذا كان لديك حساب مسجل، فقد تم إرسال رابط إعادة تعيين كلمة المرور إلى هذا البريد الإلكتروني'
new_password: 'كلمة السر الجديدة'
@@ -302,6 +431,25 @@ ar:
post: نشر
profile: 'الملف الشخصي'
profile_settings: إعدادات
reject_username:
description_notify: 'Suspending this user will temporarily disable their account.'
description_reject: 'Would you like to temporarily ban this user because of their {0}? Doing so will temporarily suspend this user until they rewrite their {0}.'
email_message_reject: 'Another member of the community recently flagged your username for review. Because of its content your user was rejected. This means you can no longer comment, like, or flag content until you rewrite your username. Please email us if you have any questions or concerns.'
no_cancel: 'No cancel'
send: Send
suspend_user: 'Suspend User'
title_notify: 'Notify the user of their temporary suspension'
title_reject: 'We noticed you rejected a username'
username: username
write_message: 'Write a message'
yes_suspend: 'Yes suspend'
reject_username_dialog:
cancel: إلغاء
description: 'ساعدنا على الفهم'
message: 'سبب الإبلاغ (اختياري)'
reason: السبب
reject_username: 'رفض اسم المستخدم'
title: 'رفض اسم المستخدم'
reply: رد
report: أبلغ
report_notif: 'شكرا على الإبلاغ عن هذا التعليق. تم إبلاغ فريق الإشراف لدينا وسيراجعه قريبًا.'
@@ -324,17 +472,90 @@ ar:
no_comments: 'لا توجد تعليقات حتى الآن، لماذا لا تكتب واحد؟'
no_comments_and_closed: 'لم تكن هناك تعليقات على هذه المقالة.'
temporarily_suspended: 'وفقا لإرشادات المجموعة {0}، تم تعليق حسابك مؤقتا. الرجاء إعادة الانضمام إلى المحادثة {1}.'
streams:
all: All
article: Story
closed: Closed
empty_result: 'No assets match this search. Maybe try widening your search?'
filter_streams: 'Filter Streams'
most_recent_stories: 'Most Recent Stories'
newest: Newest
no_results: 'No results'
oldest: Oldest
open: Open
pubdate: 'Publication Date'
search: Search
search_results: 'Search Results'
sort_by: 'Sort By'
status: 'Stream Status'
stream_status: 'Stream Status'
suspenduser:
cancel: Cancel
day: '{0} days'
days: '{0} days'
description_notify: 'Suspending this user will temporarily disable their account.'
description_suspend: 'You are suspending {0}. This comment will go to the Rejected queue, and {0} will not be allowed to like, report, reply or post until the suspension time is complete.'
email_message_suspend: "Dear {0},\n\nIn accordance with {1}s community guidelines, your account has been temporarily suspended. During the suspension, you will be unable to comment, flag or engage with fellow commenters. Please rejoin the conversation {2}."
hour: '{0} hours'
hours: '{0} hours'
notify_suspend_until: 'User {0} has been temporarily suspended. This suspension will automatically end {1}.'
one_hour: '1 hour'
select_duration: 'Select suspension duration'
send: Send
suspend_user: 'Suspend User'
title_notify: 'Notify the user of their temporary suspension'
title_suspend: 'Suspend User'
write_message: 'Write a message'
thank_you: 'نحن نقدر سلامتك وردود الفعل. سيراجع المشرف التقرير الخاص بك'
user:
bio_flags: 'flags for this bio'
user_bio: 'User Bio'
username_flags: 'flags for this username'
user_detail:
all: All
ban: 'Ban User'
banned: Banned
email: Email
id: ID
karma: Karma
karma_docs_link: 'https://docs.coralproject.net/talk/trust/#user-karma-score'
learn_more: 'Learn More'
member_since: 'Member Since'
reject_rate: 'Reject Rate'
reject_username: 'Reject Username'
rejected: Rejected
remove_ban: 'Remove Ban'
remove_suspension: 'Remove Suspension'
suspend: 'Suspend User'
suspended: Suspended
total_comments: 'Total Comments'
unreliable: Unreliable
user_history: 'User History'
user_karma_score: 'User Karma Score'
username: Username
username_needs_approval: 'Username needs approval'
username_rejected: 'Username rejected'
user_history:
action: Action
ban_removed: 'Ban removed'
date: Date
suspended: 'Suspended, {0}'
suspension_removed: 'Suspension removed'
system: System
taken_by: 'Taken By'
user_banned: 'User banned'
username_status: 'Username {0}'
user_impersonating: 'هذا المستخدم ينتحل شخصية'
user_no_comment: 'لم تترك تعليقا مطلقا. إنضم إلى المحادثة!'
username_offensive: 'اسم المستخدم هذا مسيء'
validators:
confirm_password: 'ﻚﻠﻣﺎﺗ ﺎﻠﻣﺭﻭﺭ ﻎﻳﺭ ﻢﺘﻃﺎﺒﻗﺓ. ﻱﺮﺟﻯ ﺎﻠﺘﺤﻘﻗ ﻡﺭﺓ ﺄﺧﺭﻯ'
required: 'ﻩﺬﻫ ﺎﻠﺧﺎﻧﺓ ﻢﻄﻟﻮﺒﻫ'
verify_email: 'ﻞﻴﺳ ﺏﺮﻳﺩﺍ ﺈﻠﻜﺗﺭﻮﻨﻳﺍ ﺹﺎﻠﺣﺍ'
verify_organization_name: 'ﻲﺠﺑ ﺄﻧ ﻲﺤﺗﻮﻳ ﺎﺴﻣ ﺎﻠﻣﺆﺴﺳﺓ ﻊﻟﻯ ﺄﺣﺮﻓ ﺃﻭ ﺃﺮﻗﺎﻣ ﻒﻘﻃ.'
verify_password: 'ﻲﺠﺑ ﺄﻧ ﺖﻛﻮﻧ ﻚﻠﻣﺓ ﺎﻠﻣﺭﻭﺭ 8 ﺄﺣﺮﻓ ﻊﻟﻯ ﺍﻸﻘﻟ'
verify_username: 'ﻲﻤﻜﻧ ﺄﻧ ﺖﺤﺗﻮﻳ ﺄﺴﻣﺍﺀ ﺎﻠﻤﺴﺘﺧﺪﻤﻴﻧ ﻊﻟﻯ ﺃﺮﻗﺎﻣ, ﺄﺣﺮﻓ ﻭ _ ﻒﻘﻃ'
confirm_email: 'عناوين البريد الإلكتروني لا تتطابق. يرجى التحقق مرة أخرى.'
confirm_password: 'كلمات المرور غير متطابقة. يرجى التحقق مرة أخرى'
required: 'هذه الخانة مطلوبة'
verify_email: 'ليس بريدا إلكترونيا صالحا'
verify_organization_name: 'يجب أن يحتوي اسم المؤسسة على أحرف أو أرقام فقط.'
verify_password: 'يجب أن تكون كامة المرور 8 أحرف على اﻷقل'
verify_username: 'يمكن أن تحتوي أسماء المستخدمين على أرقام, أحرف ﻭ _ فقط'
view_conversation: 'عرض المحادثة'
your_account_has_been_banned: 'تم حظر حسابك.'
your_account_has_been_suspended: 'تم تعليق حسابك مؤقتا.'
+1 -1
View File
@@ -197,6 +197,7 @@ da:
CANNOT_IGNORE_STAFF: 'Kan ikke ignorere personale.'
COMMENT_PARENT_NOT_VISIBLE: 'Den kommentar, du svarer på er blevet fjernet eller eksisterer ikke.'
COMMENT_TOO_SHORT: 'Din kommentar skal indeholde noget'
COMMENT_TOO_LONG: 'Body overstiger max længde'
COMMENTING_CLOSED: 'Kommentering er allerede lukket'
confirm_password: 'Kodeordene matcher ikke. Tjek venligst igen.'
EDIT_USERNAME_NOT_AUTHORIZED: 'Du har ikke tilladelse til at opdatere dit brugernavn.'
@@ -238,7 +239,6 @@ da:
reasons:
comment:
banned_word: 'Forbudt ord'
body_count: 'Body overstiger max længde'
comment_noagree: Uenig
comment_offensive: Offensiv
comment_other: Andre
+4 -1
View File
@@ -48,6 +48,8 @@ de:
comment_post_notif_premod: 'Vielen Dank für Ihren Kommentar. Unsere Moderatoren werden ihn in Kürze bearbeiten.'
comment_singular: Kommentar
common:
copied: 'Kopiert'
notsupported: 'Nicht unterstützt'
copy: Kopieren
error: 'Ein Problem ist aufgetreten.'
reaction: 'Reaktion'
@@ -216,11 +218,13 @@ de:
embed_comments_tab: Kommentare
embedlink:
copy: 'In die Zwischenablage kopieren'
copied: 'Kopiert'
error:
ALREADY_EXISTS: 'Ressource existiert bereits'
CANNOT_IGNORE_STAFF: 'Mitarbeiter können nicht ignoriert werden.'
COMMENT_PARENT_NOT_VISIBLE: 'Der Kommentar, auf den Sie antworten möchten, wurde entfernt oder existiert nicht.'
COMMENT_TOO_SHORT: 'Kommentare sollten mehr als ein Zeichen enthalten, bitte überprüfen Sie Ihren Kommentar und probieren Sie es erneut.'
COMMENT_TOO_LONG: 'Text überschreitet Zeichenlimit'
COMMENTING_CLOSED: 'Kommentarbereich ist bereits geschlossen'
COMMENTING_DISABLED: 'Die Kommentarfunktion ist derzeit abgeschaltet'
confirm_password: 'Passwörter nicht identisch. Bitte erneut überprüfen'
@@ -270,7 +274,6 @@ de:
reasons:
comment:
banned_word: 'Unzulässiges Wort'
body_count: 'Text überschreitet Zeichenlimit'
comment_noagree: 'Andere Meinung'
comment_offensive: Unangemessen
comment_other: Anderes
+9 -1
View File
@@ -49,6 +49,8 @@ en:
comment_singular: Comment
common:
copy: Copy
copied: 'Copied'
notsupported: 'Not supported'
error: 'An error has occurred.'
reaction: reaction
reactions: reactions
@@ -144,6 +146,7 @@ en:
organization_info_copy_2: 'We recommend creating a generic email account (eg. community@yournewsroom.com) for this purpose. This means it can remain consistent over time, and doesn''t expose a name that users could target if their account were blocked.'
organization_information: 'Organization Information'
organization_name: 'Organization Name'
suspect_or_forbidden_words_placeholder: 'Word or phrase'
product_guide_link: 'Product Guide'
report_bug_or_feedback: 'Report a bug or give feedback'
require_email_verification: 'Require Email Verification'
@@ -219,12 +222,14 @@ en:
embed_comments_tab: Comments
embedlink:
copy: 'Copy to Clipboard'
copied: 'Copied'
error:
ALREADY_EXISTS: 'Resource already exists'
AUTHENTICATION: 'An error occurred trying to authenticate your account.'
CANNOT_IGNORE_STAFF: 'Cannot ignore Staff members.'
COMMENT_PARENT_NOT_VISIBLE: 'The comment that you''re replying to has been removed or doesn''t exist.'
COMMENT_TOO_SHORT: 'Comments should be more than one character, please revise your comment and try again.'
COMMENT_TOO_LONG: 'Body exceeds max length'
COMMENTING_CLOSED: 'Commenting is already closed'
COMMENTING_DISABLED: 'Commenting is currently disabled on this site'
confirm_password: 'Passwords don''t match. Please check again'
@@ -249,6 +254,7 @@ en:
organization_contact_email: 'Organization email is not valid.'
organization_name: 'Organization name must only contain letters or numbers.'
password: 'Password must be at least 8 characters'
PAGE_NOT_AVAILABLE_ROLE: 'This page is for team use only. Please contact an administrator if you want to join this team.'
PASSWORD_INCORRECT: 'Your current password was entered incorrectly'
PASSWORD_LENGTH: 'Password is too short'
PASSWORD_REQUIRED: 'Must input a password'
@@ -281,7 +287,6 @@ en:
reasons:
comment:
banned_word: 'Banned Word'
body_count: 'Body exceeds max length'
comment_noagree: Disagree
comment_offensive: Offensive
comment_other: Other
@@ -359,6 +364,7 @@ en:
sign_in_button: 'Sign Inx'
sign_in_message: 'Sign in to interact with your community.'
password: Password
reset_password_send_button: 'Retrieve Password'
request_passowrd: 'Request a new one.'
team_sign_in: 'Team sign in'
marketing: 'This looks like an ad/marketing'
@@ -372,6 +378,7 @@ en:
approve: Approve
approved: Approved
ban_user: Ban
ban_user_actions: 'Ban User'
billion: B
close: Close
empty_queue: 'No more comments to moderate! You''re all caught up. Go have some ☕️'
@@ -406,6 +413,7 @@ en:
show_shortcuts: 'Show Shortcuts'
singleview: 'Zen mode'
sort: Sort
suspend: 'Suspend User'
system_withheld: 'System Withheld'
thismenu: 'Open this menu'
thousand: k
+17 -3
View File
@@ -139,6 +139,8 @@ es:
organization_info_copy_2: 'Recomendamos crear un email genérico (ej: community@yournewsroom.com) for this purpose. Esto significa que puede permanecer consistente con el tiempo y no expone un nombre que los usuarios puedan atacar si su cuenta fue bloqueada.'
organization_information: 'Información de la Organización'
organization_name: 'Nombre de la Organización'
product_guide_link: 'Guía de Talk'
report_bug_or_feedback: 'Registrar un error de Talk'
require_email_verification: 'Necesita confirmación su correo'
require_email_verification_text: 'Nuevos usuarios deben confirmar sus correos antes de comentar'
save: Guardar
@@ -157,6 +159,7 @@ es:
suspect_word_title: 'Lista de palabras sospechosas'
tech_settings: 'Configuración Técnica'
title: 'Configurar los comentarios'
view_last_version: 'Ver la versión de Talk'
weeks: Semanas
wordlist: 'Palabras Suspendidas'
confirm_email:
@@ -212,6 +215,7 @@ es:
CANNOT_IGNORE_STAFF: 'No puede ignorar a miembros del Staff.'
COMMENT_PARENT_NOT_VISIBLE: 'El comentario a la que estás contestando ha sido eliminado o no existe.'
COMMENT_TOO_SHORT: 'Tu comentario debe tener algo escrito'
COMMENT_TOO_LONG: 'El texto excede el límite permitido'
COMMENTING_CLOSED: 'Los comentarios ya estan cerrados'
confirm_password: 'Las contraseñas no coinciden. Inténtelo nuevamente'
EDIT_USERNAME_NOT_AUTHORIZED: 'No tiene permiso para editar el nombre de usuario.'
@@ -257,7 +261,6 @@ es:
reasons:
comment:
banned_word: 'Palabra prohibida'
body_count: 'El texto exede el límite permitido'
comment_noagree: 'No está de acuerdo'
comment_offensive: 'Es ofensivo'
comment_other: 'Otra razón'
@@ -280,7 +283,7 @@ es:
comment_is_ignored: 'Este comentario está escondido porque has ignorado al usuario.'
comment_is_rejected: 'Has rechazado este comentario.'
comments: comentarios
configure_stream: 'Configurar Hilo de Comentarios'
configure_stream: 'Configurar'
content_not_available: 'Este contenido no se encuentra disponible'
edit_name:
button: Enviar
@@ -324,6 +327,17 @@ es:
submit: 'Terminar la Instalación'
title: 'Dominios permitidos'
like: 'Me gusta'
login:
email_address: 'Dirección de correo'
forgot_password: '¿Olvidaste tu contraseña?'
go_back: 'Regresar'
sign_in: 'Entrar'
sign_in_button: 'Entrar'
sign_in_message: 'Regístrese para interactuar con su comunidad.'
password: Contraseña
reset_password_send_button: 'Recuperar Contraseña'
request_password: 'Solicita uno nuevo.'
team_sign_in: 'Entrada del equipo'
loading_results: 'Cargando Resultados'
marketing: 'Esto parece una propaganda'
moderate_this_stream: 'Moderar este hilo de comentarios'
@@ -421,7 +435,7 @@ es:
step_2_header: 'Ayúdanos a comprender'
step_3_header: 'Gracias por tu participación'
stream:
all_comments: 'Todos los comentarios'
all_comments: 'Todos'
comment_not_found: 'Este comentario ha sido eliminado o no existe.'
no_comments: 'Todavía no hay comentarios. ¿Por qué no escribes uno?'
no_comments_and_closed: 'No hubo comentarios en este artículo.'
+1 -1
View File
@@ -197,6 +197,7 @@ fi_FI:
CANNOT_IGNORE_STAFF: 'Työntekijöitä ei voi jättää huomioimatta'
COMMENT_PARENT_NOT_VISIBLE: 'Kommenttia, johon yrität vastata, ei enää ole.'
COMMENT_TOO_SHORT: 'Kommentin tulee olla vähintään kaksi merkkiä pitkä. Tarkista kirjoittamasi teksti.'
COMMENT_TOO_LONG: Liian pitkä viesti'
COMMENTING_CLOSED: 'Kommentointi on suljettu'
confirm_password: 'Salasanat eivät täsmää. Tarkista, ole hyvä.'
EDIT_USERNAME_NOT_AUTHORIZED: 'Sinulla ei ole oikeutta päivittää tai muokata käyttäjänimeä.'
@@ -240,7 +241,6 @@ fi_FI:
reasons:
comment:
banned_word: 'Kielletty sana'
body_count: 'Liian pitkä viesti'
comment_noagree: 'Olen eri mieltä'
comment_offensive: Loukkaava
comment_other: Muu
+1 -1
View File
@@ -191,6 +191,7 @@ fr:
CANNOT_IGNORE_STAFF: 'Ne peut pas ignorer les membres de l''équipe.'
COMMENT_PARENT_NOT_VISIBLE: 'Le commentaire auquel vous répondez a été supprimé ou nexiste plus.'
COMMENT_TOO_SHORT: 'Votre commentaire doit contenir quelque chose'
COMMENT_TOO_LONG: 'Le texte dépasse la longueur maximale'
COMMENTING_CLOSED: 'Les commentaires sont déjà fermés'
confirm_password: 'Les mots de passe ne correspondent pas. Vérifiez à nouveau'
EDIT_USERNAME_NOT_AUTHORIZED: 'Vous n''avez pas la permission de mettre à jour votre nom d''utilisateur.'
@@ -235,7 +236,6 @@ fr:
reasons:
comment:
banned_word: 'Mot banni'
body_count: 'Le texte dépasse la longueur maximale'
comment_noagree: 'Pas daccord'
comment_offensive: Offensive
comment_other: Autre
+1 -1
View File
@@ -197,6 +197,7 @@ nl_NL:
CANNOT_IGNORE_STAFF: 'Kan geen Staff-leden negeren.'
COMMENT_PARENT_NOT_VISIBLE: 'De reactie waarop je probeert te reageren bestaat niet of is inmiddels verwijderd.'
COMMENT_TOO_SHORT: 'Reacties moeten meer dan één teken hebben. Herzie je reactie en probeer opnieuw.'
COMMENT_TOO_LONG: 'Tekst is te lang'
COMMENTING_CLOSED: 'Reageren is al afgesloten.'
confirm_password: 'Wachtwoorden komen niet overeen. Controleer opnieuw.'
EDIT_USERNAME_NOT_AUTHORIZED: 'Je bent niet gemachtigd om je gebruikersnaam te wijzigen.'
@@ -239,7 +240,6 @@ nl_NL:
reasons:
comment:
banned_word: 'Geblokeerd woord'
body_count: 'Tekst is te lang'
comment_noagree: 'Niet eens'
comment_offensive: Aanstootgevend
comment_other: Anders
+75 -7
View File
@@ -38,6 +38,9 @@ pt_BR:
name: Nome
post: Enviar
reply: Responder
comment_history_blank:
info: 'Uma história de seus comentários irá aparecer aqui'
title: 'Você não escreveu nenhum comentário'
comment_offensive: 'Este comentário é ofensivo'
comment_plural: Comentários
comment_post_banned_word: 'Seu comentário não foi publicado porque contém conteúdo inadequado.'
@@ -83,6 +86,7 @@ pt_BR:
username_and_email: 'Nome de usuário e email'
yes_ban_user: 'Sim, banir este usuário'
configure:
access_message: 'Você deve ser um administrador para acessar as configurações. Por favor procure o administrador mais próximo e solicite acesso.'
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 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'
@@ -140,11 +144,19 @@ pt_BR:
organization_info_copy_2: 'Recomendamos a criação de uma conta de e-mail genérica (por exemplo, comunidade@suaredacao.com) para essa finalidade. Isso significa que ele pode permanecer consistente ao longo do tempo e não expor um nome que os usuários pudessem segmentar se a conta fosse bloqueada.'
organization_information: 'Informações da Organização'
organization_name: 'Nome da Organização'
suspect_or_forbidden_words_placeholder: 'Palavra ou frase'
product_guide_link: 'Guia do Produto'
report_bug_or_feedback: 'Relatar bug ou dar um feedback'
require_email_verification: 'Requerer verificação de email'
require_email_verification: 'Solicitar verificação de email'
require_email_verification_text: 'Novos usuários devem verificar seu e-mail antes de comentar'
save: Salvar
save_changes: 'Salvar alterações'
save_changes_dialog:
cancel: Cancelar
copy: 'Você fez uma ou mais alterações sem salvar. Gostaria de salvar ou descartar suas alterações?'
discard: Discartar
save_settings: 'Configurações salvas'
unsaved_changes: 'Alterações não salvas'
shortcuts: Atalhos
sign_out: Sair
stories: Histórias
@@ -159,6 +171,7 @@ pt_BR:
confirm_email:
click_to_confirm: 'Clique abaixo para confirmar seu endereço de email'
confirm: Confirmar
email_confirmation: 'Confirmação de E-mail'
continue: Continuar
createdisplay:
check_the_form: 'Formulário inválido. Verifique as entradas do formulário.'
@@ -194,45 +207,60 @@ pt_BR:
if_you_did_not: 'Se você não solicitou isso, você pode ignorar este e-mail.'
subject: 'Confirmação de e-mail'
to_confirm: 'Para confirmar a conta, visite este link: '
password_change:
body: "A sua senha foi alterada.\n\nSe você não solicitou essa alteracão, por favor nos contate em {0}"
subject: '{0} password change'
password_reset:
if_you_did: 'Se você solicitou isso, '
please_click: 'clique aqui para redefinir sua senha.'
subject: 'Alteracao de Senha'
we_received_a_request: 'Recebemos um pedido para redefinir sua senha. Se você não solicitou esta alteração, pode ignorar este e-mail.'
suspended:
subject: 'Sua conta foi suspensa'
embed_comments_tab: Comentários
embedlink:
copy: 'Copiar para área de transferência'
copy: 'Copiar conteúdo'
copied: 'Copiado'
error:
ALREADY_EXISTS: 'O recurso já existe'
AUTHENTICATION: Ocorreu um erro durante a autenticação da sua conta
CANNOT_IGNORE_STAFF: 'Não é possível ignorar membros Staff.'
COMMENT_PARENT_NOT_VISIBLE: 'O comentário que você está respondendo foi removido ou não existe.'
COMMENT_TOO_SHORT: 'Seu comentário precisar ter mais de um caracter. Revise seu comentário e envie novamente'
COMMENTING_CLOSED: 'Os comentários já estão fechados.'
COMMENTING_DISABLED: 'Commenting is currently disabled on this site'
confirm_password: 'Suas senhas não coincidem. Por favor, tente novamente.'
DELETION_NOT_SCHEDULED: 'Deletion was not scheduled'
EDIT_USERNAME_NOT_AUTHORIZED: 'Você não tem permissão para revisar seu nome de usuário.'
EDIT_WINDOW_ENDED: 'Você não pode mais editar esse comentário. O tempo expirou.'
email: 'Não é um e-mail válido'
EMAIL_ALREADY_VERIFIED: 'Endereço de e-mail já verificado '
EMAIL_IN_USE: 'Endereço de e-mail já está em uso.'
email_not_verified: 'O endereço de e-mail {0} não é verificado.'
EMAIL_NOT_VERIFIED: 'E-mail não verificado'
email_password: 'Combinação de e-mail e/ou senha incorreta.'
EMAIL_REQUIRED: 'É necessário o endereço de e-mail.'
EMAIL_VERIFICATION_TOKEN_INVALID: 'O token de verificação do email é inválido.'
INCORRECT_PASSWORD: 'Incorrect Password'
INVALID_ASSET_URL: 'O URL do recurso é inválido'
LOGIN_MAXIMUM_EXCEEDED: 'Você fez muitas tentativas de senha mal sucedidas. Espere alguns instantes.'
network_error: 'Falha ao conectar-se ao servidor. Verifique a sua conexão com a internet e tente novamente.'
NO_SPECIAL_CHARACTERS: 'Nomes de usuários podem conter números de letras e _ somente'
NOT_AUTHORIZED: 'Você não está autorizado a executar esta ação.'
NOT_FOUND: 'Recurso não encontrado'
organization_contact_email: 'O Email da organização não é válido.'
organization_name: 'O nome da organização só deve conter letras ou números.'
password: 'A senha deve conter pelo menos 8 caracteres'
PAGE_NOT_AVAILABLE_ROLE: 'Essa página só pode ser acessada para uso do time. Por favor entre em contato com um administrador caso queira fazer parte desse time.'
PASSWORD_INCORRECT: 'A senha inserida está incorreta'
PASSWORD_LENGTH: 'A senha é muito curta'
PASSWORD_REQUIRED: 'Você deve inserir uma senha.'
PASSWORD_RESET_TOKEN_INVALID: 'Seu link de redefinição de senha é inválido.'
PROFANITY_ERROR: 'Os nomes de usuários não devem conter palavrões. Entre em contato com o administrador se você acredita que isso seja incorreto.'
RATE_LIMIT_EXCEEDED: 'Tentativas excedidas'
required_field: 'Esse campo é obrigatório'
SAME_USERNAME_PROVIDED: 'Você deve enviar um nome de usuário diferente.'
temporarily_suspended: 'Sua conta está suspensa. Ela será reativada {0}. Por favor entre em contato caso tenha dúvidas.'
unexpected: 'Ocorreu um erro inesperado. Desculpa!'
username: 'Nomes de usuários podem conter números de letras e _ somente'
USERNAME_IN_USE: 'Nome de usuário já em uso'
@@ -246,6 +274,7 @@ pt_BR:
changed_name:
msg: 'Sua alteração de nome de usuário está sendo analisada por nossa equipe de moderação.'
comment: comentário
comment_is_deleted: 'This commenter has deleted their account.'
comment_is_hidden: 'Este comentário não está disponível.'
comment_is_ignored: 'Este comentário está oculto porque você ignorou esse usuário.'
comment_is_rejected: 'Você rejeitou este comentário.'
@@ -303,6 +332,7 @@ pt_BR:
sign_in_button: 'Entrar'
sign_in_message: 'Entre para interagir com a comunidade.'
password: Senha
reset_password_send_button: 'Recuperar Senha'
request_passowrd: 'Recupere-a clicando aqui.'
team_sign_in: 'Team sign in'
marketing: 'Isso parece um anúncio/marketing'
@@ -316,6 +346,7 @@ pt_BR:
approve: Aprovar
approved: Aprovado
ban_user: Banir
ban_user_actions: 'Banir Usuário'
billion: B
close: Fechar
empty_queue: 'Não há mais comentários para moderar! Aproveite para relaxar e esticar as pernas'
@@ -336,6 +367,7 @@ pt_BR:
notify_edited: '{0} editou o comentário "{1}"'
notify_flagged: '{0} marcou o comentário "{1}"'
notify_rejected: '{0} rejeitou o comentário "{1}"'
notify_reset: '{0} resetar status do comentário "{1}"'
oldest_first: 'Mais velhos primeiro'
premod: pré-moderação
prev_comment: 'Vá para o comentário anterior'
@@ -349,6 +381,8 @@ pt_BR:
show_shortcuts: 'Ver atalhos'
singleview: 'Modo zen'
sort: Ordenar
suspend: 'Suspender Usuário'
system_withheld: 'Sistema de Rejeição'
thismenu: 'Abra este menu'
thousand: k
toggle_search: 'Abrir busca'
@@ -362,7 +396,9 @@ pt_BR:
other: Outro
password_reset:
change_password: 'Alterar senha'
change_password_help: 'Please enter a new password to use to login. Make it secure!'
confirm_new_password: 'Confirmar nova senha'
mail_sent: 'Se você tem uma conta registrada, um link para alterar sua senha foi enviado para o e-mail informado'
new_password: 'Nova senha'
new_password_help: 'A senha deve ter mais de 8 caracteres'
set_new_password: 'Alterar sua senha'
@@ -383,6 +419,13 @@ pt_BR:
username: 'nome de usuário'
write_message: 'Escreve uma mensagem'
yes_suspend: 'Sim suspender'
reject_username_dialog:
cancel: Cancel
description: 'Nos ajude a entender'
message: 'Motivo para reportar (Opcional)'
reason: Motivo
reject_username: 'Rejeitar nome de Usuário'
title: 'Rejeitar nome de Usuário'
reply: Responder
report: Denunciar
report_notif: 'Obrigado por denunciar este comentário. Nossa equipe de moderação foi notificada e irá revisá-la em breve.'
@@ -398,7 +441,7 @@ pt_BR:
user_no_comment: 'Você nunca deixou um comentário. Participe da conversa!'
step_1_header: 'Relatar um problema'
step_2_header: 'Ajude-nos a entender'
step_3_header: 'Obrigdo por sua contribuição'
step_3_header: 'Obrigado por sua contribuição'
stream:
all_comments: 'Todos os comentários'
comment_not_found: 'Este comentário foi removido ou não existe.'
@@ -406,16 +449,16 @@ pt_BR:
no_comments_and_closed: 'Não houve comentários sobre este artigo.'
temporarily_suspended: 'De acordo com as diretrizes da comunidade de {0}, sua conta foi temporariamente suspensa. Por favor, volte para a conversa {1}.'
streams:
all: Todos
all: Todas
article: História
closed: Fechado
closed: Fechada
empty_result: 'Nenhum resultado encontrado para esta pesquisa. Que tal refazer a pesquisa com termos diferentes?'
filter_streams: 'Filtra as listas'
most_recent_stories: 'Matérias mais recentes'
newest: 'O mais novo'
no_results: 'No results'
no_results: 'Nenhum resultado'
oldest: 'O mais velho'
open: Aberto
open: Aberta
pubdate: 'Data de publicação'
search: Pesquisar
search_results: 'Pesquisar Resultados'
@@ -442,11 +485,36 @@ pt_BR:
bio_flags: 'Marcadas para este perfil'
user_bio: 'Perfil do usuário'
username_flags: 'Marcadas para este usuário'
user_detail:
all: Todo
ban: 'Banir usuário'
banned: Banido
email: Email
id: ID
karma: Karma
karma_docs_link: 'https://docs.coralproject.net/talk/trust/#user-karma-score'
learn_more: 'Saiba Mais'
member_since: 'Membro desde'
reject_rate: 'Taxa de rejeição'
reject_username: 'Rejeitar Usuário'
rejected: Rejeitado
remove_ban: 'Remover Bloqueio'
remove_suspension: 'Remover Suspensão'
suspend: 'Suspender Ususário'
suspended: Suspenso
total_comments: 'Total de comentários'
unreliable: Não confiável
user_history: 'Histórico do usuário'
user_karma_score: 'Nota Usuário do Karma'
username: Usuário
username_needs_approval: 'Usuário precisa de aprovação'
username_rejected: 'Usuário rejeitado'
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'
validators:
confirm_password: 'Suas senhas não coincidem. Por favor, tente novamente.'
required: 'Esse campo é obrigatório'
verify_email: 'Não é um e-mail válido'
verify_organization_name: 'O nome da organização só deve conter letras ou números.'
verify_password: 'A senha deve conter pelo menos 8 caracteres'
+7 -1
View File
@@ -1,4 +1,5 @@
const { logger } = require('../services/logging');
const { ErrHTTPNotFound } = require('../errors');
const now = require('performance-now');
const log = (req, res, next) => {
@@ -33,7 +34,12 @@ const log = (req, res, next) => {
};
const error = (err, req, res, next) => {
logger.error({ err }, 'http error');
// Don't log out an error for ErrHTTPNotFound as this is already
// logged with the included request logger.
if (!(err instanceof ErrHTTPNotFound)) {
logger.error({ err }, 'http error');
}
next(err);
};
+2
View File
@@ -5,6 +5,7 @@ const { merge } = require('lodash');
const {
BASE_URL,
BASE_ORIGIN,
BASE_PATH,
MOUNT_PATH,
STATIC_URL,
@@ -29,6 +30,7 @@ const TALK_CLIENT_ENV = Object.keys(process.env)
LIVE_URI: WEBSOCKET_LIVE_URI,
STATIC_URL,
STATIC_ORIGIN,
BASE_ORIGIN,
}
);
+6
View File
@@ -163,6 +163,12 @@ Comment.index({
created_at: -1,
});
Comment.index({
asset_id: 1,
parent_id: 1,
created_at: 1,
});
Comment.index(
{
'action_counts.flag': 1,
+5 -4
View File
@@ -1,6 +1,6 @@
{
"name": "talk",
"version": "4.5.1",
"version": "4.6.5",
"description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net",
"main": "app.js",
"private": true,
@@ -14,7 +14,7 @@
"start": "NODE_ENV=production ./bin/cli-serve -j -w",
"prebuild": "npm-run-all clean generate-introspection",
"build": "NODE_ENV=production parallel-webpack --bail",
"lint:yaml": "yamllint locales/*.yml",
"lint:yaml": "yamllint locales/*.y{a,}ml plugins/**/*.y{a,}ml",
"lint:js": "eslint bin/cli* .",
"lint": "npm-run-all lint:*",
"plugins:reconcile": "./bin/cli plugins reconcile",
@@ -126,7 +126,6 @@
"history": "^3.0.0",
"hjson": "^3.1.1",
"hjson-loader": "^1.0.0",
"https-proxy-agent": "^2.2.0",
"immutability-helper": "^2.2.0",
"imports-loader": "^0.7.1",
"inquirer": "^3.2.2",
@@ -171,8 +170,9 @@
"postcss-smart-import": "^0.5.1",
"precss": "1.4.0",
"prop-types": "^15.5.10",
"proxy-agent": "^3.0.1",
"pym.js": "^1.3.2",
"query-string": "^5.0.0",
"querystringify": "^2.0.0",
"react": "^15.4.2",
"react-apollo": "^1.4.12",
"react-broadcast": "^0.6.2",
@@ -209,6 +209,7 @@
"uglifyjs-webpack-plugin": "^1.1.6",
"url-join": "^2.0.2",
"url-loader": "^0.6.0",
"url-parse": "^1.4.3",
"url-search-params": "^0.9.0",
"uuid": "^3.1.0",
"webpack": "^3.10.0",
+1
View File
@@ -10,4 +10,5 @@ export {
getShallowChanges,
createDefaultResponseFragments,
handlePopupAuth,
buildCommentURL,
} from 'coral-framework/utils';
+87 -68
View File
@@ -25,85 +25,104 @@ let enabled = true;
// }
// });
async function checkForSpam(ctx, { asset_id, body }) {
const req = ctx.parent.parent;
const loaders = ctx.loaders;
//If the key validation failed, then we can't run with the client.
if (!enabled) {
debug('not enabled, passing');
return;
}
let spam = false;
try {
const user_ip = get(req, 'ip', false);
if (!user_ip) {
debug('no ip on request');
return;
}
// Get some headers from the request.
const user_agent = req.get('User-Agent');
if (!user_agent || user_agent.length === 0) {
debug('no user agent on request');
return;
}
const referrer = req.get('Referrer');
if (!referrer || referrer.length === 0) {
debug('no referrer on request');
return;
}
// Get the Asset that the comment is being made against.
const asset = await loaders.Assets.getByID.load(asset_id);
if (!asset) {
debug('asset not found for new comment');
return;
}
// Send off the comment to Akismet to check to see what they say.
spam = await client.checkSpam({
user_ip,
user_agent,
referrer,
permalink: asset.url,
comment_type: 'comment',
comment_content: body,
is_test: false,
});
debug(`comment analyzed as ${spam ? 'being' : 'not being'} spam`);
return spam;
} catch (err) {
console.trace(err);
return;
}
}
function handlePositiveSpam(input) {
// Attach reason information for the flag being added.
input.status = 'SYSTEM_WITHHELD';
input.actions =
input.actions && input.actions.length >= 0 ? input.actions : [];
input.actions.push({
action_type: 'FLAG',
user_id: null,
group_id: 'SPAM_COMMENT',
metadata: {},
});
}
module.exports = {
RootMutation: {
createComment: {
async pre(_, { input }, ctx) {
const req = ctx.parent.parent;
const loaders = ctx.loaders;
//If the key validation failed, then we can't run with the client.
if (!enabled) {
debug('not enabled, passing');
return;
editComment: {
pre: async (_, { asset_id, edit: { body }, edit }, ctx) => {
const spam = await checkForSpam(ctx, { asset_id, body });
if (spam) {
// Mark the comment as positive spam.
handlePositiveSpam(edit);
}
let spam = false;
try {
const user_ip = get(req, 'ip', false);
if (!user_ip) {
debug('no ip on request');
return;
},
},
createComment: {
pre: async (_, { input }, ctx) => {
const spam = await checkForSpam(ctx, input);
if (spam) {
if (input.checkSpam) {
throw new ErrSpam();
}
// Get some headers from the request.
const user_agent = req.get('User-Agent');
if (!user_agent || user_agent.length === 0) {
debug('no user agent on request');
return;
}
const referrer = req.get('Referrer');
if (!referrer || referrer.length === 0) {
debug('no referrer on request');
return;
}
// Get the Asset that the comment is being made against.
const asset = await loaders.Assets.getByID.load(input.asset_id);
if (!asset) {
debug('asset not found for new comment');
return;
}
// Send off the comment to Akismet to check to see what they say.
spam = await client.checkSpam({
user_ip,
user_agent,
referrer,
permalink: asset.url,
comment_type: 'comment',
comment_content: input.body,
is_test: false,
});
debug(`comment analyzed as ${spam ? 'being' : 'not being'} spam`);
} catch (err) {
console.trace(err);
return;
// Mark the comment as positive spam.
handlePositiveSpam(input);
}
// Attach scores to metadata.
input.metadata = merge({}, input.metadata || {}, {
akismet: spam,
});
if (spam) {
if (input.checkSpam) {
throw new ErrSpam();
}
// Attach reason information for the flag being added.
input.status = 'SYSTEM_WITHHELD';
input.actions =
input.actions && input.actions.length >= 0 ? input.actions : [];
input.actions.push({
action_type: 'FLAG',
user_id: null,
group_id: 'SPAM_COMMENT',
metadata: {},
});
}
},
},
},
@@ -15,6 +15,7 @@ ar:
or: "أو"
email: "البريد الإلكتروني"
password: "كلمة المرور"
password_error: "يجب أن تكون كلمة المرور 8 أحرف على الأقل."
forgot_your_pass: "نسيت كلمة المرور؟"
need_an_account: "تحتاج الى حساب؟"
register: "تسجيل"
@@ -43,6 +44,15 @@ ar:
username: اسم المستخدم
write_your_username: "عدل اسم المستخدم"
your_username: "يظهر اسم المستخدم في كل تعليق تنشره."
change_password:
change_password: "تغيير كلمة المرور"
passwords_dont_match: "كلمات المرور غير متطابقة"
required_field: "هذه الخانة مطلوبه"
forgot_password: "نسيت كلمة المرور؟"
save: "حفظ"
cancel: "إلغاء"
edit: "تصحيح"
changed_password_msg: "كلمة المرور الخاصة بك تم تغييرها بنجاح"
da:
talk-plugin-auth:
login:
@@ -333,48 +343,48 @@ nl_NL:
pt_BR:
talk-plugin-auth:
login:
email_verify_cta: "Please verify your email address."
request_new_verify_email: "Request another email"
verify_email: "Thank you for creating an account! We sent an email to the address you provided to verify your account."
verify_email2: "You must verify your account before engaging with the community."
not_you: "Not you?"
logged_in_as: "Signed in as"
facebook_sign_in: "Sign in with Facebook"
facebook_sign_up: "Sign up with Facebook"
logout: "Sign out"
sign_in: "Sign in"
sign_in_to_join: "Sign in to join the conversation"
or: "Or"
email: "Email Address"
password: "Password"
forgot_your_pass: "Forgot your password?"
need_an_account: "Need an account?"
register: "Register"
sign_up: "Sign Up"
confirm_password: "Confirm Password"
username: "Username"
already_have_an_account: "Already have an account?"
recover_password: "Recover password"
email_in_use: "Email address already in use"
email_or_username_in_use: "Email address or Username already in use"
required_field: "This field is required"
passwords_dont_match: "Passwords don't match."
special_characters: "Usernames can contain letters, numbers and _ only"
sign_in_to_comment: "Sign in to comment"
check_the_form: "Invalid Form. Please, check the fields"
already_have_an_account: "Já possui uma conta?"
check_the_form: "Formulário inválido. Por favor confira os campos"
confirm_password: "Confirmar senha"
email: "Endereço de email"
email_in_use: "Endereço de email já está em uso"
email_or_username_in_use: "Endereço de email ou nome de usuário já em uso"
email_verify_cta: "Por favor verifique seu endereço de email."
facebook_sign_in: "Entrar com o Facebook"
facebook_sign_up: "Cadastrar com o Facebook"
forgot_your_pass: "Esqueceu sua senha?"
request_new_verify_email: "Solicite outro email"
verify_email: "Obrigado por criar uma conta! Enviamos um email para o endereço de email informado para verificar sua conta."
verify_email2: "Você deve verificar sua conta antes de participar da comunidade."
logged_in_as: "Logado como"
logout: "Sair"
need_an_account: "Precisa de uma conta?"
not_you: "Não é você?"
or: "Ou"
password: "Senha"
passwords_dont_match: "As senhas não conferem."
recover_password: "Recuperar senha"
register: "Registrar"
required_field: "Esse campo é obrigatório"
sign_in: "Entrar"
sign_in_to_comment: "Entre para comentar"
sign_in_to_join: "Entre para participar da conversa"
sign_up: "Inscreva-se"
special_characters: "Nome de usuário contem somente letras, números and _"
username: "Nome de usuário"
set_username_dialog:
check_the_form: "Invalid Form. Please check the fields"
continue: "Continue with the same Facebook username"
error_create: "Error when changing username"
fake_comment_body: "This is an example comment. Readers can share their thoughts and opinions with newsrooms in the comments section."
fake_comment_date: "1 minute ago"
if_you_dont_change_your_name: "If you don't change your username at this step your Facebook display name will appear alongside of all your comments."
required_field: "Required field"
save: Save
special_characters: "Usernames can contain letters numbers and _ only"
username: Username
write_your_username: "Edit your username"
your_username: "Your username appears on every comment you post."
check_the_form: "Formulário inválido. Por favor confira os campos"
continue: "Continue com o mesmo nome de usuário do Facebook"
error_create: "Erro ao trocar o nome de usuário"
fake_comment_body: "Esse é um comentário de exemplo. Leitores podem compartilhar pensamentos e opiniões na seção de comentários."
fake_comment_date: "1 minuto atrás"
if_you_dont_change_your_name: "Se você não mudar seu nome de usuário neste ponto do processo, o nome que está no seu Facebook irá aparecer junto de todos os seus comentários"
required_field: "Campo obrigatório"
save: Salvar
special_characters: "Nome de usuários podem conter somente letras números e _"
username: Nome de usuário
write_your_username: "Edite seu nome de usuário"
your_username: "Seu nome de usuário aparece em cada comentário feito."
zh_CN:
talk-plugin-auth:
login:
@@ -27,7 +27,7 @@ module.exports = passport => {
try {
const { id, provider, displayName } = profile;
user = await UsersService.findOrCreateExternalUser(
user = await UsersService.upsertSocialUser(
req.context,
id,
provider,
@@ -5,7 +5,13 @@ import { t } from 'plugin-api/beta/client/services';
import { Icon } from 'plugin-api/beta/client/components/ui';
export default ({ className = '' }) => (
<div className={cn(styles.tooltip, className)}>
<div
className={cn(
styles.tooltip,
className,
'talk-plugin-featured-comments-tooltip'
)}
>
<Icon name="info_outline" className={styles.icon} />
<h3 className={styles.headline}>
{t('talk-plugin-featured-comments.featured_comments')}:
@@ -62,7 +62,7 @@ es:
talk-plugin-featured-comments:
un_feature: Desmarcar
feature: Remarcar
featured: Remarcado
featured: Remarcados
featured_comments: Comentarios Remarcados
go_to_conversation: Ir al comentario
tooltip_description: Comentarios seleccionados por nuestro equipo que valen la pena ser leidos
@@ -102,19 +102,19 @@ nl_NL:
yes_feature_comment: Ja, reactie uitlichten.
pt_BR:
talk-plugin-featured-comments:
un_feature: Un-Feature
feature: Feature
featured: Featured
featured_comments: Featured Comments
go_to_conversation: Go to conversation
tooltip_description: Comments selected by our team as worth reading
notify_self_featured: 'The comment from {0} is now featured and approved'
notify_featured: '{0} featured and approved comment "{1}"'
notify_unfeatured: '{0} unfeatured comment "{1}"'
feature_comment: Feature comment?
are_you_sure: Are you sure you would like to feature this comment?
cancel: Cancel
yes_feature_comment: Yes, feature comment
un_feature: Desfazer
feature: Destacar
featured: Destacado
featured_comments: Comentários destacados
go_to_conversation: Ir para conversa
tooltip_description: Vale a pena ler os comentários selecionados pelo nosso time
notify_self_featured: 'O comentário do {0} está em destaque e aprovado'
notify_featured: '{0} destacou e aprovou o comentário "{1}"'
notify_unfeatured: '{0} Desmarcar comentário "{1}"'
feature_comment: Destacar comentário?
are_you_sure: Você tem certeza que deseja destacar esse comentário?
cancel: Cancelar
yes_feature_comment: Sim, destacar comentário
zh_CN:
talk-plugin-featured-comments:
un_feature: "取消精选"
@@ -8,17 +8,17 @@ en:
sign_up: "Sign up with Google"
es:
talk-plugin-google-auth:
google_sign_in: "Entrar con Google"
google_sign_up: "Registrarse con Google"
sign_in: "Entrar con Google"
sign_up: "Registrarse con Google"
fr:
talk-plugin-google-auth:
google_sign_in: "Connectez-vous avec Google"
google_sign_up: "Inscrivez-vous avec Google"
sign_in: "Connectez-vous avec Google"
sign_up: "Inscrivez-vous avec Google"
zh_CN:
talk-plugin-google-auth:
google_sign_in: "使用 Google 帐号"
google_sign_up: "使用 Google 帐号"
sign_in: "使用 Google 帐号"
sign_up: "使用 Google 帐号"
zh_TW:
talk-plugin-google-auth:
google_sign_in: "使用 Google 帳號"
google_sign_up: "使用 Google 帳號"
sign_in: "使用 Google 帳號"
sign_up: "使用 Google 帳號"
@@ -26,7 +26,7 @@ module.exports = passport => {
try {
const { id, provider, displayName } = profile;
user = await UsersService.findOrCreateExternalUser(
user = await UsersService.upsertSocialUser(
req.context,
id,
provider,
@@ -1,5 +1,6 @@
ar:
talk-plugin-ignore-user:
blank_info: أنت لا تتجاهل حاليًا أي مستخدم
section_title: المستخدمون الذين تم تجاهلهم
section_info: لأنك تجاهلت المعلقين التاليين، يتم إخفاء تعليقاتهم.
stop_ignoring: إيقاف التجاهل
@@ -51,16 +52,17 @@ de:
es:
talk-plugin-ignore-user:
section_title: "Usuarios ignorados"
section_info: Because you ignored the following commenters, their comments are hidden.
blank_info: Actualmente no ignoras a ningún usuario
section_info: Debido a que ignoró a los siguientes comentaristas, sus comentarios están ocultos.
stop_ignoring: "No ignorar más"
ignore_user: Ignore User
cancel: Cancel
ignore_user: Ignorar usuario
cancel: Cancelar
confirmation: |
When you ignore a user, all comments they wrote on the site will be hidden from you. You can
undo this later from My Profile.
Cuando ignora a un usuario, todos los comentarios que escribió en el sitio estarán ocultos.
Puede deshacer esto más tarde desde Mi Perfil.
notify_success: |
You are now ignoring {0}. You can undo this action from My Profile.
confirmation_title: Ignore {0}?
Ahora estás ignorando a {0}. Puede deshacer esta acción desde Mi Perfil.
confirmation_title: Ignorar a {0}?
fr:
talk-plugin-ignore-user:
section_title: "Utilisateurs ignorés"
@@ -89,17 +91,18 @@ nl_NL:
confirmation_title: Negeer {0}?
pt_BR:
talk-plugin-ignore-user:
blank_info: Atualmente você não está ignorando nenhum usuário
section_title: "Usuários ignorados"
section_info: "Porque você ignorou os seguintes comentadores, seus comentários estão ocultos."
stop_ignoring: "Pare de ignorar"
ignore_user: Ignore User
cancel: Cancel
confirmation: |
When you ignore a user, all comments they wrote on the site will be hidden from you. You can
undo this later from My Profile.
stop_ignoring: "Parar de ignorar"
ignore_user: Ignorar usuário
cancel: Cancelar
confirmation:
Quando você ignora um usuário, todos os comentários que ele ele(a) escreveu não serão exibidos para você. Você pode
desfazer isso depois no Meu Perfil
notify_success: |
You are now ignoring {0}. You can undo this action from My Profile.
confirmation_title: Ignore {0}?
Agora você está ignorando {0}. Você pode desfazer essa ação em Meu Perfil.
confirmation_title: Ignorar {0}?
zh_CN:
talk-plugin-ignore-user:
section_title: "被忽略用户"
@@ -1,3 +1,79 @@
ar:
email:
email_change_original:
subject: تغيير البريد الإلكتروني
body: تم تغيير عنوان بريدك الإلكتروني من {0} إلى {1}. إذا لم تطلب هذا التغيير ، فيرجى الاتصال {2}.
error:
NO_LOCAL_PROFILE: لا يوجد عنوان بريد إلكتروني حالي مقترن بهذا الحساب.
LOCAL_PROFILE: هناك بريد إلكتروني مرتبط بهذا الحساب.
INCORRECT_PASSWORD: كلمة المرور المقدمة غير صحيحة.
talk-plugin-local-auth:
change_password:
change_password: "تغيير كلمة المرور"
passwords_dont_match: "كلمات المرور لا تتطابق"
required_field: "هذه الخانة مطلوبه"
forgot_password: "نسيت كلمة المرور؟"
old_password: "كلمة المرور القديمة"
new_password: "كلمة المرور الجديدة"
confirm_new_password: "تأكيد كلمة المرور الجديدة"
save: "حفظ"
cancel: "إلغاء"
edit: "تعديل"
changed_password_msg: "تغيير كلمة المرور - تم تغيير كلمة المرور الخاصة بك بنجاح"
forgot_password_sent: "نسيت كلمة المرور - لقد أرسلنا إليك رسالة بريد إلكتروني لاسترداد كلمة المرور الخاصة بك"
change_username:
change_username_note: "لا يمكن تغيير أسماء المستخدمين إلا مرة واحدة كل 14 يومًا."
is_not_eligible: "لا يمكنك حاليا تغيير اسم المستخدم الخاص بك."
save: "حفظ"
edit_profile: "تعديل الملف الشخصي"
cancel: "إلغاء"
confirm_username_change: "تأكيد تغيير اسم المستخدم"
description: "أنت تحاول تغيير اسم المستخدم الخاص بك. سوف يظهر اسم المستخدم الجديد الخاص بك على جميع تعليقاتك الماضية والتعليقات المستقبلية."
old_username: "اسم المستخدم القديم"
new_username: "اسم المستخدم الجديد"
re_enter: "أعد إدخال اسم مستخدم جديد"
bottom_note: "ملاحظة: لن تتمكن من تغيير اسم المستخدم الخاص بك مرة أخرى لمدة 14 يومًا"
confirm_changes: "تأكيد التغييرات"
username_does_not_match: "اسم المستخدم غير متطابق"
cant_be_equal: "يجب أن يكون {0} الجديد الخاص بك مختلفًا عن الحالي"
changed_username_success_msg: "اسم المستخدم تم تغييره - تم تغيير اسم المستخدم الخاص بك بنجاح. لن تتمكن من تغيير اسم المستخدم الخاص بك لمدة 14 يومًا."
change_username_attempt: "لا يمكن تحديث اسم المستخدم. لا يمكن تغيير أسماء المستخدمين إلا كل 14 يومًا."
change_email:
confirm_email_change: "تأكيد تغيير عنوان البريد الإلكتروني"
description: "أنت تحاول تغيير عنوان بريدك الإلكتروني. سيتم استخدام عنوان بريدك الإلكتروني الجديد لتسجيل الدخول ولتلقي إشعارات الحساب."
old_email: "عنوان البريد الإلكتروني القديم"
new_email: "عنوان البريد الإلكتروني الجديد"
enter_password: "أدخل كلمة المرور"
incorrect_password: "كلمة مرور خاطئة"
confirm_change: "تأكيد التغيير"
cancel: "إلغاء"
change_email_msg: "تم تغيير عنوان البريد الإلكتروني. سيتم استخدام عنوان البريد الإلكتروني هذا الآن لتسجيل الدخول ولإشعارات البريد الإلكتروني."
add_email:
add_email_address: "إضافة البريد الإلكتروني"
enter_email_address: "أدخل البريد الالكتروني:"
invalid_email_address: "البريد الإلكتروني غير صالح"
confirm_email_address: "أكد عنوان بريدك الإلكتروني:"
email_does_not_match: "عنوان البريد الإلكتروني غير مطابق"
insert_password: "إدخال كلمة المرور:"
confirm_password: "تأكيد كلمة المرور:"
required_field: "هذه الخانة مطلوبه"
done: "تم"
content:
title: "أضف عنوان بريد إلكتروني"
description: "لمزيد من الأمان ، نطلب من المستخدمين إضافة عنوان بريد إلكتروني إلى حساباتهم. سيتم استخدام عنوان بريدك الإلكتروني في:"
item_1: "تلقي التحديثات المتعلقة بأي تغييرات في حسابك (عنوان البريد الإلكتروني ، اسم المستخدم ، كلمة المرور ، إلخ.)"
item_2: "السماح لك بتنزيل تعليقاتك."
item_3: "إرسال إشعارات التعليقات التي اخترت استلامها."
verify:
title: "تحقق من عنوان البريد الإلكتروني الخاص بك"
description: "لقد أرسلنا رسالة إلكترونية إلى {0} لإثبات ملكية حسابك. يجب عليك التحقق من عنوان بريدك الإلكتروني حتى يمكن استخدامه لتأكيد تعديلات الحساب والإشعارات."
added:
title: "تمت إضافة عنوان البريد الالكتروني"
description: "تمت إضافة عنوان بريدك الإلكتروني إلى حسابك."
subtitle: "هل تحتاج إلى تغيير عنوان بريدك الإلكتروني؟"
description_2: "يمكنك تغيير إعدادات حسابك من خلال زيارة"
path: "ملفي > الإعدادات"
alert: "تمت إضافة البريد الإلكتروني!"
en:
email:
email_change_original:
@@ -74,6 +150,82 @@ en:
description_2: "You can change your account settings by visiting"
path: "My Profile > Settings"
alert: "Email Added!"
pt_BR:
email:
email_change_original:
subject: Mudança de email
body: Seu endereço de email foi alterado de {0} para {1}. Se você não solicitou essa alteração, por favor contate {2}.
error:
NO_LOCAL_PROFILE: Nenhum email existente está vinculado a essa conta.
LOCAL_PROFILE: Já existe um endereço de email associado a essa conta.
INCORRECT_PASSWORD: A senha informada está incorreta.
talk-plugin-local-auth:
change_password:
change_password: "Mudar senha"
passwords_dont_match: "Senhas não conferem"
required_field: "Esse campo é obrigatório"
forgot_password: "Esqueceu sua senha?"
old_password: "Senha antiga"
new_password: "Nova senha"
confirm_new_password: "Confirme a nova senha"
save: "Salvar"
cancel: "Cancelar"
edit: "Editar"
changed_password_msg: "Senha alterada - Sua senha foi alterada com sucesso"
forgot_password_sent: "Esqueceu a senha - Nós enviamos um email para recuperação da senha"
change_username:
change_username_note: "Nomes de usuários só podem ser alterados uma vez a cada 14 dias"
is_not_eligible: "Seu nome de usuário não pode ser alterado."
save: "Salvar"
edit_profile: "Editar perfil"
cancel: "Cancelar"
confirm_username_change: "Confirmar alteração do nome de usuário"
description: "Você está tentando alterar seu nome de usuário. Seu novo nome de usuário irá aparecer em todos os comentários antigos e novos."
old_username: "Nome de usuário antigo"
new_username: "Nome de usuário novo"
re_enter: "Repita o novo usuário"
bottom_note: "Atenção: Não será possível alterar seu nome de usuário por 14 dias"
confirm_changes: "Confirmar mudanças"
username_does_not_match: "Nome de usuário não confere"
cant_be_equal: "Seu novo {0} deve ser diferente do atual"
changed_username_success_msg: "Nome de usuário alterado - Nome de usuário alterado com sucesso. Não será possível alterar por 14 dias."
change_username_attempt: "Usuário não pode ser alterado. Nome de usuário só pode ser alterado a cada 14 dias."
change_email:
confirm_email_change: "Confirmar alteração de email"
description: "Você está tentando alterar seu endereço de email. Seu novo email será usado para entrar na plataforma e receber avisos da sua conta."
old_email: "Endereço de email antigo"
new_email: "Endereço de email novo"
enter_password: "Digite a senha"
incorrect_password: "Senha incorreta"
confirm_change: "Confirmar alteração"
cancel: "Cancelar"
change_email_msg: "Endereço de email alterado. este endereço de email será usado para entrar e receber notificações de email."
add_email:
add_email_address: "Adicionar endereço de email"
enter_email_address: "Entre com o endereço de email:"
invalid_email_address: "Email inválido"
confirm_email_address: "Confirmar endereço de email:"
email_does_not_match: "Endereço de email não confere"
insert_password: "Insira a senha:"
confirm_password: "Confirmar senha:"
required_field: "Esse campo é obrigatório"
done: "Feito"
content:
title: "Adicione um endereço de email"
description: "Para a sua segurança, exigimos que os usuários adicionem um endereço de email para suas contas. Seu email será usado para:"
item_1: "Receba avisos de alterações na sua conta(endereço de email, usuário, senha, etc.)"
item_2: "Permitir que você baixe seus comentários."
item_3: "Envie notificações de comentários que você escolheu receber."
verify:
title: "Verifique seu endereço de email"
description: "Enviamos um email para {0} para verificar sua conta. Ele pode ser usado para confirmar alterações na conta e notificações."
added:
title: "Endereço de email adicionado"
description: "Seu endereço de email foi adicionado na sua conta."
subtitle: "Precisa alterar seu endereço de email?"
description_2: "Você pode alterar as configurações da sua conta em"
path: "Meu Perfil > Configurações"
alert: "Email adicionado!"
de:
email:
email_change_original:
@@ -151,6 +303,14 @@ de:
alert: "E-Mail-Adresse hinzugefügt!"
es:
talk-plugin-local-auth:
email:
email_change_original:
subject: Cambio de correo electrónico
body: Su dirección de correo electrónico ha cambiado de {0} a {1}. Si no solicitó este cambio, póngase en contacto con {2}.
error:
NO_LOCAL_PROFILE: No hay una dirección de correo electrónico asociada a esta cuenta.
LOCAL_PROFILE: Una dirección de correo electrónico ya está asociada a esta cuenta.
INCORRECT_PASSWORD: La contraseña dada fue incorrecta.
change_password:
change_password: "Cambiar Contraseña"
passwords_dont_match: "Las contraseñas no coinciden"
@@ -176,3 +336,38 @@ es:
username_does_not_match: "El usuario no coincide"
changed_username_success_msg: "Usuario Actualizado - Tu usuario ha sido exitosamente actualizado. No podrás cambiar el usuario por 14 días."
change_username_attempt: "El usuario no puede ser actualizado. Los usuarios pueden ser cambiados cada 14 días."
change_email:
confirm_email_change: "Confirmar cambio de dirección de correo electrónico"
description: "Está intentando cambiar su dirección de correo electrónico. Su nueva dirección de correo electrónico se usará para iniciar sesión y recibir notificaciones de la cuenta."
new_email: "Nueva dirección de correo electrónico"
enter_password: "Introducir la contraseña"
incorrect_password: "Contraseña incorrecta"
confirm_change: "Confirmar cambio"
cancel: "Cancelar"
change_email_msg: "Dirección de correo electrónico modificada. Esta dirección de correo electrónico ahora se utilizará para iniciar sesión y notificaciones por correo electrónico."
add_email:
add_email_address: "Agregar dirección de correo electrónico"
enter_email_address: "Introducir la dirección de correo electrónico:"
invalid_email_address: "Dirección de correo electrónico no válida"
confirm_email_address: "Confirmar el correo:"
email_does_not_match: "La dirección de Email no coincide"
insert_password: "Insertar contraseña:"
confirm_password: "Confirmar contraseña:"
required_field: "Este campo es requerido"
done: "Hecho"
content:
title: "Agregar una dirección de correo electrónico"
description: "Para su seguridad adicional, solicitamos a los usuarios que agreguen una dirección de correo electrónico a sus cuentas. Su dirección de correo electrónico se usará para:"
item_1: "Recibe actualizaciones sobre cualquier cambio en tu cuenta (dirección de correo electrónico, nombre de usuario, contraseña, etc.)"
item_2: "Permitir que descargues tus comentarios."
item_3: "Envía notificaciones de comentarios que hayas elegido recibir."
verify:
title: "Verificar su dirección de correo electrónico"
description: "Hemos enviado un correo electrónico a {0} para verificar su cuenta. Debe verificar su dirección de correo electrónico para que se pueda usar para notificaciones y notificaciones de cambio de cuenta."
added:
title: "Dirección de correo electrónico agregada"
description: "Su dirección de correo electrónico ha sido agregada a su cuenta."
subtitle: "¿Necesita cambiar su dirección de correo electrónico?"
description_2: "Puedes cambiar la configuración de tu cuenta visitando"
path: "Mi perfil > Configuración"
alert: "¡Correo electrónico agregado!"
@@ -21,7 +21,7 @@ nl_NL:
member_since: "Gebruiker sinds"
pt_BR:
talk-plugin-member-since:
member_since: "Member Since"
member_since: "Membro desde"
zh_CN:
talk-plugin-member-since:
member_since: "成员加入日期"
@@ -84,16 +84,16 @@ nl_NL:
ban_user_dialog_headline: "Gebruiker verbannen?"
pt_BR:
talk-plugin-moderation-actions:
reject_comment: "Reject"
approve_comment: "Approve"
approved_comment: "Approved"
moderation_actions: "Moderation Actions"
ban_user: "Ban User"
ban_user_dialog_sub: "Are you sure you would like to ban this user?"
ban_user_dialog_copy: "Note: Banning this user will also place this comment in the Rejected queue."
ban_user_dialog_cancel: "Cancel"
ban_user_dialog_yes: "Yes. Ban user"
ban_user_dialog_headline: "Ban User?"
reject_comment: "Rejeitar"
approve_comment: "Aprovar"
approved_comment: "Aprovado"
moderation_actions: "Ações"
ban_user: "Banir Usuário"
ban_user_dialog_sub: "Tem certeza que deseja banir esse usuário?"
ban_user_dialog_copy: "Nota: Banindo esse usuário também irá colocar esse comentário na fila de Rejeitado."
ban_user_dialog_cancel: "Cancelar"
ban_user_dialog_yes: "Sim. Banir usuário"
ban_user_dialog_headline: "Banir usuário?"
zh_CN:
talk-plugin-moderation-actions:
reject_comment: "拒绝"
@@ -1,6 +1,12 @@
ar:
talk-plugin-notifications-category-featured:
toggle_description: تعليقي تم تمييزه
en:
talk-plugin-notifications-category-featured:
toggle_description: My comment is featured
es:
talk-plugin-notifications-category-featured:
toggle_description: Mi comentario esta presentado
de:
talk-plugin-notifications-category-featured:
toggle_description: Mein Kommentar wird empfohlen
@@ -1,9 +1,22 @@
ar:
talk-plugin-notifications:
categories:
featured:
subject: "تميَزت واحدة من تعليقاتك على {0}"
body: "{0}\n
حدد أحد أعضاء فريقنا هذا التعليق ليتم عرضه كتعليق مميز للقراء الآخرين: {1}"
en:
talk-plugin-notifications:
categories:
featured:
subject: "One of your comments was featured on {0}"
body: "{0}\nA member of our team has selected this comment to be featured for other readers: {1}"
es:
talk-plugin-notifications:
categories:
featured:
subject: "Uno de tus comentarios apareció en {0}"
body: "{0}\nUn miembro de nuestro equipo ha seleccionado este comentario para ser presentado por otros lectores: {1}"
de:
talk-plugin-notifications:
categories:
@@ -1,6 +1,12 @@
ar:
talk-plugin-notifications-category-reply:
toggle_description: يتلقى تعليقي ردا
en:
talk-plugin-notifications-category-reply:
toggle_description: My comment receives a reply
es:
talk-plugin-notifications-category-reply:
toggle_description: Mi comentario recibe una respuesta
de:
talk-plugin-notifications-category-reply:
toggle_description: Jemand antwortet auf meinen Kommentar
@@ -1,9 +1,22 @@
ar:
talk-plugin-notifications:
categories:
reply:
subject: "رد شخص ما على تعليقك على {0}"
body: "{0}\n{1} رد على تعليقك
{2}"
en:
talk-plugin-notifications:
categories:
reply:
subject: "Someone has replied to your comment on {0}"
body: "{0}\n{1} replied to your comment: {2}"
es:
talk-plugin-notifications:
categories:
reply:
subject: "Alguien ha respondido a tu comentario en {0}"
body: "{0}\n{1} respondió a tu comentario: {2}"
de:
talk-plugin-notifications:
categories:
@@ -1,6 +1,12 @@
ar:
talk-plugin-notifications-category-staff:
toggle_description: يرد أحد الموظفين على تعليقي
en:
talk-plugin-notifications-category-staff:
toggle_description: A staff member replies to my comment
es:
talk-plugin-notifications-category-staff:
toggle_description: Un miembro del personal responde a mi comentario
de:
talk-plugin-notifications-category-staff:
toggle_description: Ein Redaktionsmitglied antwortet auf meinen Kommentar
@@ -1,9 +1,22 @@
ar:
talk-plugin-notifications:
categories:
staff:
subject: "شخص ما في {0} قد رد على تعليقك"
body: "{0}\n{1} يعمل ل
{2} وقد رد على تعليقك: {3}"
en:
talk-plugin-notifications:
categories:
staff:
subject: "Someone at {0} has replied to your comment"
body: "{0}\n{1} works for {2} and has replied to your comment: {3}"
es:
talk-plugin-notifications:
categories:
staff:
subject: "Alguien en {0} ha respondido a tu comentario"
body: "{0}\n{1} trabaja para {2} y ha respondido a tu comentario: {3}"
de:
talk-plugin-notifications:
categories:
@@ -1,7 +1,15 @@
ar:
talk-plugin-notifications:
digest_enum:
DAILY: في ملخص يومي
en:
talk-plugin-notifications:
digest_enum:
DAILY: In a daily digest
es:
talk-plugin-notifications:
digest_enum:
DAILY: En un resumen diario
de:
talk-plugin-notifications:
digest_enum:
@@ -1,7 +1,15 @@
ar:
talk-plugin-notifications:
digest_enum:
HOURLY: في ملخص كل ساعة
en:
talk-plugin-notifications:
digest_enum:
HOURLY: In an hourly digest
es:
talk-plugin-notifications:
digest_enum:
HOURLY: En un resumen por hora
de:
talk-plugin-notifications:
digest_enum:
@@ -1,3 +1,21 @@
ar:
talk-plugin-notifications:
settings_title: إشعارات
settings_subtitle: تلقي الإشعارات متى
turn_off_all: لا أريد تلقي الإشعارات
banner_info:
title: التحقق من البريد الإلكتروني مطلوب
text: لتلقي إشعارات البريد الإلكتروني ، يجب أن يكون لديك عنوان بريد إلكتروني تم التحقق منه.
verify_now: تحقق من بريدك الالكتروني الآن
banner_success:
title: تم إرسال التحقق عبر البريد الإلكتروني
text: تم إرسال رسالة إلكترونية إلى {0} يحتوي على رابط التحقق.
banner_error:
title: خطأ
text: حدث خطأ في إرسال رسالة التحقق الخاصة بك. الرجاء معاودة المحاولة في وقت لاحق.
digest_option: إرسال الإشعارات
digest_enum:
NONE: فورا
en:
talk-plugin-notifications:
settings_title: Notifications
@@ -16,6 +34,24 @@ en:
digest_option: Send notifications
digest_enum:
NONE: Immediately
es:
talk-plugin-notifications:
settings_title: Notificaciones
settings_subtitle: Reciba notificaciones cuando
turn_off_all: No quiero recibir notificaciones
banner_info:
title: Se requiere verificación de correo electrónico
text: Para recibir notificaciones por correo electrónico, debe tener una dirección de correo electrónico verificada.
verify_now: Verifica tu correo electrónico ahora.
banner_success:
title: Verificación del correo electrónico enviada
text: Se envió un correo electrónico a {0} que contiene un enlace de verificación.
banner_error:
title: Error
text: Ha habido un error al enviar su correo electrónico de verificación. Por favor, inténtelo de nuevo más tarde.
digest_option: Enviar notificaciones
digest_enum:
NONE: Inmediatamente
de:
talk-plugin-notifications:
settings_title: Benachrichtigungen
@@ -1,3 +1,17 @@
ar:
talk-plugin-notifications:
templates:
digest:
subject: "نشاطك الأخير للتعليق على {0}"
footer: "لقد تلقيت هذا الإشعار نظرًا لأنك معلق على {0} وتم تمكين تلقي الإشعارات."
links:
unsubscribe: "إلغاء الاشتراك في إشعارات التعليقات"
unsubscribe_page:
unsubscribe: "إلغاء الاشتراك في إشعارات التعليقات"
click_to_confirm: "انقر أدناه لتأكيد رغبتك في إلغاء الاشتراك من جميع الإشعارات"
confirm: "أكد"
are_unsubscribed: "أنت الآن غير مشترك في جميع الإشعارات."
token_invalid: "رابط إلغاء الاشتراك غير صالح ، انقر الرابط من بريد إلكتروني أحدث أو قم بزيارة جدول تعليقات وتسجيل الدخول لتغيير تفضيلات الإشعارات الخاصة بك"
en:
talk-plugin-notifications:
templates:
@@ -2,6 +2,7 @@ import React from 'react';
import cn from 'classnames';
import styles from './styles.css';
import { t } from 'plugin-api/beta/client/services';
import { buildCommentURL } from 'plugin-api/beta/client/utils';
import { ClickOutside } from 'plugin-api/beta/client/components';
import { Icon, Button } from 'plugin-api/beta/client/components/ui';
@@ -90,7 +91,7 @@ export default class PermalinkButton extends React.Component {
className={cn(styles.input, `${name}-copy-field`)}
type="text"
ref={input => (this.permalinkInput = input)}
defaultValue={`${asset.url}?commentId=${comment.id}`}
defaultValue={buildCommentURL(asset.url, comment.id)}
readOnly
/>
@@ -105,9 +106,9 @@ export default class PermalinkButton extends React.Component {
},
])}
>
{!copyFailure && !copySuccessful && 'Copy'}
{copySuccessful && 'Copied'}
{copyFailure && 'Not supported'}
{!copyFailure && !copySuccessful && t('common.copy')}
{copySuccessful && t('common.copied')}
{copyFailure && t('common.notsupported')}
</Button>
</div>
</div>
@@ -3,6 +3,8 @@ import { gql } from 'react-apollo';
import moment from 'moment';
import update from 'immutability-helper';
import { scheduledDeletionDelayHours } from '../../config';
export const withRequestDownloadLink = withMutation(
gql`
mutation DownloadCommentHistory {
@@ -48,7 +50,7 @@ export const withRequestAccountDeletion = withMutation(
});
const scheduledDeletionDate = moment()
.add(24, 'hours')
.add(scheduledDeletionDelayHours, 'hours')
.toDate();
const data = update(prev, {
@@ -1,3 +1,54 @@
ar:
download_request:
section_title: "قم بتنزيل سجل التعليقات الخاص بي"
you_will_get_a_copy: "ستتلقى رسالة إلكترونية تحتوي على رابط لتنزيل سجل التعليقات. تستطيع طلب"
download_rate: "طلب تنزيل واحد كل {0} يوم"
most_recent_request: "طلبك الأخير"
request: "طلب سجل التعليقات"
rate_limit: "يمكنك تقديم طلب سجل تعليقات آخر في {0}"
hours: "{0} ساعات"
days: "{0} أيام"
hour: "{0} ساعة"
day: "{0} يوم"
download_preparing: "جارٍ التحضير لتنزيل الحساب - تحقق من بريدك الإلكتروني قريبًا للحصول على رابط تنزيل"
delete_request:
account_deletion_cancelled: 'طلب حذف الحساب تم إلغاؤه - لقد تم إلغاء طلبك لحذف حسابك.'
account_deletion_requested: 'تم طلب حذف الحساب'
received_on: "تم تلقي طلب لحذف حسابك في "
cancel_request_description: "إذا كنت ترغب في إعادة تفعيل حسابك ، فيمكنك إلغاء طلبك لحذف حسابك أدناه"
before: "قبل"
cancel_account_deletion_request: "إلغاء طلب حذف الحساب"
delete_my_account: "احذف حسابي"
delete_my_account_description: "سيؤدي حذف حسابك إلى محو ملفك الشخصي نهائيًا وإزالة جميع تعليقاتك من هذا الموقع."
already_submitted_request_description: "لقد أرسلت طلبًا لحذف حسابك. سيتم حذف حسابك بعد {0}. يمكنك إلغاء الطلب حتى ذلك الوقت"
your_request_submitted_description: "تم إرسال طلبك وتم إرسال التأكيد إلى عنوان البريد الإلكتروني المرتبط بحسابك."
your_account_deletion_scheduled: "من المقرر أن يتم حذف حسابك بعد:"
changed_your_mind: "غيرت رأيك؟"
simply_go_to: "ما عليك سوى الانتقال إلى حسابك مرة أخرى قبل هذا الوقت والنقر"
tell_us_why: "أخبرنا لماذا"
feedback_copy: "نود أن نعرف لماذا اخترت حذف حسابك. أرسل لنا ردود الفعل على نظام التعليق لدينا عن طريق البريد الإلكتروني"
done: "تم"
cancel: "إلغاء"
proceed: "تقدم"
input_is_not_correct: "المدخلات غير صحيحة"
step_0:
you_are_attempting: "أنت تحاول حذف حسابك. هذا يعنى:"
item_1: "تم إزالة جميع تعليقاتك من هذا الموقع"
item_2: "تم حذف جميع تعليقاتك من قاعدة البيانات الخاصة بنا"
item_3: "تم إزالة اسم المستخدم وعنوان البريد الإلكتروني من نظامنا"
step_1:
subtitle: "متى سيتم حذف حسابي؟"
description: "سيتم حذف حسابك بعد {0} ساعات من تقديم طلبك."
subtitle_2: "هل ما زال بإمكاني كتابة تعليقات حتى يتم حذف حسابي؟"
description_2: "نعم ، لا يزال بإمكانك التعليق والرد على التعليقات والتفاعل عليها حتى تنتهي صلاحية {0} ساعات."
step_2:
description: "قبل حذف حسابك، نوصيك بتنزيل سجل التعليقات الخاص بسجلاتك. بعد حذف حسابك، لن تتمكن من طلب سجل التعليقات الخاص بك."
to_download: "لتنزيل سجل التعليقات، انتقل إلى:"
path: "ملفي > تنزيل سجل تعليقاتي"
step_3:
subtitle: "هل انت متأكد انك تريد حذف حسابك؟"
description: "للتأكيد على رغبتك في حذف حسابك، يرجى كتابة العبارة التالية في مربع النص أدناه:"
type_to_confirm: "اكتب عبارة أدناه للتأكيد"
en:
download_request:
section_title: "Download My Comment History"
@@ -49,6 +100,57 @@ en:
subtitle: "Are you sure you want to delete your account?"
description: "To confirm you would like to delete your account please type in the following phrase into the text box below:"
type_to_confirm: "Type phrase below to confirm"
es:
download_request:
section_title: "Descargar el historial de mis comentarios"
you_will_get_a_copy: "Recibirás un correo electrónico con un enlace para descargar tu historial de comentarios. Puedes hacer"
download_rate: "una solicitud de descarga cada {0} días"
most_recent_request: "Tu solicitud más reciente"
request: "Solicitar historial de comentarios"
rate_limit: "Puedes enviar otra solicitud de historial de comentarios en {0}"
hours: "{0} horas"
days: "{0} dias"
hour: "{0} hora"
day: "{0} día"
download_preparing: "Preparando para la descarga de la cuenta - Revises tu correo electrónico en breve para obtener un enlace de descarga"
delete_request:
account_deletion_cancelled: 'Solicitud de eliminación de cuenta cancelada - Tu solicitud para eliminar tu cuenta ha sido cancelada.'
account_deletion_requested: 'Eliminación de cuenta solicitada'
received_on: "Se recibió una solicitud para eliminar tu cuenta en "
cancel_request_description: "Si deseas reactivar tu cuenta, puedes cancelar tu solicitud para eliminar tu cuenta a continuación"
before: "antes"
cancel_account_deletion_request: "Cancelar solicitud de eliminación de cuenta"
delete_my_account: "Borrar mi cuenta"
delete_my_account_description: "Al eliminar tu cuenta, se borrará definitivamente tu perfil y se eliminarán todos tus comentarios de este sitio."
already_submitted_request_description: "Ya ha enviado una solicitud para eliminar tu cuenta. Tu cuenta será eliminada después de {0}. Puedes cancelar la solicitud hasta ese momento."
your_request_submitted_description: "Tu solicitud ha sido enviada y la confirmación ha sido enviada a la dirección de correo electrónico asociada con tu cuenta."
your_account_deletion_scheduled: "Tu cuenta está programada para ser eliminada después de:"
changed_your_mind: "¿Has cambiado de opinión?"
simply_go_to: "Simplemente acceda a su cuenta nuevamente antes de esta hora y haga clic en"
tell_us_why: "Dinos por qué"
feedback_copy: "Nos gustaría saber por qué eligió eliminar tu cuenta. Envíenos tus comentarios sobre nuestro sistema de comentarios por correo electrónico"
done: "Hecho"
cancel: "Cancelar"
proceed: "Proceder"
input_is_not_correct: "La entrada no es correcta"
step_0:
you_are_attempting: "Estás intentando eliminar tu cuenta. Esto significa:"
item_1: "Todos tus comentarios son eliminados de este sitio"
item_2: "Todos tus comentarios son eliminados de nuestra base de datos"
item_3: "Tu nombre de usuario y dirección de correo electrónico se eliminan de nuestro sistema"
step_1:
subtitle: "¿Cuándo se eliminará mi cuenta?"
description: "Tu cuenta se eliminará {0} horas después de que se hayas enviado su solicitud."
subtitle_2: "¿Todavía puedo escribir comentarios hasta que mi cuenta se elimine?"
description_2: "Sí, todavía puedes comentar, responder y reaccionar a los comentarios hasta que expire {0} horas."
step_2:
description: "Antes de que se elimine tu cuenta, te recomendamos que descargues su historial de comentarios para tus registros. Después de que se elimine su cuenta, no podrás solicitar su historial de comentarios."
to_download: "Para descargar tu historial de comentarios, ve a:"
path: "Mi perfil > Descargar historial de mis comentarios"
step_3:
subtitle: "¿Seguro que quieres eliminar tu cuenta?"
description: "Para confirmar que deseas eliminar tu cuenta, escriba la siguiente frase en el cuadro de texto a continuación:"
type_to_confirm: "Escriba la frase a continuación para confirmar"
de:
download_request:
section_title: "Mein Kommentar-Archiv herunterladen"
@@ -100,3 +202,54 @@ de:
subtitle: "Sind Sie sicher, dass Sie Ihr Benutzerkonto löschen möchten?"
description: "Um zu bestätigen, dass Sie Ihr Konto löschen möchten, geben Sie bitte folgende Zeichen in das Textfeld ein:"
type_to_confirm: "Zur Bestätigung Zeichenfolge eingeben"
pt_BR:
download_request:
section_title: "Baixar meu histórico de comentários"
you_will_get_a_copy: "Você receberá um email com um link para baixar seu histórico de comentários. Você pode fazer"
download_rate: "uma requisição de Download a cada {0} dias"
most_recent_request: "Suas solicitações mais recentes"
request: "Solicitar histórico de comentários"
rate_limit: "Você pode solicitar outro histórico de comentários em {0}"
hours: "{0} horas"
days: "{0} dias"
hour: "{0} hora"
day: "{0} dia"
download_preparing: "Preparando Download - Cheque seu email para acessar um link de download"
delete_request:
account_deletion_cancelled: 'Solicitação de exclusão de conta cancelada - Sua solicitação de exclusão cancelada.'
account_deletion_requested: 'Exclusão de conta solicitada'
received_on: "Uma solicitação para deletar sua conta foi recebida "
cancel_request_description: "Se você deseja reativar sua conta, você pode cancelar a solicitação de exclusão abaixo"
before: "antes"
cancel_account_deletion_request: "Cancelar requisiçao de exclusão da conta"
delete_my_account: "Deletar minha conta"
delete_my_account_description: "Deletando sua conta seu perfil será excluído permanentemente e todos os seus comentários serão removidos desse site."
already_submitted_request_description: "Você já enviou uma solicitação de exclusão da conta. Sua conta será deletada depois de {0}. Você pode cancelar até a data informada"
your_request_submitted_description: "Sua solicitação foi enviada e uma confirmação foi enviada para o endereço de email associado a sua conta."
your_account_deletion_scheduled: "Sua conta está programada para ser deletada depois de:"
changed_your_mind: "Mudou de ideia?"
simply_go_to: "Simplesmente volte para sua conta novamente e clique"
tell_us_why: "Nos diga porque"
feedback_copy: "Gostariámos de saber porque você deseja excluir sua conta. Nos envie um feedback através do nosso sistema de comentário"
done: "Feito"
cancel: "Cancelar"
proceed: "Proceder"
input_is_not_correct: "A entrada está incorreta"
step_0:
you_are_attempting: "Você está tentando deletar sua conta. Isso significa:"
item_1: "Todos os seus comentários estão removidos desse site"
item_2: "Todos os seus comentários estão removidos dos nossos bancos de dado"
item_3: "Seu usuário e endereço de email estão removidos do nosso sistema"
step_1:
subtitle: "Quando minha conta será deletada?"
description: "Sua conta será deletada {0} horas depois que sua solicitação for enviada."
subtitle_2: "Ainda posso comentar antes da minha conta ser deletada?"
description_2: "Sim, você ainda pode comentar, responder, e reagir aos comentários até as {0} horas expirarem."
step_2:
description: "Antes da sua conta ser deletada, nós recomendamos você a baixar seu histórico de comentários. Depois que sua conta for deletada, não será possível solicitar seu histórico de comentários."
to_download: "Para baixar seu histórico de comentários, vá em:"
path: "Meu Perfil > Baixar meu histórico de comentários"
step_3:
subtitle: "Você tem certeza que deseja deletar sua conta?"
description: "Para confirmar que você deseja deletar sua conta por favor digite a frase a seguir na caixa de texto abaixo:"
type_to_confirm: "Digite a frase abaixo para confirmar"
@@ -1,3 +1,41 @@
ar:
download_landing:
download_your_account: "قم بتنزيل سجل التعليقات الخاص بك"
download_details: "سيتم تنزيل سجل التعليقات في ملف .zip. بعد فك ضغط محفوظات التعليقات ، سيكون لديك ملف ذو قيمة مفصولة بفاصلة (أو .csv) يمكنك استيراده بسهولة إلى تطبيق جدول البيانات المفضل لديك."
all_information_included: "لكل من تعليقاتك يتم تضمين المعلومات التالية:"
information_included:
date: "متى كتبت التعليق"
url: "الرابط الثابت للتعليق"
body: "نص التعليق"
asset_url: "عنوان URL للمقال أو القصة التي يظهر فيها التعليق"
confirm: "تنزيل محفوظات تعليقاتي"
email:
download:
subject: "تعليقاتك جاهزة للتنزيل من {0}"
download_link_ready: "انقر هنا لتنزيل تعليقاتك من
{0} اعتبارا من {1}:"
download_archive: "تنزيل الأرشيف"
delete:
subject: "حسابك ل {0} من المقرر أن يتم حذفه"
body: |
تم تلقي طلب لحذف حسابك. تمت جدولة حسابك للحذف في {1}.
بعد ذلك الوقت ، ستتم إزالة جميع تعليقاتك من الموقع ، وستتم إزالة جميع تعليقاتك من قاعدة بياناتنا ، وستتم إزالة اسم المستخدم وعنوان البريد الإلكتروني من نظامنا.
إذا غيرت رأيك ، يمكنك تسجيل الدخول إلى حسابك وإلغاء الطلب قبل وقت حذف الحساب المجدول.
deleted:
subject: "حسابك ل {0} قد تم حذفه"
body: |
حساب المعلق الخاص بك ل {0} تم حذفه الآن. نحن آسفون أن نراك تذهب!
إذا كنت ترغب في إعادة الانضمام إلى المناقشة في المستقبل ، يمكنك الاشتراك للحصول على حساب جديد.
إذا كنت ترغب في تزويدنا بتعليقات حول سبب تركك وما يمكننا فعله لجعل تجربة التعليق أفضل ، يرجى مراسلتنا عبر البريد الإلكتروني على {1}.
cancelDelete:
subject: "طلب حذف حسابك ل {0} تم إلغاؤه"
body: "لقد ألغيت طلب حذف حسابك ل {0}. تم الآن إعادة تفعيل حسابك."
error:
DOWNLOAD_TOKEN_INVALID: "رابط التنزيل الخاص بك غير صالح."
en:
download_landing:
download_your_account: "Download Your Comment History"
@@ -35,6 +73,43 @@ en:
body: "You have cancelled your account deletion request for {0}. Your account is now reactivated."
error:
DOWNLOAD_TOKEN_INVALID: "Your download link is not valid."
es:
download_landing:
download_your_account: "Descargue su historial de comentarios"
download_details: "Su historial de comentarios se descargará en un archivo .zip. Después de descomprimir el historial de comentarios, tendrá un archivo de valores separados por comas (o .csv) que podrá importar fácilmente en su aplicación de hoja de cálculo favorita."
all_information_included: "Para cada uno de sus comentarios se incluye la siguiente información:"
information_included:
date: "Cuando escribiste el comentario"
url: "La URL del enlace permanente para el comentario"
body: "El texto de comentario"
asset_url: "La URL del artículo o historia donde aparece el comentario"
confirm: "Descargar el historial de mis comentarios"
email:
download:
subject: "Sus comentarios están listos para su descarga desde {0}"
download_link_ready: "Haga clic aquí para descargar sus comentarios de {0} a partir de {1}:"
download_archive: "Descargar archivo"
delete:
subject: "Su cuenta para {0} está programada para ser eliminada"
body: |
Se recibió una solicitud para eliminar su cuenta. Su cuenta está programada para ser eliminada {1}.
Después de ese momento, todos sus comentarios serán eliminados del sitio, todos sus comentarios serán eliminados de nuestra base de datos y su nombre de usuario y dirección de correo electrónico serán eliminados de nuestro sistema.
Si cambia de opinión, puede iniciar sesión en su cuenta y cancelar la solicitud antes de la hora programada para la eliminación de su cuenta.
deleted:
subject: "Su cuenta para {0} ha sido eliminada"
body: |
Su cuenta de comentarista para {0} ahora se borró. ¡Sentimos verte partir!
Si desea volver a unirse a la discusión en el futuro, puede registrarse para obtener una nueva cuenta.
Si desea darnos su opinión sobre por qué se fue y qué podemos hacer para mejorar la experiencia de comentarios, envíenos un correo electrónico a {1}.
cancelDelete:
subject: "La solicitud de eliminación de su cuenta para {0} ha sido cancelada"
body: "Ha cancelado la solicitud de eliminación de cuenta para {0}. Su cuenta ahora está reactivada."
error:
DOWNLOAD_TOKEN_INVALID: "Su enlace de descarga no es válido."
de:
download_landing:
download_your_account: "Mein Kommentar-Archiv herunterladen"
@@ -28,8 +28,8 @@ nl_NL:
respected: Gerespecteerd
pt_BR:
talk-plugin-respect:
respect: Respect
respected: Respected
respect: Respeitar
respected: Respeitado
zh_CN:
talk-plugin-respect:
respect: "赞"
@@ -21,7 +21,7 @@ nl_NL:
label: Meest beantwoord eerst
pt_BR:
talk-plugin-sort-most-replied:
label: Most replied first
label: Mais respondidos primeiro
zh_CN:
talk-plugin-sort-most-replied:
label: "最多回复在前"
@@ -21,7 +21,7 @@ nl_NL:
label: Meest gerespecteerd eerst
pt_BR:
talk-plugin-sort-most-respected:
label: Most respected first
label: Mais respeitados primeiro
zh_CN:
talk-plugin-sort-most-respected:
label: "最多被赞在前"
@@ -1,6 +1,9 @@
en:
talk-plugin-sort-most-upvoted:
label: Most upvoted first
de:
talk-plugin-sort-most-upvoted:
label: Am besten bewertet
es:
talk-plugin-sort-oldest:
label: Más votadas primero
@@ -21,7 +21,7 @@ nl_NL:
label: Nieuwste eerst
pt_BR:
talk-plugin-sort-newest:
label: Newest first
label: Mais novos primeiro
zh_CN:
talk-plugin-sort-newest:
label: "最新发表在前"
@@ -21,7 +21,7 @@ nl_NL:
label: Oudste eerst
pt_BR:
talk-plugin-sort-oldest:
label: Oldest first
label: Mais antigos primeiro
zh_CN:
talk-plugin-sort-oldest:
label: "最早发表在前"
@@ -71,17 +71,17 @@ de:
es:
error:
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.
¿Estás seguro? El lenguaje en este comentario puede violar nuestras pautas de la comunidad.
Puede editar el comentario o enviarlo para la revisión del moderador.
talk-plugin-toxic-comments:
unlikely: "Unlikely"
highly_likely: "Highly Likely"
possibly: "Possibly"
likely: "Likely"
toxic_comment: "Toxic Comment"
unlikely: "Improbable"
highly_likely: "Altamente Probable"
possibly: "Posiblemente"
likely: "Probable"
toxic_comment: "Comentario Tóxico"
still_toxic: |
This edited comment might still violate our community guidelines.
Our moderation team will review your comment shortly.
Este comentario editado aún puede infringir las pautas de nuestra comunidad.
Nuestro equipo de moderación revisará tu comentario en breve.
flags:
reasons:
comment:
@@ -7,6 +7,7 @@
"author": "The Coral Project Team <coral@mozillafoundation.org>",
"license": "Apache-2.0",
"dependencies": {
"debug": "^4.0.1",
"ms": "^2.0.0"
}
}
@@ -1,40 +1,60 @@
const { getScores, isToxic } = require('./perspective');
const { ErrToxic } = require('./errors');
const debug = require('debug')('talk:plugin:toxic-comments');
function handlePositiveToxic(input) {
input.status = 'SYSTEM_WITHHELD';
input.actions =
input.actions && input.actions.length >= 0 ? input.actions : [];
input.actions.push({
action_type: 'FLAG',
user_id: null,
group_id: 'TOXIC_COMMENT',
metadata: {},
});
}
async function getScore(body) {
// Try getting scores.
let scores;
try {
scores = await getScores(body);
} catch (err) {
// Warn and let mutation pass.
debug('Error sending to API: %o', err);
return;
}
return scores;
}
module.exports = {
RootMutation: {
editComment: {
pre: async (_, { edit: { body }, edit }) => {
const scores = await getScore(body);
if (isToxic(scores)) {
handlePositiveToxic(edit);
}
},
},
createComment: {
async pre(_, { input }, _context, _info) {
// Try getting scores.
let scores;
try {
scores = await getScores(input.body);
} catch (err) {
// Warn and let mutation pass.
console.trace(err); // TODO: log/handle this differently?
return;
}
// Attach scores to metadata.
input.metadata = Object.assign({}, input.metadata, {
perspective: scores,
});
const scores = await getScore(input.body);
if (isToxic(scores)) {
if (input.checkToxicity) {
throw new ErrToxic();
}
input.status = 'SYSTEM_WITHHELD';
input.actions =
input.actions && input.actions.length >= 0 ? input.actions : [];
input.actions.push({
action_type: 'FLAG',
user_id: null,
group_id: 'TOXIC_COMMENT',
metadata: {},
});
// Mark the comment as positive toxic.
handlePositiveToxic(input);
}
// Attach scores to metadata.
input.metadata = Object.assign({}, input.metadata, {
perspective: scores,
});
},
},
},
@@ -6,6 +6,7 @@ const {
API_TIMEOUT,
DO_NOT_STORE,
} = require('./config');
const debug = require('debug')('talk:plugin:toxic-comments');
/**
* Get scores from the perspective api
@@ -13,6 +14,8 @@ const {
* @return {object} object containing toxicity scores
*/
async function getScores(text) {
debug('Sending to Perspective: %o', text);
const response = await fetch(
`${API_ENDPOINT}/comments:analyze?key=${API_KEY}`,
{
@@ -25,7 +28,6 @@ async function getScores(text) {
comment: {
text,
},
// TODO: support other languages.
languages: ['en'],
doNotStore: DO_NOT_STORE,
@@ -36,7 +38,22 @@ async function getScores(text) {
}),
}
);
const data = await response.json();
// If we get an error, just say it's not a toxic comment.
if (data.error) {
debug('Recieved Error when submitting: %o', data.error);
return {
TOXICITY: {
summaryScore: 0.0,
},
SEVERE_TOXICITY: {
summaryScore: 0.0,
},
};
}
return {
TOXICITY: {
summaryScore: data.attributeScores.TOXICITY.summaryScore.value,
@@ -20,7 +20,7 @@ de:
filter: Filtern
es:
talk-plugin-viewing-options:
viewing_options: "Opciones de visualización"
viewing_options: "Opciones"
sort: Ordenado por
filter: Filtrado por
fr:
@@ -35,7 +35,7 @@ nl_NL:
filter: Filtering
pt_BR:
talk-plugin-viewing-options:
viewing_options: "Viewing Options"
viewing_options: "Ver opções"
sort: Sorting
filter: Filtering
zh_CN:
+2 -2
View File
@@ -1,7 +1,7 @@
const express = require('express');
const router = express.Router();
const authorization = require('../../../middleware/authorization');
const errors = require('../../../errors');
const { ErrHTTPNotFound } = require('../../../errors');
const AssetsService = require('../../../services/assets');
const AssetModel = require('../../../models/asset');
@@ -90,7 +90,7 @@ router.get(
// Send back the asset.
let asset = await AssetsService.findById(req.params.asset_id);
if (!asset) {
return next(errors.ErrNotFound);
return next(new ErrHTTPNotFound());
}
return res.json(asset);
+2 -2
View File
@@ -1,14 +1,14 @@
const express = require('express');
const router = express.Router();
const casual = require('casual');
const { ErrNotFound } = require('../../errors');
const { ErrHTTPNotFound } = require('../../errors');
const Asset = require('../../models/asset');
router.get('/id/:asset_id', async (req, res, next) => {
try {
const asset = await Asset.findOne({ id: req.params.asset_id });
if (asset === null) {
throw new ErrNotFound();
throw new ErrHTTPNotFound();
}
res.render('dev/article.njk', {
+2 -2
View File
@@ -2,7 +2,7 @@ const SetupService = require('../services/setup');
const authentication = require('../middleware/authentication');
const logging = require('../middleware/logging');
const cookieParser = require('cookie-parser');
const { TalkError, ErrNotFound } = require('../errors');
const { TalkError, ErrHTTPNotFound } = require('../errors');
const express = require('express');
const i18n = require('../middleware/i18n');
const path = require('path');
@@ -146,7 +146,7 @@ router.use(require('./plugins'));
// Catch 404 and forward to error handler.
router.use((req, res, next) => {
next(new ErrNotFound());
next(new ErrHTTPNotFound());
});
// Add logging for errors.
+2 -15
View File
@@ -1,4 +1,4 @@
const { ErrCommentTooShort } = require('../../../errors');
const { ErrCommentTooShort, ErrCommentTooLong } = require('../../../errors');
// This phase checks to see if the comment is long enough.
module.exports = (
@@ -17,19 +17,6 @@ module.exports = (
// Reject if the comment is too long
if (charCountEnable && comment.body.length > charCount) {
// Add the flag related to Trust to the comment.
return {
status: 'REJECTED',
actions: [
{
action_type: 'FLAG',
user_id: null,
group_id: 'BODY_COUNT',
metadata: {
count: comment.body.length,
},
},
],
};
throw new ErrCommentTooLong(comment.body.length, charCount);
}
};
+11 -1
View File
@@ -19,6 +19,7 @@ const ms = require('ms');
const _ = require('lodash');
const { attachStaticLocals } = require('../middleware/staticTemplate');
const { encodeJSONForHTML } = require('./response');
const { STATIC_URL, BASE_URL } = require('../url');
// Create a redis client to use for authentication.
const { createClientFactory } = require('./redis');
@@ -97,6 +98,15 @@ const HandleGenerateCredentials = (req, res, next) => (err, user) => {
res.json({ user, token });
};
/**
* authPopupCallbackCSP is the header sent via Content-Security-Policy when
* a social callback request is being made.
*/
const authPopupCallbackCSP = (() =>
STATIC_URL && BASE_URL !== STATIC_URL
? `default-src 'self' ${STATIC_URL.replace(/\/$/, '')};`
: "default-src 'self';")();
/**
* Returns the response to the login attempt via a popup callback with some JS.
*/
@@ -106,7 +116,7 @@ const HandleAuthPopupCallback = (req, res, next) => (err, user) => {
res.header('Pragma', 'no-cache');
// Ensure the only scripts that can run here are those on the Talk domain.
res.header('Content-Security-Policy', "default-src 'self';");
res.header('Content-Security-Policy', authPopupCallbackCSP);
// Attach static locals to the response locals object.
attachStaticLocals(res.locals);
+10 -3
View File
@@ -5,7 +5,6 @@ const Assets = require('./assets');
const Settings = require('./settings');
const { ADD_COMMENT_TAG } = require('../perms/constants');
const { ErrNotAuthorized } = require('../errors');
const { get, has } = require('lodash');
const updateModel = async (item_type, query, update) => {
// Get the model to update with.
@@ -60,8 +59,16 @@ class TagsService {
asset = await Assets.findById(id);
}
if (asset && has(asset, 'settings.tags')) {
return get(asset, 'settings.tags');
// Lodash has issues with determining property existence when the object
// is a Mongoose model, this is likely related to how Mongoose proxies the
// object, but this concrete check works.
if (
asset &&
asset.settings &&
asset.settings.tags &&
Array.isArray(asset.settings.tags)
) {
return asset.settings.tags;
}
// Extract the tags from the settings object.

Some files were not shown because too many files have changed in this diff Show More