Compare commits

...
236 Commits
Author SHA1 Message Date
Kim Gardner dd0601a801 Merge pull request #2135 from coralproject/concurrency
Concurrency
2018-12-19 20:53:30 +00:00
Wyatt Johnson 9f7d485871 Update package.json 2018-12-19 20:39:25 +00:00
Wyatt Johnson ec816636e6 feat: added concurrency features 2018-12-19 12:40:08 -07:00
Kim Gardner 08f579225b Bump version 4.6.11 (#2130) 2018-12-17 16:34:43 +00:00
Kim Gardner 46987b6303 Merge pull request #2122 from oafx/oafx/qbb-localization
Add localization for QuestionBoxBuilder
2018-12-17 15:09:31 +00:00
Kim Gardner c2d07ec79f Merge branch 'master' into oafx/qbb-localization 2018-12-17 14:43:32 +00:00
Kim Gardner a98f5ef980 Merge pull request #2128 from oafx/oafx/fix/moderate-commentcontainer-wordbreak
fix minor ui/css bug in moderation/comment component caused by long links
2018-12-17 14:42:54 +00:00
Christian Ruttorf d6515e23f8 fix ui/css bug in moderation/comment component caused by long words/links 2018-12-16 08:48:21 +01:00
immber a3db05845d Merge pull request #2126 from coralproject/docs-update
Add Documentation Redirect - asset management to cms integration
2018-12-14 13:06:59 -08:00
Wyatt Johnson c8fc419cec Update _redirects 2018-12-14 20:46:57 +00:00
immber 677c8a0828 Merge branch 'master' into docs-update 2018-12-14 12:06:22 -08:00
immber 08146fd979 added redirect 2018-12-14 12:04:28 -08:00
Kim Gardner ab92fe3fe8 Merge branch 'master' into oafx/qbb-localization 2018-12-14 18:18:33 +00:00
immber 95435f0793 Docs update (#2120)
* merging in typo fixes from docs (rtd) branch

* revert generateIntrospectionResult.json

* revert git ignore

* added asset mgmt to prelaunch checklist

* updated cusotm plugin docker onbuild instructions

* add google to auth options

* add google to auth options

* added yarn watch to from source

* added dev links to quickstart

* typo on following

* fixed typos

* fixing links

* added lazy render and updated lazy load

* added version note

* renamed asset management to cms-integration

* added link to asset scraping
2018-12-14 17:05:52 +00:00
immber 7eed540671 added link to asset scraping 2018-12-13 13:10:14 -08:00
immber 374951bd33 renamed asset management to cms-integration 2018-12-13 13:04:01 -08:00
immber 42a694f56e Merge branch 'master' of https://github.com/coralproject/talk into docs-update 2018-12-13 12:52:18 -08:00
immber 1c997aea31 Merge branch 'docs-update' of https://github.com/coralproject/talk into docs-update 2018-12-13 12:51:58 -08:00
immber 44830f6cb1 added version note 2018-12-13 12:51:50 -08:00
Wyatt Johnson 22d263c6d2 Embed Optimizations (#2121)
* feat: refactored bridge

- Cleaned up implementation of bridge
- Removed unneeded babel-polyfill and webpack globals

* fix: fixed up some static headers

- bumped version

* fix: moved intersection observer out of chunk

* fix: fixed misplaced "!"
2018-12-13 20:41:40 +00:00
Christian Ruttorf 78ba82cc05 add localization for QuestionBoxBuilder title, add PropTypes for QuestionBoxBuilder 2018-12-13 17:42:50 +01:00
immber a6b9cc57ac Merge branch 'master' into docs-update 2018-12-12 12:46:04 -08:00
immber cd9cb56844 added lazy render and updated lazy load 2018-12-12 12:44:19 -08:00
Wyatt Johnson d331bdee18 fix: fixed misplaced "!" 2018-12-12 12:59:54 -07:00
Wyatt Johnson d6f6fbcd9c CDN + Observabilities fixes (#2119)
* feat: added heroku fixes

- Support pulling the request id from the request
  headers
- Add proper cache headers to static files

* Update package.json
2018-12-12 18:45:50 +00:00
immber 1ed5df71da fix bug in cli-setup (#2116)
* fix bug in cli-setup

* fix cli setup bug

* fix: fixed password bug
2018-12-12 00:20:16 +00:00
immber 8b1b61f695 pulling changes from upstream 2018-12-10 14:26:58 -08:00
Kim Gardner f1a3a5ca28 Lazy Load Fix (#2110)
* Bump version 4.6.8

* fix: added JS fix for static URL's
2018-12-06 22:45:52 +00:00
Wyatt Johnson 8ef44dc6f5 fix: dep order 2018-12-06 14:41:39 -07:00
Kiwi 362f29f77e Support lazy rendering (#2109)
* feat: support lazy rendering

* fix: added new env var to onbuild args

* review: apply suggestions
2018-12-06 21:24:31 +00:00
immber 08d342ea2e Docs update (#2105)
* merging in typo fixes from docs (rtd) branch

* revert generateIntrospectionResult.json

* revert git ignore

* added asset mgmt to prelaunch checklist

* updated cusotm plugin docker onbuild instructions

* add google to auth options

* add google to auth options

* added yarn watch to from source

* added dev links to quickstart

* typo on following

* fixed typos

* fixing links
2018-12-03 19:47:46 +00:00
immber 99777d6b49 fixing links 2018-12-03 11:07:40 -08:00
immber 6f6da1fa25 merging changes from master 2018-12-03 09:34:25 -08:00
immber 2fac304163 fixed typos 2018-12-03 09:33:03 -08:00
immber 5ca7c092db Merge branch 'master' into docs-update 2018-11-30 18:05:35 -08:00
immber cfae6b5374 typo on following 2018-11-30 18:04:20 -08:00
immber 3668a2cffa added dev links to quickstart 2018-11-30 17:56:20 -08:00
immber 92681f52aa added yarn watch to from source 2018-11-30 17:39:02 -08:00
immber 847ded5bb4 add google to auth options 2018-11-30 17:19:27 -08:00
immber 76c959f790 add google to auth options 2018-11-30 17:11:42 -08:00
immber 4659392ad3 updated cusotm plugin docker onbuild instructions 2018-11-30 15:10:00 -08:00
immber e9cec73966 added asset mgmt to prelaunch checklist 2018-11-30 14:44:35 -08:00
immber bac0ee9c3b revert git ignore 2018-11-28 14:00:07 -08:00
immber 5ae293af89 revert generateIntrospectionResult.json 2018-11-28 13:58:56 -08:00
immber cce464b82a merging in typo fixes from docs (rtd) branch 2018-11-28 13:56:31 -08:00
Kim Gardner 5d32c4ec03 Merge pull request #2097 from richardvenneman/nl-talk-plugins
Add/fix Dutch (nl_NL) translations for Talk plugins
2018-11-28 18:01:22 +00:00
Kim Gardner 50d4e2bb9b Merge branch 'master' into nl-talk-plugins 2018-11-27 21:08:22 +00:00
Richard Venneman debdd3eae4 Fix nl typo 2018-11-27 16:56:56 -04:00
Richard Venneman 286d9baddf Update Dutch (nl_NL) translations for talk-plugin-love 2018-11-27 16:56:49 -04:00
Richard Venneman 9c06a63afd Add Dutch (nl_NL) translations for talk notifications plugins 2018-11-27 16:55:32 -04:00
Richard Venneman f38e72c4df Add Dutch (nl_NL) translations for talk-plugin-google-auth 2018-11-27 16:32:36 -04:00
Richard Venneman e2fac4a2dc Fix and update talk-plugin-auth Dutch (nl_NL) translations 2018-11-27 16:30:42 -04:00
Richard Venneman ba0513fd6c Fix talk-plugin-member-since Dutch (nl_NL) translation
Member != User
2018-11-27 16:22:00 -04:00
Richard Venneman 8e29f0094c Add Dutch (nl_NL) translations for talk-plugin-facebook-auth 2018-11-27 16:20:39 -04:00
Kim Gardner f60c7e3ca6 Version 4.6.7 (#2096) 2018-11-27 20:20:00 +00:00
Richard Venneman b3c034d89d Add Dutch (nl_NL) translation for talk-plugin-local-auth 2018-11-27 16:16:34 -04:00
Kim Gardner be2657eb30 Merge pull request #2095 from richardvenneman/update-nl-translations
Update Dutch (nl_NL) translations
2018-11-27 19:39:16 +00:00
Richard Venneman fd32d2aa24 Merge branch 'master' into update-nl-translations 2018-11-27 15:29:05 -04:00
Richard Venneman 809b39ae2a Update Dutch (nl_NL) translations 2018-11-27 15:25:14 -04:00
Kim Gardner 6197981480 Merge pull request #2092 from coralproject/sp-trans-4
Fixed Sp translations
2018-11-27 18:39:51 +00:00
Kim Gardner eb58dd52f8 Fixed Sp translations 2018-11-26 15:25:44 +00:00
Kim Gardner f5c011f44b Merge pull request #2077 from coralproject/ie11-fixes
Fix IE11 issues in Admin
2018-11-16 12:59:04 -05:00
Chi Vinh Le 396ccbd6c2 fix: workaround flexbox bug in IE11 2018-11-15 23:51:28 +01:00
Chi Vinh Le 4fd61b3731 fix: polyfill missing forEach for NodeLists in IE11 2018-11-15 23:51:14 +01:00
Kim Gardner cbd795dc95 Merge pull request #2073 from coralproject/sp-trans-3
Add Spanish translations for blank comment history
2018-11-09 16:18:02 -05:00
Kim Gardner 65a6b484a9 Add Spanish translations for blank comment history 2018-11-09 10:00:53 -05:00
Kim Gardner 45c193b9bf Merge pull request #2069 from coralproject/wyattjoh-patch-1
Toxic Comments Fix
2018-11-08 11:53:41 -05:00
Wyatt Johnson ed037f8461 Merge branch 'master' into wyattjoh-patch-1 2018-11-08 16:00:46 +00:00
Kim Gardner 7bb0c73ee4 Merge pull request #2070 from coralproject/add-email-iframe-size
Fix add email dialog cutoff
2018-11-08 10:33:43 -05:00
Chi Vinh Le f13654b297 fix: lint 2018-11-08 16:04:16 +00:00
Chi Vinh Le 4a165c0972 fix: ensure iframe is big enough for add email dialog 2018-11-08 15:58:26 +00:00
Wyatt Johnson bbf034a595 Update ToxicDetail.js 2018-11-07 22:27:48 +00:00
Kim Gardner 9f6ddd3801 Version 4.6.6 (#2066) 2018-11-07 18:31:06 +00:00
Kim Gardner 34184d07eb Merge pull request #2065 from coralproject/button-trans-es
Fix button translation
2018-11-06 20:32:26 +00:00
Kim Gardner 057d083341 Merge branch 'master' into button-trans-es 2018-11-06 20:07:35 +00:00
Kim Gardner c5f107a168 Merge pull request #2067 from coralproject/perspective-feedback
Perspective Feedback
2018-11-06 20:05:32 +00:00
Wyatt Johnson 17a801d6d2 fix: removed url parsing 2018-11-06 13:02:20 -07:00
Kim Gardner 888a61f895 Merge branch 'master' into perspective-feedback 2018-11-06 19:56:31 +00:00
Wyatt Johnson 9edf1d7c41 feat: added perspective feedback 2018-11-06 12:39:16 -07:00
Kim Gardner b802fdd5eb Merge pull request #2061 from coralproject/display-avatar-left
Display avatar on the left
2018-11-06 19:31:16 +00:00
Kim Gardner 1a7b3fe903 Fix button translation 2018-11-06 13:50:31 -05:00
Kiwi cadbbd0b5c Merge branch 'master' into display-avatar-left 2018-11-02 00:09:03 +01:00
Chi Vinh Le b6101b880d fix: show avatar on the left 2018-11-02 00:01:46 +01:00
immber afb9fdcdc1 Merge pull request #2060 from coralproject/losowsky-patch-1
Updated text
2018-11-01 12:22:26 -07:00
Andrew Losowsky 53abb65da1 Updated text
- updated User Drawer information
- updated Shortcuts information
- changed the order
2018-11-01 15:08:08 -04:00
immber 7b5192d40c Merge pull request #2045 from coralproject/moderation-docs
updated mod queues documentation
2018-11-01 09:23:40 -07:00
Wyatt Johnson 8fdea8734a Merge branch 'master' into moderation-docs 2018-11-01 16:03:14 +00:00
Kim Gardner 588fbb0eb9 Merge pull request #2036 from tanhengyeow/cli-tools
Document CLI tools as many people don't know about this #1941
2018-10-30 18:29:08 +00:00
Kim Gardner a28024dfed Revert CSS from #1990 2018-10-30 18:18:35 +00:00
Kim Gardner 343eb3e8b6 Merge branch 'master' into cli-tools 2018-10-30 18:17:27 +00:00
Kim Gardner 80319f9aca Merge pull request #2047 from coralproject/sp-trans-1
Spanish translation tweak #1
2018-10-30 18:16:59 +00:00
Kim Gardner 33e1e5e729 Merge branch 'master' into sp-trans-1 2018-10-30 18:08:26 +00:00
Kim Gardner ea2b2bc2e3 Merge pull request #2032 from vittoboa/italian-translation
Add italian translation
2018-10-30 18:08:15 +00:00
Kim Gardner 6ca2f21bc2 Merge branch 'master' into italian-translation 2018-10-30 18:01:25 +00:00
Kim Gardner 0d9b795740 Merge pull request #2051 from coralproject/edit-link-trans
i18n support for Edit link
2018-10-30 18:00:54 +00:00
Kim Gardner 73482d68ac Merge branch 'master' into italian-translation 2018-10-30 17:55:43 +00:00
Wyatt Johnson e837dfc3c1 Merge branch 'master' into edit-link-trans 2018-10-30 17:54:37 +00:00
Kim Gardner 2cd9a71d62 i18n support for Edit link 2018-10-30 17:40:45 +00:00
Wyatt Johnson ad73fcd94b fix: fixed typo (#2049) 2018-10-30 17:31:32 +00:00
Wyatt Johnson 66145d6fd5 Merge branch 'master' into sp-trans-1 2018-10-30 17:06:44 +00:00
Wyatt Johnson 7d246bba76 Metadata Scraper Debugger (#2048)
* feat: added asset scrape debugger

* fix: added docs for scraper

* fix: added asset scraping to sidebar
2018-10-30 17:02:33 +00:00
Kim Gardner 0ce0d57a96 Spanish translation tweak #1 2018-10-30 15:45:26 +00:00
Kim Gardner 792bc713f2 Merge branch 'master' into italian-translation 2018-10-30 15:35:53 +00:00
Kim Gardner 399f5ef7ba Merge pull request #2046 from kant/patch-1
Typo on #77
2018-10-30 15:34:17 +00:00
Darío Hereñú 14c0945687 Typo on #77
* plus some other typos found (and fixed)
2018-10-30 11:01:23 -03:00
immber 52f2f48385 updated mod queues documentation 2018-10-29 16:05:26 -07:00
Andrew Losowsky e52605e26b removed Reliable/Unreliable flaggers (#2043)
We don't display this any more
2018-10-29 16:29:13 +00:00
Andrew Losowsky ae4d6e2424 Updated Tag text (#2042) 2018-10-29 16:12:17 +00:00
Kiwi 968d20e744 Merge pull request #2018 from coralproject/admin-improvements
Consistency Improvements
2018-10-26 20:28:19 +02:00
Kiwi 49d2416172 Merge branch 'master' into admin-improvements 2018-10-26 20:08:24 +02:00
Parikshit Hooda aac7cae6d6 typo fix : whih to which (#2039)
typo fix : whih to which
2018-10-26 17:22:19 +00:00
Kim Gardner f4fd843efe Merge pull request #1963 from twosmalltrees/1920
Update text and description in moderation configure view
2018-10-26 10:58:36 +01:00
Heng Yeow c4de69218c Update _config.yml 2018-10-26 13:55:30 +08:00
Heng Yeow 94ae029a4a Create 02-03-cli-tools.md 2018-10-26 13:54:37 +08:00
Kim Gardner 1dc1f91b72 Merge branch 'master' into 1920 2018-10-25 21:00:47 +01:00
Kiwi c98216cf3c Merge branch 'master' into admin-improvements 2018-10-25 21:58:16 +02:00
Kim Gardner 2c9f8586d3 Merge pull request #2035 from coralproject/fix-pointer-events
Fix pointer events in configure card
2018-10-25 20:55:03 +01:00
Chi Vinh Le 4ce5d97e3b Always have pointer events in configure cards 2018-10-25 21:42:18 +02:00
Wyatt Johnson 3ea2632187 Merge branch 'master' into admin-improvements 2018-10-25 17:57:24 +00:00
vittoboa c9b709ad43 Add italian translation 2018-10-25 09:34:33 +02:00
Kim Gardner dcd7145510 Update Spanish translations (#2019)
* Update translations

* More sp trans

* Add translation for Contains Link
2018-10-24 11:58:42 -06:00
Kim Gardner d3896ed97d Merge pull request #2016 from hovoodd/stream-status
Fixed stream status updating
2018-10-24 17:39:22 +01:00
Kim Gardner 47a3cba5f5 Merge branch 'master' into stream-status 2018-10-24 13:57:53 +01:00
Kim Gardner 0f1cabe126 Merge pull request #2017 from iSasFTW/master
Visible validation errors in the username change box
2018-10-24 13:56:52 +01:00
Saska Kolehmainen 5f62083c2f Merge branch 'master' into master 2018-10-23 20:53:52 +03:00
Wyatt Johnson 3cabe50a7b fix: strip out 0xAD from the comment body before testing for links 2018-10-23 11:06:31 -06:00
Kim Gardner c0efc33044 Merge branch 'master' into stream-status 2018-10-23 17:47:48 +01:00
Kim Gardner 9344c1bc80 Merge pull request #2015 from hovoodd/change-username
Added additional check for `usernameCanBeUpdated` condition in profile
2018-10-23 17:47:37 +01:00
Kim Gardner 1bfe0fbac1 Merge branch 'master' into change-username 2018-10-23 17:41:10 +01:00
Kim Gardner b3d704be76 Merge branch 'master' into 1920 2018-10-23 17:23:11 +01:00
Kim Gardner 3c100160fb Merge pull request #2014 from hovoodd/collapsible-list
Made Banned/Suspended Word Lists collapsible in Admin > Configure
2018-10-23 16:19:23 +01:00
Wyatt Johnson 49c7d2eeda fix: move priority up for token fetching 2018-10-22 12:25:20 -06:00
Wyatt Johnson cff9b29a36 fix: adjust perspective defaults when failure occurs 2018-10-22 12:22:01 -06:00
Wyatt Johnson 5a1f93975c feat: added support for access_token hash passthrough 2018-10-22 12:13:15 -06:00
Wyatt Johnson cd6da751a6 fix: added patch to reset form 2018-10-22 12:12:35 -06:00
Saska Kolehmainen 41610ccf57 Added error message to profile tab in embedded comments for when disallowed characters are added while changing the username. 2018-10-21 17:45:54 +03:00
hovoodd 607a59ad63 Fix stream status updating 2018-10-20 22:59:13 +03:00
hovoodd c0fea4821b Add additional check for usernameCanBeUpdated condition in profile 2018-10-20 22:05:32 +03:00
hovoodd 8432df15a1 Add collapsible prop to banned and suspended lists in admin 2018-10-20 19:07:56 +03:00
hovoodd 61486caa9e Make ConfigureCard collapsible 2018-10-20 19:01:01 +03:00
Harrison Reid 7f45f7e2e2 Add Code of Conduct Link 2018-10-20 08:43:13 +11:00
Kim Gardner 9e511817a8 Merge pull request #1990 from snorremd/master
Display avatar left of comments by default
2018-10-19 19:51:21 +01:00
Kim Gardner 4015e19040 Merge branch 'master' into master 2018-10-19 19:47:36 +01:00
Kim Gardner c1fcc82b62 Merge pull request #2006 from twosmalltrees/1939
Keep Question Box visible after stream is closed #1939
2018-10-19 19:47:10 +01:00
Kim Gardner 893c8d32b2 Merge branch 'master' into 1920 2018-10-19 19:07:59 +01:00
Kim Gardner 2dfadebd42 Merge branch 'master' into 1939 2018-10-19 18:45:46 +01:00
Kim Gardner a2d5af1372 Merge pull request #2007 from coralproject/base64-secret
Base64 Secret Encoding
2018-10-19 18:45:12 +01:00
Kim Gardner 7a5416a807 Merge branch 'master' into base64-secret 2018-10-19 18:39:36 +01:00
Kim Gardner 8c2c632f52 Merge pull request #2011 from coralproject/email-prefix
Email Prefix Fix
2018-10-19 18:39:02 +01:00
Wyatt Johnson fabec2c89b review: fix invalid substring 2018-10-19 16:28:13 +00:00
Wyatt Johnson 7f18603fd5 fix: fixes #2009 2018-10-19 10:05:05 -06:00
Kim Gardner 3a69e0cf96 Merge branch 'master' into base64-secret 2018-10-18 21:53:43 +01:00
Wyatt Johnson 7ad3d247b5 feat: added base64 secret encoding 2018-10-18 14:33:45 -06:00
Harrison Reid 130dc578b3 Refactor Stream component, move QuestionBox render logic into seperate fn 2018-10-18 21:25:36 +11:00
Harrison Reid fd7114ab2a Keep question box visible when comment stream is closed 2018-10-18 21:06:00 +11:00
immber 5e4bdf1f0c Perspective (#2001)
* perspective documentation update

* fixed typo
2018-10-16 20:25:11 +00:00
immber 71e1b69afe perspective documentation update (#1999) 2018-10-16 18:35:10 +00:00
Kim Gardner 6fa18f6c7c Merge branch 'master' into 1920 2018-10-15 16:52:37 +01:00
Kim Gardner bda9b55562 Merge branch 'master' into master 2018-10-15 16:50:42 +01:00
Kim Gardner d843320c4a Merge pull request #1976 from brycekahle/fix-admin-menu-visibility
do not show admin drawer unless user has access
2018-10-15 16:50:30 +01:00
Kim Gardner 8a88228d9c Merge branch 'master' into 1920 2018-10-12 18:57:35 +01:00
Kim Gardner 0a9664d464 Merge branch 'master' into fix-admin-menu-visibility 2018-10-12 18:53:09 +01:00
Kim Gardner b3749b9332 Merge pull request #1961 from hovoodd/master
Added filters to Admin > Community > People
2018-10-12 18:48:55 +01:00
Kim Gardner aecb7de019 Add Spanish translations 2018-10-12 18:24:55 +01:00
Snorre Magnus Davøen ade3cf6252 Display avatar left of comments by default
Previously avatars would be displayed on top of comments. Most comment
and forum systems will display avatars to the left of comments/posts.
The https://github.com/snorremd/talk-plugin-gravatar plugin currently
renders avatars in the avatar slot which will render the avatar on top
of the comments. This change modified the comment container to be
displayed as a flex container with row direction. It also adds 10px top
margin to the avatar slot which corresponds with the comment header.

Signed-off-by: Snorre Magnus Davøen <snorremd@gmail.com>
2018-10-12 19:13:04 +02:00
Wyatt Johnson 1837ae4a0f Merge branch 'master' into fix-admin-menu-visibility 2018-10-12 16:44:22 +00:00
Kim Gardner 334074a7ca Merge branch 'master' into master 2018-10-12 16:55:59 +01:00
Fabian Marz f93c4adc97 Fixed typo in GDPR docs. (#1986) 2018-10-11 14:57:16 +00:00
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
Bryce Kahle 8b2dc3a46d do not show admin drawer unless user has access
Fixes https://github.com/coralproject/talk/issues/1791
2018-10-08 13:02:37 -04: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
Harrison Reid 9222ee57a5 Update text and description in moderation configure view
Rename include_comment_stream to code_of_conduct_summary, update text.
Rename include_comment_stream_desc to code_of_conduct_summary_desc, update text.
2018-10-04 18:46:18 +10:00
hovoodd 67cca7d85d Add filters to list of people 2018-10-03 22:15:05 +03: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
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
153 changed files with 5618 additions and 1219 deletions
+3 -3
View File
@@ -41,9 +41,9 @@ integration_job: &integration_job
- attach_workspace:
at: ~/coralproject/talk
- <<: *create_indexes
- run:
name: Setup the database with defaults
command: ./bin/cli setup --defaults
# - run:
# name: Setup the database with defaults
# command: ./bin/cli setup --defaults
- run:
name: Run the integration tests
command: bash .circleci/e2e.sh
+1
View File
@@ -11,6 +11,7 @@ ONBUILD ARG TALK_DEFAULT_LANG=en
ONBUILD ARG TALK_WHITELISTED_LANGUAGES
ONBUILD ARG TALK_PLUGINS_JSON
ONBUILD ARG TALK_WEBPACK_SOURCE_MAP
ONBUILD ARG TALK_DEFAULT_LAZY_RENDER
# Bundle app source
ONBUILD COPY . /usr/src/app
+2 -2
View File
@@ -14,11 +14,11 @@ 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
For advanced configuration and usage of Talk, check out our [Configuration](https://docs.coralproject.net/talk/advanced-configuration/) and [Integration](https://docs.coralproject.net/talk/integrating/authentication/) how-tos. This covers topics in whih you will need dev support to fully customize and integrate Talk, such as SSO/authentication, creating and managing assets and articles, styling Talk with custom CSS, and setting up Notifications and SMTP support.
For advanced configuration and usage of Talk, check out our [Configuration](https://docs.coralproject.net/talk/advanced-configuration/) and [Integration](https://docs.coralproject.net/talk/integrating/authentication/) how-tos. This covers topics in which you will need dev support to fully customize and integrate Talk, such as SSO/authentication, creating and managing assets and articles, styling Talk with custom CSS, and setting up Notifications and SMTP support.
## Versions & Upgrading
+29
View File
@@ -219,6 +219,28 @@ async function rewrite(search, replace, options) {
}
}
/**
* debugAssetURL will scrape the given URL and print it's scraped values.
*/
async function debugAssetURL(url) {
try {
const meta = await scraper.scrape(url);
const table = new Table({ head: ['Property', 'Value'] });
for (const [property, value] of Object.entries(meta)) {
table.push([property, value]);
}
console.log(table.toString());
util.shutdown(0);
} catch (err) {
console.error(err);
util.shutdown(1);
}
}
//==============================================================================
// Setting up the program command line arguments.
//==============================================================================
@@ -234,6 +256,13 @@ program
.description('list all the assets in the database')
.action(listAssets);
program
.command('debug [url]')
.description(
'prints the scraped metadata that would be added to the given asset'
)
.action(debugAssetURL);
program
.command('refresh [age]')
.description('queues the assets that exceed the age requested')
+17 -5
View File
@@ -1,8 +1,10 @@
#!/usr/bin/env node
const throng = require('throng');
const { CONCURRENCY } = require('../config');
const { logger } = require('../services/logging');
const util = require('./util');
const program = require('commander');
const serve = require('../serve');
//==============================================================================
// Setting up the program command line arguments.
@@ -22,8 +24,18 @@ program
)
.parse(process.argv);
// Start serving.
serve(program).catch(err => {
console.error(err);
util.shutdown(1);
throng({
workers: CONCURRENCY,
start: i => {
logger.info({ workerID: i }, 'started worker');
// Load in the serve.
const serve = require('../serve');
// Start serving.
serve(program).catch(err => {
console.error(err);
util.shutdown(1);
});
},
});
+37 -31
View File
@@ -45,11 +45,10 @@ const performSetup = async () => {
} catch (err) {
// If the error is `not init`, then we're good, otherwise, it's something
// else.
if (err instanceof ErrSettingsNotInit) {
if (!err instanceof ErrSettingsNotInit) {
throw err;
return;
}
throw err;
}
if (program.defaults) {
@@ -138,7 +137,7 @@ const performSetup = async () => {
console.log("\nWe'll ask you some questions about your first admin user.\n");
let user = await inquirer.prompt([
let { username, email } = await inquirer.prompt([
{
type: 'input',
name: 'username',
@@ -161,39 +160,46 @@ const performSetup = async () => {
return 'Email is required';
},
},
{
name: 'password',
message: 'Password',
type: 'password',
filter: password => {
return UsersService.isValidPassword(password).catch(err => {
throw err.message;
});
},
},
{
name: 'confirmPassword',
message: 'Confirm Password',
type: 'password',
filter: (confirmPassword, { password }) => {
if (password !== confirmPassword) {
return Promise.reject(new Error('Passwords do not match'));
}
return UsersService.isValidPassword(confirmPassword).catch(err => {
throw err.message;
});
},
},
]);
let password = '';
while (!password) {
answers = await inquirer.prompt([
{
name: 'password',
message: 'Password',
type: 'password',
filter: password => {
try {
UsersService.isValidPassword(password);
} catch (err) {
throw err.message;
}
return password;
},
},
{
name: 'confirmPassword',
message: 'Confirm Password',
type: 'password',
},
]);
if (answers.password !== answers.confirmPassword) {
console.error('Passwords do not match');
} else {
password = answers.password;
}
}
const ctx = Context.forSystem();
let { user: newUser } = await SetupService.setup(ctx, {
settings: settings.toObject(),
user: {
email: user.email,
username: user.username,
password: user.password,
email,
username,
password,
},
});
+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,
+5 -6
View File
@@ -7,9 +7,9 @@ import t from 'coral-framework/services/i18n';
import { can } from 'coral-framework/services/perms';
import cn from 'classnames';
const CoralDrawer = ({ handleLogout, currentUser }) => (
<Drawer className={cn('talk-admin-drawer-nav', styles.drawer)}>
{currentUser && can(currentUser, 'ACCESS_ADMIN') ? (
const CoralDrawer = ({ handleLogout, currentUser }) =>
currentUser && can(currentUser, 'ACCESS_ADMIN') ? (
<Drawer className={cn('talk-admin-drawer-nav', styles.drawer)}>
<div>
<Navigation className={styles.nav}>
{can(currentUser, 'MODERATE_COMMENTS') && (
@@ -48,9 +48,8 @@ const CoralDrawer = ({ handleLogout, currentUser }) => (
<span>{`v${process.env.VERSION}`}</span>
</Navigation>
</div>
) : null}
</Drawer>
);
</Drawer>
) : null;
CoralDrawer.propTypes = {
handleLogout: PropTypes.func.isRequired,
@@ -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"
>
@@ -123,7 +123,7 @@ class UserDetailComment extends React.Component {
<IfHasLink text={comment.body}>
<span className={styles.hasLinks}>
{/* TODO: translate string */}
<Icon name="error_outline" /> Contains Link
<Icon name="error_outline" /> {t('common.contains_link')}
</span>
</IfHasLink>
<div className={styles.actions}>
+5
View File
@@ -14,6 +14,11 @@ import { hideShortcutsNote } from './actions/moderation';
smoothscroll.polyfill();
if (!NodeList.prototype.forEach) {
// Polyfill IE11 missing forEach in NodeList.
NodeList.prototype.forEach = Array.prototype.forEach;
}
function init({ store, localStorage }) {
const shouldHide = localStorage.getItem('coral:shortcutsNote') === 'hide';
if (shouldHide) {
@@ -34,6 +34,21 @@
}
}
.filterHeader {
margin-top: 30px;
font-size: 16px;
font-weight: 900;
}
.filterDetail {
margin-top: 15px;
font-size: 16px;
}
.radioGroup {
margin: 5px 0;
}
.mainContent {
width: calc(100% - 300px);
padding: 34px 14px;
@@ -9,6 +9,7 @@ import PropTypes from 'prop-types';
import ActionsMenu from 'coral-admin/src/components/ActionsMenu';
import ActionsMenuItem from 'coral-admin/src/components/ActionsMenuItem';
import { isSuspended, isBanned } from 'coral-framework/utils/user';
import { RadioGroup, Radio } from 'react-mdl';
import moment from 'moment';
import {
ADMIN,
@@ -64,11 +65,12 @@ class People extends React.Component {
render() {
const {
onSearchChange,
onFilterChange,
users = [],
setUserRole,
viewUserDetail,
loadMore,
filters,
} = this.props;
const hasResults = !!users.nodes.length;
@@ -87,11 +89,43 @@ class People extends React.Component {
id="commenters-search"
type="text"
className={styles.searchBoxInput}
defaultValue=""
onChange={onSearchChange}
value={filters.search}
onChange={onFilterChange('search')}
placeholder={t('streams.search')}
/>
</div>
<div className={styles.filterHeader}>
{t('community.filter_users')}
</div>
<div className={styles.filterDetail}>{t('community.status')}</div>
<RadioGroup
name="statusFilter"
value={filters.status}
childContainer="div"
onChange={onFilterChange('status')}
className={styles.radioGroup}
>
<Radio value="">{t('community.all')}</Radio>
<Radio value="active">{t('community.active')}</Radio>
<Radio value="suspended">{t('community.suspended')}</Radio>
<Radio value="banned">{t('community.banned')}</Radio>
</RadioGroup>
<div className={styles.filterDetail}>
{t('community.filter_role')}
</div>
<RadioGroup
name="roleFilter"
value={filters.role}
childContainer="div"
onChange={onFilterChange('role')}
className={styles.radioGroup}
>
<Radio value="">{t('community.all')}</Radio>
<Radio value={ADMIN}>{t('community.admin')}</Radio>
<Radio value={STAFF}>{t('community.staff')}</Radio>
<Radio value={MODERATOR}>{t('community.moderator')}</Radio>
<Radio value={COMMENTER}>{t('community.commenter')}</Radio>
</RadioGroup>
</div>
<div className={styles.mainContent}>
{hasResults ? (
@@ -176,7 +210,7 @@ class People extends React.Component {
})
}
>
Suspend User
{t('modqueue.suspend')}
</ActionsMenuItem>
)}
@@ -195,7 +229,7 @@ class People extends React.Component {
})
}
>
Ban User
{t('modqueue.ban_user_actions')}
</ActionsMenuItem>
)}
</ActionsMenu>
@@ -254,7 +288,8 @@ class People extends React.Component {
People.propTypes = {
users: PropTypes.object.isRequired,
onSearchChange: PropTypes.func.isRequired,
filters: PropTypes.object.isRequired,
onFilterChange: PropTypes.func.isRequired,
setUserRole: PropTypes.func.isRequired,
viewUserDetail: PropTypes.func.isRequired,
unbanUser: PropTypes.func.isRequired,
@@ -17,59 +17,65 @@ import update from 'immutability-helper';
import { Spinner } from 'coral-ui';
import withQuery from 'coral-framework/hocs/withQuery';
class PeopleContainer extends React.Component {
class PeopleContainer extends React.PureComponent {
timer = null;
state = {
searchValue: '',
search: '',
role: '',
status: '',
};
onSearchChange = e => {
const { value } = e.target;
this.setState({ searchValue: value }, () => {
statusToQuery = {
active: { suspended: false, banned: false },
suspended: { suspended: true },
banned: { banned: true },
};
onFilterChange = filter => e =>
this.setState({ [filter]: e.target.value }, () => {
clearTimeout(this.timer);
this.timer = setTimeout(() => {
this.search(value);
}, 350);
this.timer = setTimeout(this.filter, 350);
});
getFilterState = () => {
const { role, status } = this.state;
return {
status: this.statusToQuery[status] || null,
role: role || null,
};
};
search = async value => {
return this.props.data.fetchMore({
query: SEARCH_QUERY,
filter = () =>
this.props.data.fetchMore({
query: FILTER_QUERY,
variables: {
value,
state: this.getFilterState(),
value: this.state.search,
limit: 10,
},
updateQuery: (previous, { fetchMoreResult: { users } }) => {
const updated = update(previous, {
updateQuery: (previous, { fetchMoreResult: { users } }) =>
update(previous, {
users: {
nodes: {
$set: users.nodes,
},
nodes: { $set: users.nodes },
hasNextPage: { $set: users.hasNextPage },
endCursor: { $set: users.endCursor },
},
});
return updated;
},
}),
});
};
setUserRole = async (id, role) => {
await this.props.setUserRole(id, role);
};
loadMore = () => {
return this.props.data.fetchMore({
loadMore = () =>
this.props.data.fetchMore({
query: LOAD_MORE_QUERY,
variables: {
value: this.state.searchValue,
limit: 5,
cursor: this.props.root.users.endCursor,
state: this.getFilterState(),
value: this.state.search,
limit: 5,
},
updateQuery: (previous, { fetchMoreResult: { users } }) => {
const updated = update(previous, {
updateQuery: (previous, { fetchMoreResult: { users } }) =>
update(previous, {
users: {
nodes: {
$apply: nodes => appendNewNodes(nodes, users.nodes),
@@ -77,11 +83,8 @@ class PeopleContainer extends React.Component {
hasNextPage: { $set: users.hasNextPage },
endCursor: { $set: users.endCursor },
},
});
return updated;
},
}),
});
};
render() {
if (this.props.data.error) {
@@ -98,9 +101,9 @@ class PeopleContainer extends React.Component {
return (
<People
onSearchChange={this.onSearchChange}
onFilterChange={this.onFilterChange}
viewUserDetail={this.props.viewUserDetail}
setUserRole={this.setUserRole}
setUserRole={this.props.setUserRole}
showSuspendUserDialog={this.props.showSuspendUserDialog}
showBanUserDialog={this.props.showBanUserDialog}
unbanUser={this.props.unbanUser}
@@ -109,6 +112,7 @@ class PeopleContainer extends React.Component {
root={this.props.root}
users={this.props.root.users}
loadMore={this.loadMore}
filters={this.state}
/>
);
}
@@ -125,16 +129,6 @@ PeopleContainer.propTypes = {
root: PropTypes.object,
};
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
viewUserDetail,
showSuspendUserDialog,
showBanUserDialog,
},
dispatch
);
const LOAD_MORE_QUERY = gql`
query TalkAdmin_Community_People_LoadMoreUsers(
$limit: Int
@@ -169,9 +163,13 @@ const LOAD_MORE_QUERY = gql`
}
`;
const SEARCH_QUERY = gql`
query TalkAdmin_Community_People_SearchUsers($value: String, $limit: Int) {
users(query: { value: $value, limit: $limit }) {
const FILTER_QUERY = gql`
query TalkAdmin_Community_People_FilterUsers(
$state: UserStateInput
$value: String
$limit: Int
) {
users(query: { state: $state, value: $value, limit: $limit }) {
hasNextPage
endCursor
nodes {
@@ -199,6 +197,16 @@ const SEARCH_QUERY = gql`
}
`;
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
viewUserDetail,
showSuspendUserDialog,
showBanUserDialog,
},
dispatch
);
export default compose(
connect(
null,
@@ -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>
@@ -155,9 +155,19 @@ class StreamSettings extends React.Component {
<ConfigureCard
checked={settings.infoBoxEnable}
onCheckbox={this.updateInfoBoxEnable}
title={t('configure.include_comment_stream')}
title={t('configure.code_of_conduct_summary')}
>
<p>{t('configure.include_comment_stream_desc')}</p>
<p>
{t('configure.code_of_conduct_summary_desc')}
&nbsp;
<a
target="_blank"
rel="noopener noreferrer"
href="https://guides.coralproject.net/create-a-code-of-conduct/"
>
Code of Conduct Guide.
</a>
</p>
<div
className={cn(
styles.configSettingInfoBox,
@@ -238,12 +248,14 @@ class StreamSettings extends React.Component {
onChange={this.updateClosedTimeout}
value={getTimeoutAmount(settings.closedTimeout)}
label={t('configure.closed_comments_label')}
disabled={!settings.autoCloseStream}
/>
<div className={styles.configTimeoutSelect}>
<SelectField
label="comments closed time window"
value={getTimeoutMeasure(settings.closedTimeout)}
onChange={this.updateClosedTimeoutMeasure}
readOnly={!settings.autoCloseStream}
>
<Option value={'hours'}>{t('configure.hours')}</Option>
<Option value={'days'}>{t('configure.days')}</Option>
@@ -6,19 +6,23 @@ import ConfigureCard from 'coral-framework/components/ConfigureCard';
const Wordlist = ({ suspectWords, bannedWords, onChangeWordlist }) => (
<div>
<ConfigureCard title={t('configure.banned_words_title')}>
<ConfigureCard collapsible title={t('configure.banned_words_title')}>
<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>
<ConfigureCard title={t('configure.suspect_word_title')}>
<ConfigureCard collapsible title={t('configure.suspect_word_title')}>
<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,13 +61,20 @@
line-height: 1.5;
font-weight: 300;
margin-bottom: 8px;
overflow-wrap: break-word;
word-break:break-word;
}
.body {
margin-top: 0px;
flex: 1;
color: black;
max-width: 500px;
/*
IE11 fix Next line was supposed to be:
max-width: 500px;
*/
padding-right: 20px;
/** IE11 fix end **/
font-weight: 300;
font-size: 16px;
overflow-wrap: break-word;
@@ -188,4 +195,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"
>
@@ -180,8 +183,7 @@ class Comment extends React.Component {
<div className={styles.sideActions}>
<IfHasLink text={comment.body}>
<span className={styles.hasLinks}>
{/* TODO: translate string */}
<Icon name="error_outline" /> Contains Link
<Icon name="error_outline" /> {t('common.contains_link')}
</span>
</IfHasLink>
<div className={`actions ${styles.actions}`}>
@@ -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>
);
@@ -50,7 +50,7 @@ class StoriesContainer extends Component {
const { updateAssetState } = this.props;
try {
updateAssetState(id, closeStream ? Date.now() : null);
await updateAssetState(id, closeStream ? Date.now() : null);
this.fetchAssets();
} catch (err) {
console.error(err);
+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 {
@@ -1,6 +1,7 @@
import React from 'react';
import QuestionBox from '../../../components/QuestionBox';
import DefaultQuestionBoxIcon from '../../../components/DefaultQuestionBoxIcon';
import PropTypes from 'prop-types';
import cn from 'classnames';
import styles from './QuestionBoxBuilder.css';
import { Icon } from 'coral-ui';
@@ -12,6 +13,7 @@ const icons = [{ default: DefaultIcon }, 'forum', 'build', 'format_quote'];
class QuestionBoxBuilder extends React.Component {
render() {
const {
title,
questionBoxIcon,
questionBoxContent,
onContentChange,
@@ -20,7 +22,7 @@ class QuestionBoxBuilder extends React.Component {
return (
<div className={styles.root}>
<h4>Include an Icon</h4>
<h4>{title}</h4>
<ul className={styles.iconList}>
{icons.map(item => {
@@ -53,4 +55,12 @@ class QuestionBoxBuilder extends React.Component {
}
}
QuestionBoxBuilder.propTypes = {
title: PropTypes.string,
questionBoxIcon: PropTypes.string,
questionBoxContent: PropTypes.string,
onContentChange: PropTypes.func,
onIconChange: PropTypes.func,
};
export default QuestionBoxBuilder;
@@ -69,6 +69,7 @@ class Settings extends React.Component {
{questionBoxEnable && (
<div className={styles.questionBoxContainer}>
<QuestionBoxBuilder
title={t('configure.include_an_icon')}
questionBoxIcon={questionBoxIcon}
questionBoxContent={questionBoxContent}
onIconChange={onQuestionBoxIconChange}
@@ -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} />
@@ -18,6 +18,11 @@
.comment {
position: relative;
padding-left: 15px;
display: flex;
}
.commentContainer {
width: 100%;
}
.commentLevel0 {
@@ -138,6 +143,7 @@
}
.commentAvatar {
margin-top: 10px;
max-width: 60px;
}
@@ -645,7 +645,7 @@ export default class Comment extends React.Component {
})}
onClick={this.onClickEdit}
>
Edit
{t('framework.edit')}
</a>
)}
</span>
@@ -38,5 +38,4 @@
position: relative;
margin-top: 28px;
padding-bottom: 50px;
min-height: 600px;
}
@@ -206,15 +206,30 @@ class Stream extends React.Component {
);
}
renderQuestionBox() {
const {
root,
asset,
asset: {
settings: { questionBoxEnable, questionBoxContent, questionBoxIcon },
},
} = this.props;
const slotPassthrough = { root, asset };
if (questionBoxEnable) {
return (
<QuestionBox content={questionBoxContent} icon={questionBoxIcon}>
<Slot fill="streamQuestionArea" passthrough={slotPassthrough} />
</QuestionBox>
);
}
}
render() {
const {
root,
appendItemArray,
asset,
asset: {
comment: highlightedComment,
settings: { questionBoxEnable },
},
asset: { comment: highlightedComment },
postComment,
notify,
updateItem,
@@ -260,14 +275,7 @@ class Stream extends React.Component {
content={asset.settings.infoBoxContent}
enable={asset.settings.infoBoxEnable}
/>
{questionBoxEnable && (
<QuestionBox
content={asset.settings.questionBoxContent}
icon={asset.settings.questionBoxIcon}
>
<Slot fill="streamQuestionArea" passthrough={slotPassthrough} />
</QuestionBox>
)}
{this.renderQuestionBox()}
{!banned &&
temporarilySuspended && (
<RestrictedMessageBox>
@@ -305,6 +313,7 @@ class Stream extends React.Component {
) : (
<Markdown content={asset.settings.disableCommentingMessage} />
)}
{this.renderQuestionBox()}
</div>
)}
+324
View File
@@ -0,0 +1,324 @@
import queryString from 'querystringify';
import URLSearchParams from '@ungap/url-search-params';
import pym from 'pym.js';
import EventEmitter from 'eventemitter2';
import { buildUrl } from 'coral-framework/utils/url';
import SnackBar from './SnackBar';
import onIntersect from './onIntersect';
import {
createStorage,
connectStorageToPym,
} from 'coral-framework/services/storage';
// Rebuild the origin if it isn't defined. This is our poor-mans polyfill
// for the location APIs.
if (!window.location.origin) {
window.location.origin = `${window.location.protocol}//${
window.location.hostname
}${window.location.port ? `:${window.location.port}` : ''}`;
}
const NOTIFICATION_OFFSET = 200;
// Ensure there is a trailing slash.
function ensureEndSlash(p) {
return p.match(/\/$/) ? p : `${p}/`;
}
// Build the URL to load in the pym iframe.
function buildStreamIframeUrl(talkBaseUrl, query) {
let url = talkBaseUrl + 'embed/stream?';
url += queryString.stringify(query);
return url;
}
// detectAssetURL will try to grab the canonical url from the head, if it isn't
// available, source the url from the current one.
function detectAssetURL() {
try {
// Try to get the url from the canonical tag on the page.
return document.querySelector('link[rel="canonical"]').href;
} catch (e) {
window.console.warn(
'This page does not include a canonical link tag. Talk has inferred this asset_url from the window object. Query params have been stripped, which may cause a single thread to be present across multiple pages.'
);
return window.location.origin + window.location.pathname;
}
}
function buildQuery({ asset_id, asset_url }) {
// Compose the query to send down to the Talk API so it knows what to load.
const query = {};
// Parse the url parameters to extract some of the information.
const search = new URLSearchParams(window.location.search);
// Pull the Comment ID out of the query string.
const commentID = search.get('commentId') || search.get('commentID');
if (commentID) {
query.comment_id = commentID;
}
// Insert the asset_id into the query.
if (asset_id) {
query.asset_id = asset_id;
}
// If the asset_url is defined, use it, otherwise, detect it.
if (asset_url) {
query.asset_url = asset_url;
} else {
query.asset_url = detectAssetURL();
}
return query;
}
// Get dimensions of viewport.
function viewportDimensions() {
let target = window;
let prefix = 'inner';
if (!('innerWidth' in window)) {
prefix = 'client';
target = document.documentElement || document.body;
}
return {
width: target[`${prefix}Width`],
height: target[`${prefix}Height`],
};
}
export default class Bridge {
constructor(
element,
{
// Pull out the URLs used to setup Talk.
talk: talkBaseUrl,
talkStaticUrl = talkBaseUrl,
// Default the following to null.
events = null,
snackBarStyles = null,
onAuthChanged = null,
// Determine if we're in lazy mode or not. By default, the build argument
// will determine the lazy render status. This default is primarily used
// when the embed code cannot be changed, but control of the Talk serving
// domain is available.
lazy = process.env.TALK_DEFAULT_LAZY_RENDER === 'TRUE',
// Any additional options are extracted to be sent to the embed via the
// pym bridge.
...opts
}
) {
this.pym = null;
this.element = element;
this.opts = opts;
this.query = buildQuery(this.opts);
this.emitter = new EventEmitter({ wildcard: true });
this.snackBar = new SnackBar(snackBarStyles || {});
this.onAuthChanged = onAuthChanged;
this.talkBaseUrl = ensureEndSlash(talkBaseUrl);
this.talkStaticUrl = ensureEndSlash(talkStaticUrl);
this.lazy = lazy;
// Store queued operations in a queue that can be processed once the stream
// is rendered.
this.queued = [];
// Attach to the events emitted by the pym parent.
if (events) {
events(this.emitter);
}
// Start the embed loading process.
if (this.lazy) {
// When the dom element containing the talk embed container is in view,
// render the stream with force turned on so that it skips this portion.
onIntersect(this.element, () => this.load());
} else {
// We aren't being lazy, load it now!
this.load();
}
}
ensureRendered() {
// Check to see if the pym bridge is created, and the embed is loaded.
if (this.pym === null) {
throw new Error('Stream Embed must be rendered first');
}
}
queueWhenRendered(callback) {
// Check to see if the queue is alive, if it isn't, run the callback now,
// otherwise, push the callback to be processed when the stream has loaded.
if (this.queued !== null) {
this.queued.push(callback);
} else {
callback();
}
}
setupPym() {
const url = buildStreamIframeUrl(this.talkBaseUrl, this.query);
this.pym = new pym.Parent(this.element.id, url, {
title: this.opts.title,
id: `${this.element.id}_iframe`,
name: `${this.element.id}_iframe`,
});
// NOTE: Workaround for iOS Safari which ignores `width` but respects `min-width` value.
this.pym.el.firstChild.style.width = '1px';
this.pym.el.firstChild.style.minWidth = '100%';
// Resize parent iframe height when child height changes
let cachedHeight;
this.pym.onMessage('height', height => {
if (height !== cachedHeight) {
this.pym.el.firstChild.style.height = `${height}px`;
cachedHeight = height;
}
});
// Send the config back over the pym bridge if requested.
this.pym.onMessage('getConfig', () => {
this.pym.sendMessage('config', JSON.stringify(this.opts));
});
// If the auth changes, and someone is listening for it, then re-emit it.
if (this.onAuthChanged) {
this.pym.onMessage('coral-auth-changed', message => {
this.onAuthChanged(message ? JSON.parse(message) : null);
});
}
// Remove the permalink comment id from the search.
this.pym.onMessage('coral-view-all-comments', () => {
const query = queryString.parse(location.search);
// Remove the commentId/commentID url param.
delete query.commentId;
delete query.commentID;
// Rebuild the search field without the commentId in it.
const search = queryString.stringify(query);
// Change the url.
window.history.replaceState(
{},
document.title,
buildUrl({ ...location, search })
);
});
// Remove the permalink comment id from the hash.
this.pym.onMessage('coral-view-comment', id => {
const search = queryString.stringify({
...queryString.parse(location.search),
commentId: id,
});
// Change the url to the permalink url.
window.history.replaceState(
{},
document.title,
buildUrl({ ...location, search })
);
});
// Helps child show notifications at the right scrollTop.
this.pym.onMessage('getPosition', () => {
const { height } = viewportDimensions();
let position = height + document.body.scrollTop;
if (position > NOTIFICATION_OFFSET) {
position = position - NOTIFICATION_OFFSET;
}
this.pym.sendMessage('position', position);
});
// When end-user clicks link in iframe, open it in parent context
this.pym.onMessage('navigate', url => {
// Open the new window, detach the opener, and focus on it.
const w = window.open(url, '_blank');
w.opener = null;
w.focus();
});
// Pass events from iframe to the event emitter.
this.pym.onMessage('event', raw => {
const { eventName, value } = JSON.parse(raw);
this.emitter.emit(eventName, value);
});
}
/**
* load will configure the pym parent if it hasn't already been, and setup the
* snackBar. If there are any queued operations, it will run them first.
*/
load() {
if (this.pym !== null) {
throw new Error('Stream Embed already rendered');
}
// Setup Pym.
this.setupPym();
// Attach the snackBar to the pym parent and to the body of the page.
this.snackBar.attach(window.document.body, this.pym);
// If the user clicks outside the embed, then tell the embed.
document.addEventListener('click', this.handleClick.bind(this), true);
// Listens to ${name}Storage requests on pym and relay it to
// ${name}Storage.
['local', 'session'].forEach(name => {
connectStorageToPym(
createStorage(`${name}Storage`),
this.pym,
`${name}Storage`
);
});
// Process any queued operations.
const queued = this.queued;
this.queued = null;
queued.forEach(callback => callback());
}
handleClick() {
this.pym.sendMessage('click');
}
enablePluginsDebug() {
this.pym.sendMessage('enablePluginsDebug');
}
disablePluginsDebug() {
this.pym.sendMessage('disablePluginsDebug');
}
login(token) {
this.pym.sendMessage('login', token);
}
logout() {
this.pym.sendMessage('logout');
}
remove() {
// Remove the event listeners.
document.removeEventListener('click', this.handleClick.bind(this));
this.emitter.removeAllListeners();
// Remove the snackbar.
this.snackBar.remove();
// Remove the pym parent.
this.pym.remove();
this.pym = null;
}
}
-195
View File
@@ -1,195 +0,0 @@
import queryString from 'query-string';
import pym from 'pym.js';
import EventEmitter from 'eventemitter2';
import { buildUrl } from 'coral-framework/utils/url';
import Snackbar from './Snackbar';
import {
createStorage,
connectStorageToPym,
} from 'coral-framework/services/storage';
const NOTIFICATION_OFFSET = 200;
// Build the URL to load in the pym iframe.
function buildStreamIframeUrl(talkBaseUrl, query) {
let url = [
talkBaseUrl,
talkBaseUrl.match(/\/$/) ? '' : '/', // make sure no double-'/' if opts.talk already ends with '/'
'embed/stream?',
].join('');
url += queryString.stringify(query);
return url;
}
// Get dimensions of viewport.
function viewportDimensions() {
let e = window,
a = 'inner';
if (!('innerWidth' in window)) {
a = 'client';
e = document.documentElement || document.body;
}
return {
width: e[`${a}Width`],
height: e[`${a}Height`],
};
}
export default class Stream {
constructor(el, talkBaseUrl, query, config) {
this.query = query;
// Extract the non-opts opts from the object.
const {
events = null,
snackBarStyles = null,
onAuthChanged = null,
...opts
} = config;
this.opts = opts;
this.emitter = new EventEmitter({ wildcard: true });
this.pym = new pym.Parent(el.id, buildStreamIframeUrl(talkBaseUrl, query), {
title: opts.title,
id: `${el.id}_iframe`,
name: `${el.id}_iframe`,
});
this.snackBar = new Snackbar(snackBarStyles || {});
// Workaround: IOS Safari ignores `width` but respects `min-width` value.
this.pym.el.firstChild.style.width = '1px';
this.pym.el.firstChild.style.minWidth = '100%';
// Resize parent iframe height when child height changes
let cachedHeight;
this.pym.onMessage('height', height => {
if (height !== cachedHeight) {
this.pym.el.firstChild.style.height = `${height}px`;
cachedHeight = height;
}
});
// Attach to the events emitted by the pym parent.
if (events) {
events(this.emitter);
}
this.pym.onMessage('getConfig', () => {
this.pym.sendMessage('config', JSON.stringify(opts));
});
// If the auth changes, and someone is listening for it, then re-emit it.
if (onAuthChanged) {
this.pym.onMessage('coral-auth-changed', message => {
onAuthChanged(message ? JSON.parse(message) : null);
});
}
// Attach the snackbar to the pym parent and to the body of the page.
this.snackBar.attach(window.document.body, this.pym);
// 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,
});
// Remove the commentId url param.
const url = buildUrl({ ...location, search });
// Change the url.
window.history.replaceState({}, document.title, url);
});
// Remove the permalink comment id from the hash.
this.pym.onMessage('coral-view-comment', id => {
const search = queryString.stringify({
...queryString.parse(location.search),
commentId: id,
});
// Remove the commentId url param.
const url = buildUrl({ ...location, search });
// Change the url.
window.history.replaceState({}, document.title, url);
});
// Helps child show notifications at the right scrollTop.
this.pym.onMessage('getPosition', () => {
const { height } = viewportDimensions();
let position = height + document.body.scrollTop;
if (position > NOTIFICATION_OFFSET) {
position = position - NOTIFICATION_OFFSET;
}
this.pym.sendMessage('position', position);
});
// When end-user clicks link in iframe, open it in parent context
this.pym.onMessage('navigate', url => {
window.open(url, '_blank').focus();
});
// Pass events from iframe to the event emitter.
this.pym.onMessage('event', raw => {
const { eventName, value } = JSON.parse(raw);
this.emitter.emit(eventName, value);
});
// If the user clicks outside the embed, then tell the embed.
document.addEventListener('click', this.handleClick.bind(this), true);
// Listens to local storage requests on pym and relay it to local storage.
connectStorageToPym(
createStorage('localStorage'),
this.pym,
'localStorage'
);
// Listens to session storage requests on pym and relay it to session storage.
connectStorageToPym(
createStorage('sessionStorage'),
this.pym,
'sessionStorage'
);
}
enablePluginsDebug() {
this.pym.sendMessage('enablePluginsDebug');
}
disablePluginsDebug() {
this.pym.sendMessage('disablePluginsDebug');
}
login(token) {
this.pym.sendMessage('login', token);
}
logout() {
this.pym.sendMessage('logout');
}
remove() {
// Remove the event listeners.
document.removeEventListener('click', this.handleClick.bind(this));
this.emitter.removeAllListeners();
// Remove the snackbar.
this.snackBar.remove();
// Remove the pym parent.
this.pym.remove();
}
handleClick() {
this.pym.sendMessage('click');
}
}
-33
View File
@@ -1,33 +0,0 @@
export default class StreamInterface {
constructor(stream) {
this._stream = stream;
}
on(eventName, callback) {
return this._stream.emitter.on(eventName, callback);
}
off(eventName, callback) {
return this._stream.emitter.off(eventName, callback);
}
login(token) {
return this._stream.login(token);
}
logout() {
return this._stream.logout();
}
remove() {
return this._stream.remove();
}
enablePluginsDebug() {
return this._stream.enablePluginsDebug();
}
disablePluginsDebug() {
return this._stream.disablePluginsDebug();
}
}
+55 -90
View File
@@ -1,106 +1,71 @@
import URLSearchParams from 'url-search-params';
import Stream from './Stream';
import StreamInterface from './StreamInterface';
// Polyfill IntersectionObserver always, the alternative is that we have to also
// polyfill for Promise, which itself adds 1KB gziped, which means that the
// 4KB that the intersection observer really doesn't take up that much in terms
// of size.
import 'intersection-observer';
// Rebuild the origin if it isn't defined. This is our poor-mans polyfill
// for the location API's.
if (!window.location.origin) {
window.location.origin = `${window.location.protocol}//${
window.location.hostname
}${window.location.port ? `:${window.location.port}` : ''}`;
}
import Bridge from './Bridge';
import wrapBridge from './wrapBridge';
// parses the Asset URL from the config variable
function parseAssetURL() {
try {
// Try to get the url from the canonical tag on the page.
return document.querySelector('link[rel="canonical"]').href;
} catch (e) {
window.console.warn(
'This page does not include a canonical link tag. Talk has inferred this asset_url from the window object. Query params have been stripped, which may cause a single thread to be present across multiple pages.'
/**
* validateElement will throw an error when the element is not configured
* correctly or is not an HTMLElement at all.
*
* @param {HTMLElement} element the HTMLElement where the stream will be rendered
*/
function validateElement(element) {
if (!element) {
throw new Error(
'Please provide Coral.Talk.render() the HTMLElement you want to render Talk in.'
);
}
return window.location.origin + window.location.pathname;
if (typeof element !== 'object') {
throw new Error(
`Coral.Talk.render() expected HTMLElement but got ${element} (${typeof element})`
);
}
// Ensure element has an id, as pym can't directly accept the HTMLElement.
if (!element.id) {
element.id = `_${Math.random()}`;
}
}
export class Talk {
/**
* validateConfig is the configuration validation tool.
*
* @param {Object} config the configuration that will be used to setup Talk.
*/
function validateConfig(config) {
if (!config || typeof config !== 'object' || !config.talk) {
throw new Error(
'Coral.Talk.render() expected configuration with at least opts.talk as the Talk Base URL, none found'
);
}
}
export const Talk = {
/**
* Render a Talk stream
* @param {HTMLElement} element - Element to render the stream in
* @param {Object} config - Configuration options for talk
* @param {String} config.talk - Talk base URL
* @param {String} [config.title] - Title of Stream (rendered in iframe)
* @param {String} [config.asset_url] - Asset URL
* @param {String} [config.asset_id] - Asset ID
* @param {String} config.talk - URL to the Talk installation
* @param {String} [config.asset_id] - (optional) ID for the Asset
* @param {String} [config.asset_url] - (optional) URL where the Asset is located
* @param {String} [config.auth_token] - (optional) A jwt representing the session
* @param {String} [config.lazy] - (optional) If set the stream will only render lazily
* @param {String} [config.talkStaticUrl] - (optional) Static URL used to serve Talk
* @return {Object}
*
* Example:
* ```
* const embed = Talk.render(document.getElementById('talkStreamEmbed'), config);
*
* // trigger a login with optional token.
* embed.login(token);
*
* // trigger a logout.
* embed.logout();
*
* // listen to events (in this case all events).
* embed.on('**', function(value) {
* console.log(this.event, value);
* });
* ```
*/
static render(element, config) {
if (!element) {
throw new Error(
'Please provide Coral.Talk.render() the HTMLElement you want to render Talk in.'
);
}
if (typeof element !== 'object') {
throw new Error(
`Coral.Talk.render() expected HTMLElement but got ${element} (${typeof element})`
);
}
if (!config || typeof config !== 'object' || !config.talk) {
throw new Error(
'Coral.Talk.render() expected configuration with at least opts.talk as the Talk Base URL, none found'
);
}
render: (element, config) => {
// Validate the element.
validateElement(element);
// Ensure el has an id, as pym can't directly accept the HTMLElement.
if (!element.id) {
element.id = `_${Math.random()}`;
}
// Validate the configuration.
validateConfig(config);
// Compose the query to send down to the Talk API so it knows what to load.
const query = {};
// Parse the url parameters to extract some of the information.
const search = new URLSearchParams(window.location.search);
// Pull the commentID out from the query params.
const commentID = search.get('commentId') || search.get('commentID');
if (commentID) {
query.comment_id = commentID;
}
// Extract the asset id from the options.
if (config.asset_id) {
query.asset_id = config.asset_id;
}
// Parse the Asset URL.
query.asset_url = config.asset_url;
if (!query.asset_url) {
query.asset_url = parseAssetURL();
}
// Create the new Stream.
const stream = new Stream(element, config.talk, query, config);
// Return the public interface for the stream.
return new StreamInterface(stream);
}
}
// Create the new Bridge, and wrap it up.
return wrapBridge(new Bridge(element, config));
},
};
+30
View File
@@ -0,0 +1,30 @@
export default function onIntersect(el, callback) {
// Ensure that the intersection observer is available.
if (!IntersectionObserver) {
// tslint:disable-next-line:no-console
window.console.warn('IntersectionObserver not available, rendering now');
callback();
return;
}
// Create the Intersection Observer that will wait till the embed is within
// view and will then call the callback.
const observer = new IntersectionObserver(
entries => {
if (entries[0].isIntersecting) {
// Stop receiving intersection events.
observer.disconnect();
// Fire the callback.
callback();
}
},
{
rootMargin: '100px',
threshold: 1.0,
}
);
// Start observing the element for visibility.
observer.observe(el);
}
+31
View File
@@ -0,0 +1,31 @@
export default bridge => {
// whenRendered will call the callback when the stream has rendered (or now if
// the stream is already rendered).
const whenRendered = callback => bridge.queueWhenRendered(callback);
// onlyWhenRendered will guard the callback unless the stream is rendered.
const onlyWhenRendered = callback => {
bridge.ensureRendered();
callback();
};
// Return the limited stream interface.
return {
// Directly map the on/off from the event emitter to the stream.
on: (eventName, callback) => bridge.emitter.on(eventName, callback),
off: (eventName, callback) => bridge.emitter.off(eventName, callback),
// Queue up the login operation until the stream has been rendered.
login: token => whenRendered(() => bridge.login(token)),
// Queue up the logout operation until the stream has been rendered.
logout: () => whenRendered(() => bridge.logout()),
// Remove the stream if it's already been rendered.
remove: () => onlyWhenRendered(() => bridge.remove()),
// Queue up the plugin config until the embed has rendered.
enablePluginsDebug: () => whenRendered(() => bridge.enablePluginsDebug()),
disablePluginsDebug: () => whenRendered(() => bridge.disablePluginsDebug()),
};
};
+20 -16
View File
@@ -1,10 +1,5 @@
import * as actions from '../constants/auth';
import jwtDecode from 'jwt-decode';
function cleanAuthData(localStorage) {
localStorage.removeItem('token');
localStorage.removeItem('exp');
}
import { setStorageAuthToken, clearStorageAuthToken } from '../services/auth';
export const checkLogin = () => (
dispatch,
@@ -15,7 +10,7 @@ export const checkLogin = () => (
rest('/auth')
.then(result => {
if (!result.user) {
cleanAuthData(localStorage);
clearStorageAuthToken(localStorage);
dispatch(checkLoginSuccess(null));
client.resetWebsocket();
return;
@@ -23,12 +18,16 @@ export const checkLogin = () => (
dispatch(checkLoginSuccess(result.user));
pym.sendMessage('coral-auth-changed', JSON.stringify(result.user));
client.resetWebsocket();
// 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);
clearStorageAuthToken(localStorage);
client.resetWebsocket();
} else {
console.error(error);
@@ -49,14 +48,20 @@ const checkLoginSuccess = user => ({
user,
});
export const setAuthToken = token => (dispatch, _, { localStorage }) => {
localStorage.setItem('exp', jwtDecode(token).exp);
localStorage.setItem('token', token);
export const setAuthToken = token => (
dispatch,
_,
{ localStorage, client }
) => {
setStorageAuthToken(localStorage, token);
// Dispatch the set auth token action. For some browsers and situations, we
// 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());
};
@@ -65,9 +70,7 @@ export const handleSuccessfulLogin = (user, token) => (
_,
{ client, localStorage, postMessage }
) => {
const { exp } = jwtDecode(token);
localStorage.setItem('exp', exp);
localStorage.setItem('token', token);
setStorageAuthToken(localStorage, token);
// Send the message via the messages service to the window.opener if it
// exists.
@@ -79,6 +82,7 @@ export const handleSuccessfulLogin = (user, token) => (
);
}
// Now that we just set a token, set the token!
client.resetWebsocket();
dispatch({
@@ -105,7 +109,7 @@ export const logout = () => async (
}
// Clear the auth data persisted to localStorage.
cleanAuthData(localStorage);
clearStorageAuthToken(localStorage);
// Reset the websocket.
client.resetWebsocket();
@@ -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 = {
@@ -1,50 +1,48 @@
.card {
margin-bottom: 20px;
align-items: flex-start;
min-height: 100px;
max-width: 600px;
min-height: 100px;
flex-direction: row;
align-items: flex-start;
margin-bottom: 20px;
overflow: visible;
}
.collapsibleCard {
min-height: auto;
}
.enabledCard {
border-left: 7px solid #00796b;
}
.action {
flex-shrink: 0;
margin-right: 12px;
}
.wrapper {
flex-grow: 1;
}
.header {
margin-top: 3px;
margin-bottom: 7px;
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 2px;
}
.title {
font-size: 18px;
font-weight: 500;
}
.wrapper {
width: 100%;
.body {
margin-top: 7px;
font-size: 14px;
letter-spacing: 0;
}
.action {
display: inline-block;
position: absolute;
top: 0;
left: 0;
padding: 20px;
}
.content {
display: inline-block;
padding: 0px 30px;
box-sizing: border-box;
}
.enabledSetting {
border-left-color: #00796b;
border-left-style: solid;
border-left-width: 7px;
}
.disabledSetting {
padding-left: 22px;
}
.disabledSettingText {
.disabledBody {
color: #ccc;
pointer-events: none;
}
@@ -1,46 +1,69 @@
import React from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import styles from './ConfigureCard.css';
import classnames from 'classnames/bind';
import { Card } from 'coral-ui';
import { Checkbox } from 'react-mdl';
import cn from 'classnames';
import { Checkbox, IconButton } from 'react-mdl';
const ConfigureCard = ({
title,
children,
className,
onCheckbox,
checked,
...rest
}) => (
<Card
{...rest}
className={cn(styles.card, className, {
[styles.enabledSetting]: checked === true,
[styles.disabledSetting]: checked === false,
})}
>
{checked !== undefined && (
<div className={styles.action}>
<Checkbox onChange={onCheckbox} checked={checked} />
</div>
)}
<div
className={cn(styles.wrapper, {
[styles.content]: checked !== undefined,
})}
>
<div className={styles.header}>{title}</div>
<div
className={cn({
[styles.disabledSettingText]: checked === false,
import styles from './ConfigureCard.css';
const cn = classnames.bind(styles);
class ConfigureCard extends PureComponent {
state = {
isOpen: !this.props.collapsible,
};
toggle = () => this.setState({ isOpen: !this.state.isOpen });
render() {
const {
title,
children,
className,
onCheckbox,
checked,
collapsible,
...rest
} = this.props;
const { isOpen } = this.state;
const iconName = isOpen ? 'keyboard_arrow_up' : 'keyboard_arrow_down';
return (
<Card
{...rest}
className={cn(styles.card, className, {
enabledCard: checked === true,
collapsibleCard: collapsible,
})}
>
{children}
</div>
</div>
</Card>
);
{checked !== undefined && (
<div className={styles.action}>
<Checkbox onChange={onCheckbox} checked={checked} />
</div>
)}
<div className={styles.wrapper}>
<div className={styles.header}>
<div className={styles.title}>{title}</div>
{collapsible && (
<IconButton onClick={this.toggle} name={iconName} ripple />
)}
</div>
{isOpen && (
<div
className={cn(styles.body, {
disabledBody: checked === false,
})}
>
{children}
</div>
)}
</div>
</Card>
);
}
}
ConfigureCard.propTypes = {
title: PropTypes.string,
@@ -48,6 +71,7 @@ ConfigureCard.propTypes = {
onCheckbox: PropTypes.func,
checked: PropTypes.bool,
children: PropTypes.node,
collapsible: PropTypes.bool,
};
export default ConfigureCard;
@@ -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;
+11
View File
@@ -0,0 +1,11 @@
import jwtDecode from 'jwt-decode';
export const setStorageAuthToken = (storage, token) => {
storage.setItem('exp', jwtDecode(token).exp);
storage.setItem('token', token);
};
export const clearStorageAuthToken = storage => {
storage.removeItem('token');
storage.removeItem('exp');
};
+26 -2
View File
@@ -22,6 +22,7 @@ import {
} from 'coral-framework/services/storage';
import { createHistory } from 'coral-framework/services/history';
import { createIntrospection } from 'coral-framework/services/introspection';
import { setStorageAuthToken } from 'coral-framework/services/auth';
import introspectionData from 'coral-framework/graphql/introspection.json';
import coreReducers from '../reducers';
import { checkLogin as checkLoginAction } from '../actions/auth';
@@ -46,7 +47,30 @@ const getAuthToken = (store, storage) => {
// 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) {
} else if (location.hash && location.hash.startsWith('#access_token=')) {
// Check to see if the access token is living in the URL as a hash.
const token = location.hash.substring(14);
history.replaceState(
{},
document.title,
window.location.pathname + window.location.search
);
// Once we clear the hash above, this login method will not persist across
// refreshes. We will need to persist the token to storage if it's
// available.
if (storage) {
setStorageAuthToken(storage, token);
}
return token;
} else if (
!bowser.safari &&
!bowser.ios &&
storage &&
storage.getItem('token')
) {
// Use local storage auth tokens where there's a stable api.
return storage.getItem('token');
}
@@ -136,7 +160,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;
+9 -4
View File
@@ -12,6 +12,7 @@ import 'moment/locale/da';
import 'moment/locale/de';
import 'moment/locale/es';
import 'moment/locale/fr';
import 'moment/locale/it';
import 'moment/locale/nl';
import 'moment/locale/pt-br';
@@ -22,6 +23,7 @@ import daTA from 'timeago.js/locales/da';
import deTA from 'timeago.js/locales/de';
import esTA from 'timeago.js/locales/es';
import frTA from 'timeago.js/locales/fr';
import itTA from 'timeago.js/locales/it';
import nlTA from 'timeago.js/locales/nl';
import pt_BRTA from 'timeago.js/locales/pt_BR';
import zh_CNTA from 'timeago.js/locales/zh_CN';
@@ -34,6 +36,7 @@ import da from '../../../locales/da.yml';
import de from '../../../locales/de.yml';
import es from '../../../locales/es.yml';
import fr from '../../../locales/fr.yml';
import it from '../../../locales/it.yml';
import nl_NL from '../../../locales/nl_NL.yml';
import pt_BR from '../../../locales/pt_BR.yml';
import zh_CN from '../../../locales/zh_CN.yml';
@@ -61,6 +64,7 @@ export const translations = {
...de,
...es,
...fr,
...it,
...nl_NL,
...pt_BR,
...zh_CN,
@@ -106,10 +110,11 @@ 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('it', itTA);
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;
}
+20 -1
View File
@@ -30,7 +30,7 @@ const CONFIG = {
ENABLE_TRACING: Boolean(process.env.APOLLO_ENGINE_KEY),
// EMAIL_SUBJECT_PREFIX is the string before emails in the subject.
EMAIL_SUBJECT_PREFIX: process.env.TALK_EMAIL_SUBJECT_PREFIX || '[Talk]',
EMAIL_SUBJECT_PREFIX: process.env.TALK_EMAIL_SUBJECT_PREFIX,
// DEFAULT_LANG is the default language used for server sent emails and
// rendered text.
@@ -76,6 +76,10 @@ const CONFIG = {
// on the scraper when it makes requests.
SCRAPER_HEADERS: process.env.TALK_SCRAPER_HEADERS || '{}',
// HTTP_X_REQUEST_ID is a string which represents the request header where we
// should source the request ID from, otherwise, a new one will be generated.
HTTP_X_REQUEST_ID: process.env.TALK_HTTP_X_REQUEST_ID || null,
//------------------------------------------------------------------------------
// JWT based configuration
//------------------------------------------------------------------------------
@@ -213,6 +217,9 @@ const CONFIG = {
// messages through the websocket to keep the socket alive.
KEEP_ALIVE: process.env.TALK_KEEP_ALIVE || '30s',
// CONCURRENCY is the number of workers that will serve traffic.
CONCURRENCY: parseInt(process.env.TALK_CONCURRENCY || '1'),
//------------------------------------------------------------------------------
// Cache configuration
//------------------------------------------------------------------------------
@@ -220,6 +227,14 @@ const CONFIG = {
CACHE_EXPIRY_COMMENT_COUNT:
process.env.TALK_CACHE_EXPIRY_COMMENT_COUNT || '1hr',
// EMBED_EXPIRY_TIME is the time that the embed will be cacheable for, sent as
// the max-age= directive on the Cache-Control header.
EMBED_EXPIRY_TIME: ms(process.env.TALK_EMBED_EXPIRY || '24hr'),
// EMBED_EXPIRY_TIME is the time that the rest of the static files will be
// cacheable for, sent as the max-age= directive on the Cache-Control header.
STATIC_EXPIRY_TIME: ms(process.env.TALK_STATIC_EXPIRY || '1w'),
//------------------------------------------------------------------------------
// Recaptcha configuration
//------------------------------------------------------------------------------
@@ -271,6 +286,10 @@ const CONFIG = {
// CONFIG VALIDATION
//==============================================================================
if (typeof CONFIG.EMAIL_SUBJECT_PREFIX === 'undefined') {
CONFIG.EMAIL_SUBJECT_PREFIX = '[Talk]';
}
if (process.env.NODE_ENV === 'test') {
if (!CONFIG.ROOT_URL) {
CONFIG.ROOT_URL = `http://${localAddress}:3001`;
+6 -2
View File
@@ -98,12 +98,16 @@ sidebar:
url: /configuration/
- title: Advanced Configuration
url: /advanced-configuration/
- title: Configuration with CLI tools
url: /configuration-cli-tools/
- title: Integrating
children:
- title: Authentication
url: /integrating/authentication/
- title: Asset Management
url: /integrating/asset-management/
- title: CMS Integration
url: /integrating/cms-integration/
- title: Asset Scraping
url: /integrating/asset-scraping/
- title: Configuring the Comment Stream
url: /integrating/configuring-comment-stream/
- title: Configuring the Admin
+2 -1
View File
@@ -1,3 +1,4 @@
/ /talk/
/talk/reference/server/ /talk/api/server/
/talk/reference/graphql/ /talk/api/graphql/
/talk/reference/graphql/ /talk/api/graphql/
/talk/integrating/asset-management/ /talk/integrating/cms-integration/
+40 -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,37 @@ 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
@@ -220,6 +222,17 @@ Once you have added the domain of these docs, you can click the button below.
<div class="mount"></div>
</div>
### Developer Endpoints
With your local instance of Talk running in development mode (env variable `NODE_ENV=development`) you should now also be able to access the following developer routes:
* [http://127.0.0.1:3000/dev](http://127.0.0.1:3000/dev]) provides a sample comment stream
* [http://127.0.0.1:3000/dev/assets](http://127.0.0.1:3000/dev/assets]) provides a list of all stories in Talk and can generate new sample assets
### Conclusion
At this point you've successfully installed, configured, and ran your very own
instance of Talk! Continue through this documentation on this site to learn more
on how to configure, develop with, and contribute to Talk!
@@ -70,5 +70,7 @@ You can now start the application by running:
yarn watch:server
```
If you are developing a custom plugin you can use `yarn watch:client` or `yarn watch` to run both client and server.
At this stage, you should refer to the [configuration](/talk/configuration/) for
configuration variables that are specific to your installation.
+3 -1
View File
@@ -23,11 +23,13 @@ permalink: /pre-launch-checklist/
- [ ] Do you need to migrate comments from a legacy system? We currently support Disqus, Livefyre, and Civil Comments.
- Use the [Talk Import](https://github.com/coralproject/talk-importer) framework
- [ ] Do you want to provide single sign-on (SSO) by integrating with an external auth system?
- See [Authenticating with Talk](/talk/integrating/authentication/)
- [ ] Do you want to integrate Talk with your CMS to automate embedding Talk Comment Stream into your site?
- See [CMS Integration](/talk/integrating/cms-integration/)
- [ ] Do you want to use Social sign-on?
- Facebook
+15 -2
View File
@@ -246,6 +246,21 @@ Refer to the documentation for [TALK_JWT_ALG](#talk-jwt-alg) for other signing
methods and other forms of the `TALK_JWT_SECRET`. If you are interested in using
multiple keys, then refer to [TALK_JWT_SECRETS](#talk-jwt-secrets).
You can also encode your secret as a base64 string (if you are using a symmetric
algorithm) as long as you prefix it with `base64:`. For example:
```plain
TALK_JWT_SECRET={"secret": "base64:dGVzdA=="}
```
Would be the same as:
```plain
TALK_JWT_SECRET={"secret": "test"}
```
As `dGVzdA==` is just `test` encoded using base64.
## TALK_JWT_SECRETS
Used when specifying multiple secrets used for key rotations. This is a JSON
@@ -271,7 +286,6 @@ Note that the secret is stored in a JSON object, keyed by `secret`. This is only
needed when specifying in the multiple secrets for `TALK_JWT_SECRETS`, but may
be used to specify the single [TALK_JWT_SECRET](#talk-jwt-secret).
When the value of [TALK_JWT_ALG](#talk-jwt-alg) is **not** a `HS*` value, then
the value of the `TALK_JWT_SECRETS` should take the form:
@@ -282,7 +296,6 @@ TALK_JWT_SECRETS=[{"kid": "1", "private": "<my private key>", "public": "<my pub
Refer to the documentation on the [TALK_JWT_ALG](#talk-jwt-alg) for more
information on what to store in these parameters.
## TALK_JWT_SIGNING_COOKIE_NAME
The default cookie name that is use to set a cookie containing a JWT that was
+181
View File
@@ -0,0 +1,181 @@
---
title: Configuration with CLI tools
permalink: /configuration-cli-tools/
class: configuration
toc: true
---
Talk can be configured using CLI tools. The CLI tools are available through `./bin/cli`. Below is a list of commands available:
```
Usage: cli [options] [command]
Options:
-h, --help output usage information
Commands:
serve serve the application
db run database commands
settings interact with the application settings
assets interact with assets
setup setup the application
jobs work with the job queues
token work with the access tokens
users work with the application auth
migration provides utilities for migrating the database
plugins provides utilities for interacting with the plugin system
help [cmd] display help for [cmd]
```
## Serving the application
To serve the application, run `./bin/cli serve`. Please check that you have installed the Talk before doing this step. Below is a list of additional options available for this command:
```
Usage: cli-serve [options]
Options:
-j, --jobs enable job processing on this thread
--disabled-jobs <jobs> disable jobs specified if the -j option is passed, specified as a comma separated list (default: )
-w, --websockets enable the websocket (subscriptions) handler on this thread
-h, --help output usage information
```
## Database commands
You can also run database commands using `./bin/cli db`. Below is a list of additional options available for this command:
```
Usage: cli-db [options] [command]
Options:
-h, --help output usage information
Commands:
createIndexes creates the database indexes and waits until they are created
```
## Application settings
You can also change application settings using `./bin/cli settings`. Below is a list of additional options available for this command:
```
Usage: cli-settings [options] [command]
Options:
-h, --help output usage information
Commands:
change-org-name change the organization name
```
## Interaction with assets
You can also interact with your application's assets using `./bin/cli assets`. Below is a list of additional options available for this command:
```
Usage: cli-assets [options] [command]
Options:
-h, --help output usage information
Commands:
list [options] list all the assets in the database
refresh [age] queues the assets that exceed the age requested
update-url <assetID> <url> update the URL of an asset
merge <srcID> <dstID> merges two assets together by moving comments from src to dst and deleting the src asset
rewrite [options] <search> <replace> rewrites asset url's using the provided regex replacement pattern
```
## Setting up the application
You can also run a setup wizard to setup the wizard using `./bin/cli setup`. Below is a list of additional options available for this command:
```
Usage: cli-setup [options]
runs the setup wizard to setup the application
Options:
--defaults apply defaults for config instead of prompting
-h, --help output usage information
```
## Working with job queues
You can also work with job queues using `./bin/cli jobs`. Below is a list of additional options available for this command:
```
Usage: cli-jobs [options] [command]
Options:
-h, --help output usage information
Commands:
process starts job processing
cleanup [options] cleans up inactive jobs
```
## Working with access tokens
You can also work with access tokens using `./bin/cli token`. Below is a list of additional options available for this command:
```
Usage: cli-token [options] [command]
Options:
-h, --help output usage information
Commands:
list <userID> list tokens for a user
revoke <tokenID> revokes a token with a given id
create <userID> <tokenName> create a token for a user with a given name
```
## Working with your application's authentication
You can also work with your application's authentication using `./bin/cli users`. Below is a list of additional options available for this command:
```
Usage: cli-users [options] [command]
Options:
-h, --help output usage information
Commands:
create creates a local user
delete <userID> delete a user
list searches for a user based on their stored username and email
set-role <userID> sets the role on a user
verify <userID> <email> verifies the given user's email address
```
## Migrating your database
Talk provides utilities for migrating your database using `./bin/cli migration`. Below is a list of additional options available for this command:
```
Usage: cli-migration [options] [command]
Options:
-h, --help output usage information
Commands:
create <name> creates a new migration
run [options] runs all pending migrations
```
## Interacting with the plugin system
Talk provides utilities to interact with the plugin system using `./bin/cli plugins`. Below is a list of additional options available for this command:
```
Usage: cli-plugins [options] [command]
Options:
-h, --help output usage information
Commands:
create creates a seed plugin
list
reconcile [options] reconciles dependencies by downloading external plugins
```
@@ -5,13 +5,15 @@ permalink: /commenter-features/
## Signing up for Talk
There are 2 ways that newsrooms can support signup/login functionality with Talk:
There are 3 ways that newsrooms can support signup/login functionality with Talk:
* Use Talks auth plugin out of the box (supports account registration with username and password, as well as features like forgot password)
* Use 3rd party authentication provider such as FaceBook or Google. We provide plugins that support logging in with either [Facebook](/talk/plugin/talk-plugin-facebook-auth/)
or [Google](/talk/plugin/talk-plugin-google-auth/). (Note: you must provide your own Facebook App ID and Secret, which you can read more about here: [https://developers.facebook.com](https://developers.facebook.com))
* Create their own auth plugin to integrate with your own auth systems
We also provide a Facebook auth plugin that supports logging in with Facebook (you must provide your own Facebook App ID and Secret, which you can read more about here: [https://developers.facebook.com](https://developers.facebook.com))
## Comments and Replies
@@ -5,7 +5,7 @@ permalink: /moderator-features/
## The Talk Admin
The Admin is your moderators will moderate your comments, and your Admins will
The Admin is where your moderators will moderate your comments, and your Admins will
configure and manage the different parts of Talk.
### Moderate
@@ -18,57 +18,99 @@ site-wide.
**New**
The New queue contains all comments that have not been moderated yet.
>_Conditions:_ Pre-Mod disabled
**Reported**
Comments have been published? _**Yes**_
The Reported queue contains all comments that need moderator attention.
The New queue contains all comments that have not been moderated yet. This queue contains comments that havent yet been Accepted or Rejected by a moderator
**Approved**
**Pre-mod**
The Approved queue contains all approved comments.
>_Conditions:_ Pre-Mod enabled
Comments have been published? _**No**_
This queue contains comments that havent yet been Accepted or Rejected by a moderator
**Accepted**
Comments have been published? _**Yes**_
Comments that have been approved by a moderator, written by a Staff member (and so auto approved), or Featured (and so auto approved). Tags for reports still appear on comments in this queue, but the comments will not go back into Reported.
**Rejected**
The Rejected queue contains all comments that have been rejected, either
manually by moderators or automatically, e.g. they have used a banned word.
Comments have been published? _**No**_
Comments that have been rejected by a moderator, or rejected because they contained a banned word/phrase. Tags for reports still appear on comments in this queue, but the comments will not go back into Reported.
**Reported**
>_Conditions:_ Pre-Mod disabled
Comments have been published? _**depends**_
This queue contains comments that have been reported for moderator review, either by users or automatically by Talk, and have not yet been Approved or Rejected. If there are comments to review in this queue, a red dot will be displayed in the top menu.
**All**
The All queue contains all comments that have been submitted either article or
site-wide.
#### Moderation Badges
#### Moderation Tags that appear in Reported/other queues
**Pre-mod**
The Pre-mod badge signifies comments that are being pre-modded.
_Comment published? No_
The Pre-mod tag signifies comments that are being pre-modded.
**User**
The User badge signifies comments that have been reported by another user.
_Comment published? Yes_
**History**
Comment has been reported by a user. The More Details box contains the name/s of the users who reported the comment, and any additional information they provided.
**Staff**
_Comment published? Yes_
Comment has been reported by a staff member. The More Details box contains the name/s of the staff member who reported the comment, and any additional information they provided.
**Suspect Word**
_Comment published? Yes_
Comment contains a word or phrase that is on the Suspect words list in the Configure > Moderation tab (administrators only). The word/phrase will be highlighted in yellow.
**Karma**
_Comment published? No_
The History tag signifies comments that have been flagged because the users history indicates their Accepted:Rejected ratio has reached below the karma threshold. [Read more about karma here.](/talk/trust/#user-karma-score)
**Spam**
_Comment published? No_
If you are using the Akismet anti-spam plugin, this tag means that Akismet has flagged the comment as spam. You must Approve it for it to appear in the stream. **Requires:** [talk-plugin-akismet](/talk/plugin/talk-plugin-akismet/)
The History badge signifies comments that have been flagged because of a users
history.
**Toxic**
The Toxic badge signifies comments that are above the set Toxicity Probability
Threshold. Note you must have [talk-plugin-toxic-comments](/talk/additional-plugins/#talk-plugin-toxic-comments) enabled.
[Read more about Toxic Comments here](/talk/toxic-comments/).
_Comment published? No_
**Suspect**
If you are using the Toxic Comments plugin, this tag means that Perspective API has flagged the comment as likely to be toxic, above the threshold you have set (default: 80%). You must Approve it for it to appear in the stream. **Requires:** [talk-plugin-toxic-comments](/talk/additional-plugins/#talk-plugin-toxic-comments).
Read more about [Toxic Comments here](/talk/toxic-comments/).
The Suspect badge signifies comments that contain a Suspect Word.
**Contains Link**
The Contains Link badge signifies a comment that contains a link, which can
The Contains Link tag signifies a comment that contains a link, which can
sometimes mean it is a spam or ad comment.
**Flag Details View**
**More Details View**
At the bottom of each comment in the moderation queues, you can see more
information about a comments flags by clicking on More Detail.
@@ -88,9 +130,35 @@ Rejecting a comment removes the comment from the stream.
Featuring a comment adds that comment to the Featured Comments tab on the
stream.
#### Keyboard Shortcuts
Talk also supports a number of keyboard shortcuts that moderators can leverage
to moderate queues quickly:
| Shortcut | Action |
| -------- | -------------------------- |
| `j` | Go to the next comment |
| `k` | Go to the previous comment |
| `ctrl+f` | Open search |
| `1-5` | Switch queues |
| `z` | Zen mode |
| `?` | Open shortcuts menu |
| `d` | Approve |
| `f` | Reject |
Note: "Zen mode" allows a moderator to view and action only one comment at a time. Enjoy the silence!
#### The User Drawer
In order to get an idea of what sort of a commenter someone is, moderators can
click on the commenter's username in any moderation queue/elsewhere in the admin (eg the Community page) to see details about
their comments, and to take actions.
#### User Drawer Actions
**Suspend User**
Suspending a user allows a moderator to give a commenter a “time-out”; during
Suspending a user allows a moderator to give a commenter a “time-out” for a set time chosen from four options; during
that time they wont be allowed to post comments or react to comments.
**Ban User**
@@ -99,15 +167,13 @@ Banning a user allows a moderator to permanently disallow a commenter to
interact with their community. The commenters previous comments will remain on
the site. This action can only be un-done manually by a moderator.
#### Viewing a Users Comment History
**Reject Username**
In order to get an idea of what sort of a commenter someone is, moderators can
click on the commenters username in any moderation queue and see details about
their history.
If a username is violating your guidelines but isn't bad enough to lead to a ban (eg. they are impersonating a famous person), you can reject their username. This will prevent them from publishing comments until they submit a new username. The new name will appear on the Community page for moderator approval before they can publish comments again.
**Username, Email and Member Since Date**
This shows the basic details about a commenter.
This shows the basic details about a commenter's account
**Total Comments**
@@ -119,32 +185,17 @@ display on the site.
This shows the % of comments a commenter has had rejected by moderators, or
automatically.
**Reports**
**Karma score**
This shows if a commenter is a reliable flagger, an unreliable flagger, or a
neutral flagger. [Read more about reliable and unreliable flaggers here](/talk/trust/#reliable-and-unreliable-flaggers).
The user's current score. To see the default threshhold, click on the 'i' next to the word Karma. If the user's score is equal to or below the default threshhold, the number will be highlighted red. [Read more about karma scores here.](/talk/trust/#user-karma-score)
**User History**
This shows the history of the account - if a user has changed their username, or been suspended or banned. The name of the moderator who suspended/banned them/lifted the suspection or ban is displayed here as well.
**Moderating from this View**
Talk also allows you to moderate a commenters recent comments from this view.
#### Keyboard Shortcuts
Talk also supports a number of keyboard shortcuts that moderators can leverage
to moderate quickly:
| Shortcut | Action |
| -------- | -------------------------- |
| `j` | Go to the next comment |
| `k` | Go to the previous comment |
| `ctrl+f` | Open search |
| `t` | Switch queues |
| `z` | Zen mode |
| `?` | Open this menu |
| `d` | Approve |
| `f` | Reject |
Note: "Zen mode" allows a moderator to view and action only one comment at a time. Enjoy the silence!
Talk also allows you to moderate a commenters recent comments from this view. You can 'Select all' to select up to ten previous comments, and take bulk accept/reject actions.
### Stories
+1 -27
View File
@@ -7,7 +7,7 @@ Trust is a set of components within Talk that incorporate basic automated modera
## User Karma Score
Using Trusts calculations, Talk will automatically hold back, move to the Reported queue, and tag with a 'History' marker, any comments by users who have an Unreliable karma score. (This is for sites who practice post-moderation. If you set pre-moderation of all comments sitewide, this feature has limited use.)
Using Trusts calculations, Talk will automatically hold back, move to the Reported queue, and tag with a 'Karma' marker, any comments by users who have an Unreliable karma score. (This is for sites who practice post-moderation. If you set pre-moderation of all comments sitewide, this feature has limited use.)
All users start out with a Neutral karma score (`0`). If they have a comment approved by a moderator, their score increases by `1`; if they have a comment rejected by a moderator, it decreases by `1`. When a commenter's score is labeled as Unreliable, their comments must be approved from the Reported queue before they are posted. Commenters are shown a message stating that a moderator will review their comment shortly.
@@ -27,29 +27,3 @@ We strongly recommend not telling your community how this system works, or where
If you see that a high proportion of first-time commenters on your site are abusive, you might want to change the threshhold to `0`, at least temporarily. You can configure your own Trust thresholds by using [TRUST_THRESHOLDS](/talk/advanced-configuration/#trust-thresholds) in your site configuration.
## Reliable and Unreliable Flaggers
Trust also calculates how reliable users are in terms of the comments they
report. This information is displayed to moderators in the User History drawer,
which is accessed by clicking on a users name in the Admin. Currently, no other action is taken based on this score.
If a user's reports mostly match what moderators reject, their Report status
will display to moderators as Reliable in the user information drawer. If a
user's reports mostly differ from what moderators reject, their Report status
will show as Unreliable.
If Talk doesn't have enough reports to make a call, or the reports even out, their
status is Neutral.
Here are the default thresholds:
```text
-1 and lower: Unreliable
0 to +1: Neutral
+2 and higher: Reliable
```
You can configure your own Trust thresholds by using [TRUST_THRESHOLDS](/talk/advanced-configuration/#trust-thresholds) in your
configuration.
Note: Report Karma doesn't include reports of "I don't agree with this comment".
+1 -1
View File
@@ -15,7 +15,7 @@ Even if GDPR will not apply to you, it is recommended to enable these
features as a best practice to provide your users with control over their own
data.
## GPDR Feature Overview
## GDPR Feature Overview
Integrating our GDPR tools will give your users and organizations the following benefits:
+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
+44
View File
@@ -0,0 +1,44 @@
---
title: Asset Scraping
permalink: /integrating/asset-scraping/
---
By default, Assets in Talk have their metadata scraped when they are loaded.
This provides the easiest way for newsrooms to integrate their CMS's into Talk
in a simple way. We use the following
[meta tags](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) on
the target pages that allow us to extract some properties.
| Asset Property | Selector |
|--------------------|----------|
| `title` | See [`metascraper-title`](https://github.com/microlinkhq/metascraper/blob/dc664c37ea1b238b1e3e9d5342edfacc9027892c/packages/metascraper-title/index.js) |
| `description` | See [`metascraper-description`](https://github.com/microlinkhq/metascraper/blob/dc664c37ea1b238b1e3e9d5342edfacc9027892c/packages/metascraper-description/index.js) |
| `image` | See [`metascraper-image`](https://github.com/microlinkhq/metascraper/blob/dc664c37ea1b238b1e3e9d5342edfacc9027892c/packages/metascraper-image/index.js) |
| `author` | See [`metascraper-author`](https://github.com/microlinkhq/metascraper/blob/dc664c37ea1b238b1e3e9d5342edfacc9027892c/packages/metascraper-author/index.js) |
| `publication_date` | See [`metascraper-date`](https://github.com/microlinkhq/metascraper/blob/dc664c37ea1b238b1e3e9d5342edfacc9027892c/packages/metascraper-date/index.js) |
| `modified_date` | `meta[property="article:modified"]` |
| `section` | `meta[property="article:section"]` |
You can use the `./bin/cli assets debug <url>` to print the scraped metadata
from that URL. For example:
```bash
$ ./bin/cli assets debug https://www.washingtonpost.com/technology/2018/10/30/apple-event-october-ipad-pro-macbook-air/
┌──────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Property │ Value │
├──────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ title │ Apple redesigns the iPad Pro, breathes new life in the MacBook Air │
├──────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ description │ Apple is unveiling new iPads and MacBooks at an event in New York starting at 10 a.m. Fowler is there and will report in with the news and hands-on analysis throughout the day. │
├──────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ image │ https://www.washingtonpost.com/resizer/JAwNQE2alL2JjiWrbXeJ46wZHqA=/1484x0/arc-anglerfish-washpost-prod-washpost.s3.amazonaws.com/public/G5TWBFW4LAI6RC5MX7QB7TODUY.jpg │
├──────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ author │ Geoffrey A. Fowler │
├──────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ publication_date │ 2018-10-30T10:40:00.000Z │
├──────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ modified_date │ │
├──────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ section │ │
└──────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
```
+10 -4
View File
@@ -3,8 +3,10 @@ title: Authenticating with Talk
permalink: /integrating/authentication/
---
You can integrate Talk with any external authentication service that will enable
seamless single sign-on for users within your organization. There are a few
Out of the box Talk supports account registration with username and password, as well as features like forgot password.
You can also integrate Talk with any external authentication service that will enable
seamless single sign-on (SSO) for users within your organization. There are a few
methods of doing so:
1. Passport Middleware
@@ -17,8 +19,12 @@ choice.
You would choose the **Passport Middleware** route when you are OK using an auth
that is triggered from inside Talk that is not connected to an external auth
state (you don't use the auth anywhere else now). A great example of this is our
[talk-plugin-facebook-auth](/talk/plugin/talk-plugin-facebook-auth/) plugin.
state (you don't use the auth anywhere else now).
Plugins are available for the following 3rd party authentication providers:
* [Facebook](/talk/plugin/talk-plugin-facebook-auth/)
* [Google](/talk/plugin/talk-plugin-google-auth/)
## Custom Token Integration
@@ -1,28 +1,90 @@
---
title: Asset Management
permalink: /integrating/asset-management/
title: CMS Integration
permalink: /integrating/cms-integration/
---
## Embedding Comments on Your Site
Talk provides an embed script that you can drop into your site where you want a comments section to appear. By default that script dynamically generate Assets
in Talk in order to make it easier for lighter installations.
You can find the embed script inside talk under `Configure > Tech Settings > Embed Script`. It should look something like this, but with your domain in place of `<TALK_ROOT_URL>`:
```
<div id="coral_talk_stream"></div>
<script src="<TALK_ROOT_URL>/static/embed.js" async onload="
Coral.Talk.render(document.getElementById('coral_talk_stream'), {
talk: '<TALK_ROOT_URL>'
});
"></script>
```
## Triggering the Comments Section (client side, i.e. Your site)
When the embed script is triggered on your page load several things are initiated inside Talk, including fetching all comments for the specified article, establishing websocket connections for this user, and checking users session for SSO/authentication.
Instead of greedily triggering the embed to render on _EVERY PAGE LOAD_, we highly recommend implementing a _“lazy”_ rendering strategy to only render the comments section if a user wants to interact with it. This will greatly improve your initial page load performance, and will be critical to managing server resources if youre running Talk on a heavy-traffic production site.
We recommend using one of these _“lazy”_ loading strategies:
#### Scroll to Comments Section
Wait for user to scroll to the comment section before triggering the embed to render.
You can pass lazy: true to the render options, like so:
```
Coral.Talk.render(document.getElementById('container'), {
talk: 'https://my-talk-installation.com',
lazy: true,
});
```
Or you can enable lazy rendering by default on all assets using ENV variable `TALK_DEFAULT_LAZY_RENDER=TRUE`
_*Note: This feature requires Talk version 4.6.8 or greater_
#### Show Comments Button
You can hide the comments section until a user clicks button, then trigger the embed to render
This example uses jQuery to render the embed on the button's click event
```
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<div id="coral_talk_stream">
<button>Show Comments</button>
</div>
<script type="text/javascript">
$('#coral_talk_stream button').on('click', function() {
$.getScript('<TALK_ROOT_URL>/static/embed.js', function() {
Coral.Talk.render(document.getElementById('coral_talk_stream'), {
talk: '<TALK_ROOT_URL>',
});
});
});
</script>
```
## Creating Assets in Talk (server side, i.e. What you need to send to Talk)
One of the most frequent questions that we get asked by organizations trying to
integrate Talk is: _How do we hook our CMS up to Talk so that articles are in
sync?_
This guide is designed to explain the steps to take your base installation of
Talk and configure it to allow only assets pushed into it from your CMS, and
keep your URL/title in sync. We won't cover here how to install the plugin, as
it is covered in our [Plugins Overview](/talk/plugins/).
### “Lazy Asset Creation
Talks provided embed script by default dynamically generates Assets in Talk based on each unique url that triggers it. The url must reference an existing Permitted Domain. If your articles/stories always have unique urls, then you will not need to modify the default behavior.
## Why do we need to create a plugin?
Assets created in this way will then be scraped to load metadata, see [Asset Scraping](/talk/integrating/asset-scraping/)
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:
## Customizing the Integration with a Plugin
In order to have more strict control over the asset creation flow to allow only assets pushed into it from your CMS, and keep your URL/title in sync we will create a plugin.
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 create a plugin that will:
We will then modify our embed so that we can [Target the Asset](#Target-the-Asset).
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).
This guide is designed to explain the steps to take your base installation of Talk and configure it. We won't cover here how to install the plugin, as it is covered in our [Plugins Overview](/talk/plugins/).
But first we should grab our basic plugin structure:
@@ -248,7 +310,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%20a%20New%20Asset%20Route)
, 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.
+12 -2
View File
@@ -105,6 +105,8 @@ Then the application can be started as is.
### Docker
To deploy customized plugins to a production instance of Talk, we recommend using the Docker onbuild strategy outlined below.
If you deploy using Docker, you can extend from the `*-onbuild` image, an
example `Dockerfile` for your project could be:
@@ -112,14 +114,22 @@ example `Dockerfile` for your project could be:
FROM coralproject/talk:4.5-onbuild
```
Where the directory for your instance would contain a `plugins.json` file
describing the plugin requirements and a `plugins` directory containing any
Establish a private repository for your instance that includes the following:
* a `plugins.json` file
listing the plugin requirements
* a `plugins` directory containing any
other local plugins that should be included.
* a Dockerfile as outlined above
Git submodules can also be used to point to plugin directories that might be outside your primary repository.
Onbuild triggers will execute when the image is building with your custom
configuration and will ensure that the image is ready to use by building all
assets inside the image as well.
Once built, you can deploy the docker image to your architecture by tagging the image and including it in your docker-compose.yml.
For more information on the onbuild image, refer to the
[Installation from Docker](/talk/installation-from-docker/) documentation.
+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,
+5 -1
View File
@@ -4,7 +4,11 @@ const { SEARCH_OTHER_USERS } = require('../../perms/constants');
const { escapeRegExp } = require('../../services/regex');
const mergeState = (query, state) => {
const { status } = state;
const { role, status } = state;
if (role) {
query.merge({ role });
}
if (status) {
const { username, banned, suspended } = status;
+1 -1
View File
@@ -85,7 +85,7 @@ const onConnect = async (connectionParams, connection) => {
*/
const batchedUserRefresher = new DataLoader(
userIDs => {
console.log(`OPERATION: refreshing ${userIDs.length} users.`);
debug(`OPERATION: refreshing ${userIDs.length} users.`);
return User.find({ id: { $in: userIDs } }).then(
singleJoinBy(userIDs, 'id')
);
+1
View File
@@ -189,6 +189,7 @@ type UserStatus {
}
input UserStateInput {
role: USER_ROLES
status: UserStatusInput
}
+3 -62
View File
@@ -3,59 +3,7 @@ const scraper = require('../../services/scraper');
const Assets = require('../../services/assets');
const { createLogger } = require('../../services/logging');
const logger = createLogger('jobs:scraper');
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');
// Load the scraper with the rules.
const metascraper = require('metascraper').load([
require('metascraper-title')(),
require('metascraper-description')(),
require('metascraper-image')(),
require('metascraper-author')(),
require('metascraper-date')(),
require('./rules/modified')(),
require('./rules/section')(),
]);
let customHeaders = {};
try {
customHeaders = JSON.parse(SCRAPER_HEADERS);
} catch (err) {
console.error('Cannot parse TALK_SCRAPER_HEADERS');
throw err;
}
// Parse the headers to be added to the scraper.
const headers = merge(
{
'User-Agent': `Coral-Talk/${version}`,
},
customHeaders
);
// Add proxy configuration if exists.
const agent = SCRAPER_PROXY_URL ? new HttpsProxyAgent(SCRAPER_PROXY_URL) : null;
/**
* Scrapes the given asset for metadata.
*/
async function scrape({ url }) {
const res = await fetch(url, {
headers,
agent,
});
const html = await res.text();
// Get the metadata from the scraped html.
const metadata = await metascraper({
html,
url,
});
return metadata;
}
/**
* Updates an Asset based on scraped asset metadata.
@@ -64,16 +12,9 @@ function update(id, meta) {
return Asset.update(
{ id },
{
$set: {
title: meta.title || '',
description: meta.description || '',
image: meta.image ? meta.image : '',
author: meta.author || '',
publication_date: meta.date || '',
modified_date: meta.modified || '',
section: meta.section || '',
$set: merge(meta, {
scraped: new Date(),
},
}),
}
);
}
@@ -95,7 +36,7 @@ module.exports = () => {
}
// Scrape the metadata from the asset.
const meta = await scrape(asset);
const meta = await scraper.scrape(asset.url);
log.info('Finished scraping');
+3 -3
View File
@@ -130,8 +130,8 @@ ar:
enable_questionbox: 'اطرح سؤال على القراء'
enable_questionbox_description: 'هذا السؤال سيظهر في الجزء العلوي من جدول التعليقات هذا. اسأل القراء عن مسألة معينة في المقال او اطرح أسئلة نقاشية.. الخ.'
hours: ساعات
include_comment_stream: 'ادراج وصف جدول التعليقات للقراء'
include_comment_stream_desc: 'اكتب رسالة ليتم إضافتها إلى الجزء العلوي من جدول التعليقات. ضع موضوعا، اشمل القواعد الإرشادية للمجموعة .. الخ'
code_of_conduct_summary: 'ادراج وصف جدول التعليقات للقراء'
code_of_conduct_summary_desc: 'اكتب رسالة ليتم إضافتها إلى الجزء العلوي من جدول التعليقات. ضع موضوعا، اشمل القواعد الإرشادية للمجموعة .. الخ'
include_question_here: 'اكتب سؤالك هنا:'
include_text: 'ادرج النص هنا.'
moderate: اشرف
@@ -225,6 +225,7 @@ ar:
CANNOT_IGNORE_STAFF: 'لا يمكن تجاهل الموظفين.'
COMMENT_PARENT_NOT_VISIBLE: 'التعليق الذي ترد عليه تمت إزالته أو غير موجود.'
COMMENT_TOO_SHORT: 'يجب أن تكون التعليقات أكثر من حرف واحد، يرجى مراجعة تعليقك وإعادة المحاولة.'
COMMENT_TOO_LONG: 'يتجاوز النص الحد الأقصى للطول المسموح'
COMMENTING_CLOSED: 'تم إغلاق فاعلية التعليق'
COMMENTING_DISABLED: 'التعليق معطّل حاليًا على هذا الموقع'
confirm_password: 'كلمات المرور غير متطابقة. يرجى التحقق مرة أخرى'
@@ -281,7 +282,6 @@ ar:
reasons:
comment:
banned_word: 'كلمة محظورة'
body_count: 'يتجاوز النص الحد الأقصى للطول المسموح'
comment_noagree: أعارض
comment_offensive: مسيء
comment_other: آخر
+3 -3
View File
@@ -123,8 +123,8 @@ da:
enable_questionbox: 'Stil et spørgsmål til læserne'
enable_questionbox_description: 'Dette spørgsmål vises øverst i denne kommentarstrøm. Stil et spørgsmål til læserne om et bestemt problem i artiklen eller stil spørgsmål til diskussion mv.'
hours: Timer
include_comment_stream: 'Inkluder kommentarstrømmens beskrivelse for læsere'
include_comment_stream_desc: 'Skriv en besked, der skal tilføjes øverst i din kommentarstrøm. Indsæt et emne der omfatter fællesskabsretningslinjer mv.'
code_of_conduct_summary: 'Inkluder kommentarstrømmens beskrivelse for læsere'
code_of_conduct_summary_desc: 'Skriv en besked, der skal tilføjes øverst i din kommentarstrøm. Indsæt et emne der omfatter fællesskabsretningslinjer mv.'
include_question_here: 'Stil dit spørgsmål her:'
include_text: 'Medtag din tekst her.'
moderate: Moderat
@@ -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
+7 -3
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'
@@ -130,10 +132,11 @@ de:
enable_questionbox: 'Stellen Sie den Lesern eine Frage'
enable_questionbox_description: 'Diese Frage erscheint am Anfang des Kommentarbereichs. Regen Sie eine Diskussion an.'
hours: Stunden
include_comment_stream: 'Einleitung zum Kommentarbereich'
include_comment_stream_desc: 'Verfassen Sie eine Einleitung, die über jedem Kommentarbereich erscheint. Nützlich z.B. für Community-Richtlinien.'
code_of_conduct_summary: 'Einleitung zum Kommentarbereich'
code_of_conduct_summary_desc: 'Verfassen Sie eine Einleitung, die über jedem Kommentarbereich erscheint. Nützlich z.B. für Community-Richtlinien.'
include_question_here: 'Stellen Sie Ihre Frage hier:'
include_text: 'Fügen Sie Ihren Text hier ein.'
include_an_icon: 'Fügen Sie ein Icon hinzu'
moderate: Moderieren
moderation_settings: Moderations-Einstellungen
open: Öffnen
@@ -216,11 +219,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 +275,6 @@ de:
reasons:
comment:
banned_word: 'Unzulässiges Wort'
body_count: 'Text überschreitet Zeichenlimit'
comment_noagree: 'Andere Meinung'
comment_offensive: Unangemessen
comment_other: Anderes
+18 -3
View File
@@ -48,7 +48,10 @@ en:
comment_post_notif_premod: 'Thank you for posting. Our moderation team will review your comment shortly.'
comment_singular: Comment
common:
contains_link: 'Contains Link'
copy: Copy
copied: 'Copied'
notsupported: 'Not supported'
error: 'An error has occurred.'
reaction: reaction
reactions: reactions
@@ -60,6 +63,7 @@ en:
active: Active
admin: Administrator
ads_marketing: 'This looks like an ad/marketing'
all: 'All'
are_you_sure: 'Are you sure you would like to ban {0}?'
ban_user: 'Ban User?'
banned: Banned
@@ -67,6 +71,8 @@ en:
cancel: Cancel
commenter: Commenter
dont_like_username: 'Dislike username'
filter_users: 'Filter users'
filter_role: Role
flaggedaccounts: 'Reported Usernames'
flags: Flags
impersonating: Impersonation
@@ -83,6 +89,7 @@ en:
spam_ads: Spam/Ads
staff: Staff
status: Status
suspended: Suspended
username_and_email: 'Username and Email'
yes_ban_user: 'Yes Ban User'
configure:
@@ -130,8 +137,9 @@ en:
enable_questionbox: 'Ask Readers a Question'
enable_questionbox_description: 'This question will appear at the top of this comment stream. Ask readers about a certain issue in the article or pose discussion questions etc.'
hours: Hours
include_comment_stream: 'Include Comment Stream Description for Readers'
include_comment_stream_desc: 'Write a message to be added to the top of your comment stream. Pose a topic include community guidelines etc.'
code_of_conduct_summary: 'Summary of your Code of Conduct'
code_of_conduct_summary_desc: 'This message will appear above every comments stream on your site. Click here to learn more about writing a good code.'
include_an_icon: 'Include an Icon'
include_question_here: 'Write your question here:'
include_text: 'Include your text here.'
moderate: Moderate
@@ -144,6 +152,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 +228,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 +260,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 +293,6 @@ en:
reasons:
comment:
banned_word: 'Banned Word'
body_count: 'Body exceeds max length'
comment_noagree: Disagree
comment_offensive: Offensive
comment_other: Other
@@ -308,6 +319,7 @@ en:
comments: comments
configure_stream: Configure
content_not_available: 'This content is not available'
edit: Edit
edit_name:
button: Submit
error: 'Usernames can contain letters numbers and _ only'
@@ -359,6 +371,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 +385,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 +420,7 @@ en:
show_shortcuts: 'Show Shortcuts'
singleview: 'Zen mode'
sort: Sort
suspend: 'Suspend User'
system_withheld: 'System Withheld'
thismenu: 'Open this menu'
thousand: k
+55 -31
View File
@@ -4,19 +4,19 @@ es:
view_options: 'Ver opciones'
already_flagged_username: 'Usted ya reportó a este usuario.'
bandialog:
are_you_sure: '¿Estás segura que quieres suspender a {0}?'
ban_user: '¿Quieres suspender el Usuario?'
are_you_sure: '¿Estás seguro que quieres suspender a {0}?'
ban_user: '¿Quieres suspender al Usuario?'
banned_user: 'Usuario Suspendido'
cancel: Cancelar
email_message_ban: "Estimado/a {0},\n\nUsted o alguien con acceso a su cuenta a violado los lineamientos de nuestra comunidad. Como consecuencia de esto, su cuenta fue bloqueada. No podrá realizar ni reportar más comentarios. Si usted piensa que esto ha sido un error, por favor contáctese con nosotros."
email_message_ban: "Estimado/a {0},\n\nUsted o alguien con acceso a su cuenta ha violado los lineamientos de nuestra comunidad. Como consecuencia de esto, su cuenta fue bloqueada. No podrá realizar ni reportar más comentarios. Si usted piensa que esto ha sido un error, por favor contáctese con nosotros."
note: 'Nota: {0}'
note_ban_user: 'Suspender a este usuario no le va a permitir (al usuario) borrar ni editar ni comentar.'
note_reject_comment: 'Suspender a este usuario también va a colocar este comentario en la cola de Rechazados.'
notify_ban_description: 'Esto notificará al usuario por email que fueron suspendidos de la comunidad'
notify_ban_description: 'Esto notificará al usuario por correo electrónico que fueron suspendidos de la comunidad'
notify_ban_headline: 'Notificar al usuario de la suspensión'
send: Enviar
write_a_message: 'Escribe un mensaje'
yes_ban_user: 'Si, Suspender al usuario'
yes_ban_user: 'Si, suspender al usuario'
bio_offensive: 'Esta biografia es ofensiva'
cancel: Cancelar
characters_remaining: 'caracteres restantes'
@@ -30,21 +30,25 @@ es:
view_context: 'Ver contexto'
comment_box:
cancel: Cancelar
characters_remaining: 'carácteres restantes'
characters_remaining: 'caracteres restantes'
comment: 'Publicar un comentario'
comment_post_banned_word: 'Tu comentario contiene una o más palabras que no están permitidas en nuestro espacio, por lo que no será publicado. Si crees que es un error, por favor contacta a nuestro equipo de moderación.'
comment_post_banned_word: 'Tu comentario contiene una o más palabras que no están permitidas en nuestro espacio, por lo que no será publicado. Si crees que es un error, por favor contactate a nuestro equipo de moderación.'
comment_post_notif: 'Tu comentario ha sido publicado.'
comment_post_notif_premod: 'Gracias por el comentario. Nuestro equipo de moderación va a revisarlo muy pronto.'
name: Nombre
post: Publicar
reply: Responder
comment_history_blank:
info: 'Una historia de tus comentarios aparecerá aquí'
title: 'No has escrito ningún comentario'
comment_offensive: 'Este comentario es ofensivo'
comment_plural: Comentarios
comment_post_banned_word: 'Tu comentario contiene una o más palabras que no están permitidas en nuestro espacio, por lo que no será publicado. Si crees que es un error, por favor contacta a nuestro equipo de moderación.'
comment_post_banned_word: 'Tu comentario contiene una o más palabras que no están permitidas en nuestro espacio, por lo que no será publicado. Si crees que es un error, por favor contactate con nuestro equipo de moderación.'
comment_post_notif: 'Tu comentario ha sido publicado.'
comment_post_notif_premod: 'Gracias por el comentario. Nuestro equipo de moderación va a revisarlo muy pronto.'
comment_singular: Comentario
common:
contains_link: 'Contiene enlace'
copy: Copiar
error: 'Ha ocurrido un error.'
reaction: reacción
@@ -56,36 +60,40 @@ es:
account_creation_date: 'Fecha de creación de la cuenta'
active: Activa
admin: Administrator
ads_marketing: 'Esto parece ser un ad/marketing'
are_you_sure: '¿Estás segura que quieres suspender a {0}?'
ads_marketing: 'Esto parece ser un publicidad/acción de comercialización'
all: 'Todos'
are_you_sure: '¿Estás seguro que quieres suspender a {0}?'
ban_user: '¿Quieres suspender al Usuario?'
banned: Suspendido
banned_user: 'Usuario Suspendido'
banned_user: 'Usuario suspendido'
cancel: Cancelar
commenter: Comentarista
dont_like_username: 'No me gusta este nombre de usuario'
flaggedaccounts: 'Nombres de Usuario Reportados'
filter_users: 'Filtrarr usuarios'
filter_role: Rol
flaggedaccounts: 'Nombres de usuario reportados'
flags: Reportes
impersonating: Impersonando
loading: 'Cargando resultados'
moderator: Moderator
newsroom_role: 'Rol en la redacción'
no_flagged_accounts: 'No hay ningún nombre de usario reportado en este momento.'
no_flagged_accounts: 'No hay ningún nombre de usuario reportado en este momento.'
no_results: 'No se encontraron usuarios con ese nombre o correo.'
offensive: Ofensivo
other: Otro
people: Gente
role: 'Seleccionar rol...'
select_status: 'Seleccionar estado...'
spam_ads: Spam/Publicidad
spam_ads: Correo basura/Publicidad
staff: Personal
status: Estado
suspended: Suspendido
username_and_email: 'Usuario y Correo'
yes_ban_user: 'Si, Suspendan el usuario'
yes_ban_user: 'Sí, suspendan el usuario'
configure:
access_message: 'Usted debe ser un administrador para acceder a esta página. Encuentre a otro admin y actualice los permisos de su cuenta!'
access_message: 'Usted debe ser un administrador para acceder a esta página. Encuentre a otro administrador y actualice los permisos de su cuenta!'
apply: Aplicar
banned_word_text: 'Comentarios que contengan estas palabras o frases, en mayusculas o minúsculas, serán automáticamente eliminados del hilo de comentario. Escribir una palabra y apretar Enter o Tabulador para agregarla. O pueden pegar una lista de palabras separadas por coma.'
banned_word_text: 'Comentarios que contengan estas palabras o frases, en mayúsculas o minúsculas, serán automáticamente eliminados del hilo de comentarios. Escribir una palabra y apretar Enter o Tabulador para agregarla. O pueden pegar una lista de palabras separadas por coma.'
banned_words_title: 'Lista de palabras prohibidas'
close: Cerrar
close_after: 'Cerrar comentarios luego de'
@@ -93,14 +101,14 @@ es:
close_stream_configuration: 'Este hilo de comentario está en este momento cerrado. Al abrirlo, nuevos comentarios serán publicados y mostrados.'
closed_comments_desc: 'Escribe un mensaje que será mostrado cuando los comentarios estén cerrados y no se acepten más comentarios.'
closed_comments_label: 'Escribe un mensaje...'
closed_stream_settings: 'Mensaje que se muestra cuando no se aceptan más comentarios en el articulo'
closed_stream_settings: 'Mensaje que se muestra cuando no se aceptan más comentarios en el artículo'
comment_count_error: 'Por favor escribir un número válido'
comment_count_header: 'Limitar el largo del comentario'
comment_count_text_post: carácteres
comment_count_text_post: caracteres
comment_count_text_pre: 'Los comentarios serán limitados a'
comment_settings: Configuración
comment_stream: 'Hilo de Comentarios'
comment_stream_will_close: 'El hilo de comentarios se cerrara'
comment_stream_will_close: 'El hilo de comentarios se cerrará'
community: Comunidad
configure: Configurar
copy_and_paste: 'Copiar y pegar el código de más abajo en tu CMS para colocar la caja de comentarios en tus artículos'
@@ -117,16 +125,16 @@ es:
embed_comment_stream: 'Colocar Hilo de Comentarios'
enable_pre_moderation: 'Permitir pre-moderación'
enable_pre_moderation_text: 'Los moderadores deben aprobar todo comentario antes que sea publicado.'
enable_premod: 'Activar Pre Moderación'
enable_premod: 'Activar Pre-Moderación'
enable_premod_description: 'Los y las moderadoras deben aprobar cualquier comentario antes de su publicación'
enable_premod_links: 'Pre-Moderar Comentarios que contienen Enlaces'
enable_premod_links: 'Pre-Moderar comentarios que contienen Enlaces'
enable_premod_links_description: 'Los y las moderadoras deben aprobar cualquier comentario que contengan enlaces antes de su publicación.'
enable_premod_links_text: 'Los moderadores deben aprobar todo comentario que contenga un enlace antes de que sea publicado.'
enable_questionbox: 'Hacer una pregunta a los y las lectoras.'
enable_questionbox_description: 'Esta pregunta aparecerá en la parte de arriba del hilo de comentarios.'
hours: Horas
include_comment_stream: 'Incluir Descripción de Hilo de Comentarios para Lectores'
include_comment_stream_desc: 'Escribir un mensaje para ser incluido al principio del hilo de comentarios. Un tema planteado podría ser la guía de comunidad, etc.'
code_of_conduct_summary: 'Incluir Descripción de Hilo de Comentarios para Lectores'
code_of_conduct_summary_desc: 'Escribir un mensaje para ser incluido al principio del hilo de comentarios. Un tema planteado podría ser la guía de comunidad, etc.'
include_question_here: 'Escribir la pregunta aquí.'
include_text: 'Agregar tu texto aquí.'
moderate: Moderar
@@ -135,10 +143,12 @@ es:
open_stream: 'Abrir Hilo de Comentarios'
open_stream_configuration: 'Este hilo de comentarios está abierto. Al cerrarlo no se podrán publicar nuevos comentarios pero todos los comentarios anteriores aún serán mostrados.'
organization_contact_email: 'Email de la Organización'
organization_info_copy: 'Nosotros usamos esta información en las notificaciones de email generadas por Talk. Esto conecta los mensajes de tu organización, y provee una forma para que los usuarios se comuniquen si tienen un inconveniente con su cuenta.'
organization_info_copy: 'Nosotros usamos esta información en las notificaciones de correo electrónico generadas por Talk. Esto conecta los mensajes de tu organización, y provee una forma para que los usuarios se comuniquen si tienen un inconveniente con su cuenta.'
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 +167,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 +223,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 +269,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,8 +291,9 @@ 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: Editar
edit_name:
button: Enviar
error: 'Nombres de usuarios pueden solamente incluir letras, números y _'
@@ -307,14 +319,14 @@ es:
save: Guardar
create:
confirm_password: 'Confirmar Contraseña'
email: 'Dirección de correo'
organization_contact_email: 'Organización: Email de contacto'
email: 'Dirección de correo electrónico'
organization_contact_email: 'Organización: correo electrónico de contacto'
password: Contraseña
save: Guardar
username: 'Nombre de Usuario'
final:
close: 'Cerrar este instalador'
description: '¡Gracias por instalar Talk! Enviamos un correo para verificar su dirección de correo. Mientras esta terminando de configurar su cuenta, ya puede comenzar a involucrarse con sus lectores.'
description: '¡Gracias por instalar Talk! Enviamos un correo para verificar su dirección de correo electrónico. Mientras esta terminando de configurar su cuenta, ya puede comenzar a involucrarse con sus lectores.'
launch: 'Iniciar Talk'
initial:
description: 'Vamos a crear su comunidad Talk en unos pocos pasos.'
@@ -324,6 +336,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'
@@ -382,6 +405,7 @@ es:
other: Otro
password_reset:
change_password: 'Cambio de contraseña'
change_password_help: 'Por favor, introduzca una nueva contraseña. ¡Hazlo seguro!'
confirm_new_password: 'Confirme su nueva contraseña'
mail_sent: 'Si tiene una cuenta registrada, un enlace para resetear su clave ha sido enviado a esa dirección de correo.'
new_password: 'Nueva contraseña'
@@ -421,7 +445,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.'
+3 -3
View File
@@ -123,8 +123,8 @@ fi_FI:
enable_questionbox: 'Kysy lukijoilta'
enable_questionbox_description: 'Tämä kysymys tulee näkymään kommenttiosion ylälaidassa. Kysy artikkelin aiheesta tai ohjaa keskustelua kysymyksen avulla.'
hours: Tuntia
include_comment_stream: 'Sisällytä kommentoinnin kuvaus lukijoille'
include_comment_stream_desc: 'Kirjoita kommenttiosion yläreunassa näkyvä viesti. Aseta keskustelun aihe, sisällytä sääntöjä tms.'
code_of_conduct_summary: 'Sisällytä kommentoinnin kuvaus lukijoille'
code_of_conduct_summary_desc: 'Kirjoita kommenttiosion yläreunassa näkyvä viesti. Aseta keskustelun aihe, sisällytä sääntöjä tms.'
include_question_here: 'Kirjoita kysymyksesi tähän:'
include_text: 'Lisää teksti tähän.'
moderate: Moderoi
@@ -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
+3 -3
View File
@@ -127,8 +127,8 @@ fr:
enable_questionbox: 'Posez une question aux lecteurs.'
enable_questionbox_description: 'Cette question apparaîtra en haut de ce fil de commentaires. Demandez aux lecteurs de s''exprimer sur un sujet évoqué dans l''article ou posez les termes de la discussion, etc.'
hours: Heures
include_comment_stream: 'Inclure la description du fil de commentaires pour les lecteurs'
include_comment_stream_desc: 'Écrivez un message à ajouter au haut de votre flux de commentaires. Publiez un sujet de discussion, avec des suggestion sur la tenue des débats, etc.'
code_of_conduct_summary: 'Inclure la description du fil de commentaires pour les lecteurs'
code_of_conduct_summary_desc: 'Écrivez un message à ajouter au haut de votre flux de commentaires. Publiez un sujet de discussion, avec des suggestion sur la tenue des débats, etc.'
include_question_here: 'Écrivez votre question ici.'
include_text: 'Entrez votre texte ici.'
moderate: Modérer
@@ -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
+574
View File
@@ -0,0 +1,574 @@
it:
admin_sidebar:
sort_comments: 'Ordina Commenti'
view_options: 'Visualizza Opzioni'
already_flagged_username: 'Hai già segnalato questo username.'
bandialog:
are_you_sure: 'Sei sicuro di voler bloccare {0}?'
ban_user: 'Blocca Utente'
banned_user: 'Utenti bloccati'
cancel: Annulla
email_message_ban: "Caro {0},\n\nQualcuno che ha accesso al tuo account ha violato le nostre linee guida per la community. Di conseguenza, il tuo account è stato bloccato. Non potrai più commentare, apprezzare o segnalare commenti. Se ritieni questo sia un errore, contatta i nostri moderatori."
note: 'Nota: {0}'
note_ban_user: 'Bloccare questo utente non gli permetterà di commentare, interagire, o segnalare commenti.'
note_reject_comment: 'Bloccare questo utente inserirà inoltre questo commento tra quelli rifiutati.'
notify_ban_description: 'Questo notificherà l''utente, tramite email, che è stato bloccato dalla comunità.'
notify_ban_headline: 'Notifica l''utente della interdizione'
send: Invia
write_a_message: 'Scrivi un messaggio'
yes_ban_user: 'Si, blocca utente'
bio_offensive: 'Questa descrizione è offensiva'
cancel: Annulla
characters_remaining: 'caratteri rimanenti'
comment:
anon: Anonimo
ban_user: 'Blocca Utente'
comment: 'Pubblica un commento'
edited: Modificato
flagged: segnalato
undo_reject: Annulla
view_context: 'Visualizza il contesto'
comment_box:
cancel: Annulla
characters_remaining: 'caratteri rimanenti'
comment: 'Pubblica un commento'
comment_post_banned_word: 'Il tuo commento contiene una o più parole non permesse, non sarà quindi pubblicato. Se ritieni questo sia un errore, contatta i nostri moderatori.'
comment_post_notif: 'Il tuo commento è stato pubblicato.'
comment_post_notif_premod: 'Grazie per il tuo contributo. Il nostro team di moderatori revisionerà presto il tuo commento.'
name: Nome
post: Invia
reply: Rispondi
comment_history_blank:
info: 'La cronologia dei tuoi commenti apparirà qui'
title: 'Non hai pubblicato nessun commento'
comment_offensive: 'Questo commento è offensivo'
comment_plural: Commenti
comment_post_banned_word: 'Il tuo commento contiene una o più parole non permesse, non sarà quindi pubblicato. Se ritieni questo sia un errore, contatta i nostri moderatori.'
comment_post_notif: 'Il tuo commento è stato pubblicato.'
comment_post_notif_premod: 'Grazie per il tuo contributo. Il nostro team di moderatori revisionerà presto il tuo commento.'
comment_singular: Commento
common:
copy: Copia
copied: 'Copiato'
notsupported: 'Non supportato'
error: 'Si è verificato un errore.'
reaction: reazione
reactions: reazioni
reply: 'rispondi' # noun, singular
replies: 'risposte'
story: 'Storia'
community:
account_creation_date: 'Data creazione account'
active: Attivo
admin: Amministratore
ads_marketing: 'Questo sembra essere un annuncio pubblicitario'
all: 'Tutti'
are_you_sure: 'Sei sicuro di voler bloccare {0}?'
ban_user: 'Blocca Utente?'
banned: Bloccato
banned_user: 'Utente bloccato'
cancel: Annulla
commenter: Utente
dont_like_username: 'Username non adatto'
filter_users: 'Filtra utenti'
filter_role: Ruolo
flaggedaccounts: 'Username segnalati'
flags: Segnalazioni
impersonating: 'Impersonare'
loading: 'Caricamento risultati'
moderator: Moderatore
newsroom_role: 'Ruolo della sala stampa'
no_flagged_accounts: 'Nessun utente segnalato per ora.'
no_results: 'Nessun utente con quell''username o indirizzo email trovato.'
offensive: Offensivo
other: Altro
people: Persone
role: 'Seleziona ruolo...'
select_status: 'Seleziona status...'
spam_ads: Spam/pubblicità
staff: Staff
status: Status
suspended: Sospeso
username_and_email: 'Username e Email'
yes_ban_user: 'Si, blocca utente'
configure:
access_message: 'Devi essere amministratore per accedere alle configurazioni. Perfavore contatta un amministratore e chiedi di promuoverti!'
apply: Applica
banned_word_text: 'Commenti che contengono queste parole o frasi, che siano in maiuscolo o minuscolo, saranno automaticamente rimossi. Inserisci una parola e premi Invio o Tab per aggiungerla. Oppure aggiungi una lista di parole separate da virgole.'
banned_words_title: 'Lista di parole vietate'
close: Chiudi
close_after: 'Chiudi commenti successivi'
close_stream: 'Chiudi il thread dei commenti'
close_stream_configuration: 'Questo thread dei commenti è attualmente chiuso. Aprirlo per visulizzare nuovi commenti'
closed_comments_desc: 'Scrivi un messaggio da visualizzare quando il tuo thread dei commenti viene chiuso senza accettarne nuovi.'
closed_comments_label: 'Scrivi un messaggio...'
closed_stream_settings: 'Thread dei messaggi chiusi'
comment_count_error: 'Inserisci un numero valido.'
comment_count_header: 'Limite lunghezza commento'
comment_count_text_post: caratteri
comment_count_text_pre: 'I commenti saranno limitati a'
comment_settings: Impostazioni
comment_stream: 'Thread dei commenti'
comment_stream_will_close: 'Il thread dei commenti sarà chiuso'
community: Comunità
configure: Configura
copy_and_paste: 'Copia e incolla il codice qui sotto nel tuo CMS per includere il box dei commenti nei tuoi articoli'
custom_css_url: 'URL per il CSS personalizzato'
custom_css_url_desc: 'URL del CSS che sostituirà lo stile predefinito del thread dei commenti. Può essere interno o esterno.'
days: Giorni
description: 'Modifica le impostazioni dei commenti in questa storia.'
disable_commenting_desc: 'Scrivi il messaggio che sarà visualizzato quando i commenti saranno disattivati.'
disable_commenting_title: 'Disattiva tutti i commenti del sito'
domain_list_text: 'Inserisci i domini che vuoi autorizzare per Talk. Per esempio i tuoi ambienti di sviluppo, test o produzione (ex. localhost:3000 staging.domain.com domain.com).'
domain_list_title: 'Domain permessi'
edit_comment_timeframe_heading: 'Periodo di tempo disponibile per modificare i commenti'
edit_comment_timeframe_text_post: 'secondi per modificare i commenti.'
edit_comment_timeframe_text_pre: 'Gli utenti avranno'
edit_info: 'Modifica informazioni'
embed_comment_stream: 'Integra il thread dei commenti'
enable_pre_moderation: 'Abilita pre-moderazione'
enable_pre_moderation_text: 'I moderatori dovranno approvare tutti i commenti prima che vengano pubblicati.'
enable_premod: 'Abilita pre-moderazione'
enable_premod_description: 'I moderatori dovranno approvare tutti i commenti prima che vengano pubblicati.'
enable_premod_links: 'Pre-Modera commenti contenenti link'
enable_premod_links_description: 'I moderatori devono approvare tutti i commenti contenenti link prima di essere pubblicati.'
enable_premod_links_text: 'I moderatori devono approvare tutti i commenti contenenti link prima di essere pubblicati.'
enable_questionbox: 'Domanda qualcosa ai lettori'
enable_questionbox_description: 'Questa domanda apparirà sopra al thread dei commenti. Chiedi ai lettori cosa ne pensano dell''articolo o poni domande per iniziare una conversazione etc.'
hours: Ore
include_comment_stream: 'Includi la descrizione del thread dei commenti'
include_comment_stream_desc: 'Scrivi un messaggio che sarà aggiunto sopra il thread dei commenti. Poni una domanda, includi le linee guida della comunità etc.'
include_question_here: 'Scrivi la tua domanda di seguito:'
include_text: 'Includi il testo qui.'
moderate: Modera
moderation_settings: 'Impostazioni di moderazione'
open: Apri
open_stream: 'Apri il thread'
open_stream_configuration: 'Questo thread dei commenti è attualmente aperto. Chiudendolo non saranno più accettati nuovi commenti, mentre tutti i commenti precedenti continueranno ad essere visibili.'
organization_contact_email: 'Indirizzo email dell''organizzazione'
organization_info_copy: 'Utilizziamo queste informazioni nelle email di notifica generate da Talk. Questo connette i messaggi alla tua organizzazione, permettendo agli utenti di contattarti se riscontrano problemi con il loro account.'
organization_info_copy_2: 'Raccomandiamo di creare un indirizzo email generico (eg. community@yournewsroom.com) per questo scopo. Così da poter rimanere consistente nel tempo, senza rivelare il proprio nome che gli utenti potrebbero prendere di mira nel caso il loro account venga bloccato.'
organization_information: 'Informazioni dell''organizzazione'
organization_name: 'Nome dell''organizzazione'
suspect_or_forbidden_words_placeholder: 'Parola o frase'
product_guide_link: 'Guida all''uso del prodotto'
report_bug_or_feedback: 'Segnala un bug o lascia un feedback'
require_email_verification: 'Richiede la verifica dell''email'
require_email_verification_text: 'I nuovi utenti devono verificare la loro email prima di poter commentare'
save: Salva
save_changes: 'Salva le modifiche'
save_changes_dialog:
cancel: Annulla
copy: 'Hai apportato una o più modifiche senza salvare. Vuoi salvare o scartare le modifiche?'
discard: Scarta
save_settings: 'Salva impostazioni'
unsaved_changes: 'Modifiche non salvate'
shortcuts: Scorciatoie
sign_out: Esci
stories: Storie
stream_settings: 'Impostazioni thread dei commenti'
suspect_word_text: 'Commenti che contengono queste parole o frasi, che siano in maiuscolo o minuscolo, saranno evidenziate nel thread dei commenti. Inserisci una parola e premi Invio o Tab per aggiungerla. Oppure aggiungi una lista di parole separate da virgole.'
suspect_word_title: 'Lista delle parole indiziate'
tech_settings: 'Impostazioni tecniche'
title: 'Configura il thread dei commenti'
view_last_version: 'Visualizza ultima versione'
weeks: Settimane
wordlist: 'Parole vietate'
confirm_email:
click_to_confirm: 'Premi qui sotto per confermare il tuo indirizzo email.'
confirm: Conferma
email_confirmation: 'Conferma Email'
continue: Continua
createdisplay:
check_the_form: 'Modulo non valido. Ricontrolla i campi'
continue: 'Continua con lo stesso username di Facebook'
error_create: 'Errore nel cambiamento dell''username'
fake_comment_body: 'Questo è un commento esempio. I lettori possono condividere i loro pensieri e opinioni con le redazioni nella sezione dei commenti.'
fake_comment_date: '1 minuto fa'
if_you_dont_change_your_name: 'Se non modifichi il tuo username in questo momento, il tuo nome Facebook comparirà accanto a tutti i tuoi commenti.'
required_field: 'Campo obbligatorio'
save: Salva
special_characters: 'Gli username possono contenere solo lettere numeri e _'
username: Username
write_your_username: 'Modifica il tuo username'
your_username: 'Il tuo username appare in tutti i commenti che pubblichi.'
done: Finito
edit_comment:
body_input_label: 'Modifica questo commento'
edit_window_expired: 'Non puoi più modificare questo commento. Il periodo di tempo disponibile per modificarlo è terminato. Perchè non ne pubblichi uno nuovo?'
edit_window_expired_close: Chiudi
edit_window_timer_prefix: 'Modifica periodo di tempo: '
minute: minuto
minutes_plural: minuti
save_button: 'Salva modifiche'
second: secondo
seconds_plural: secondi
email:
banned:
body: 'In conformità con le linee guida della comunità The Coral Project, il tuo account è stato bloccato. Non ti è più permesso commentare, segnalare o interagire con la nostra comunità.'
subject: 'Il tuo account è stato bloccato'
confirm:
confirm_email: 'Conferma Email'
has_been_requested: 'Una email di conferma è stata richiesta per i seguenti account:'
if_you_did_not: 'Se non sei stato tu a richiederla puoi tranquillamente ignorare questa email.'
subject: 'Email di conferma'
to_confirm: 'Per confermare l''account visita il seguente link:'
password_change:
body: "La password del tuo account è stata modificata.\n\nSe non sei stato tu a richiedere questa modifica, contattaci a {0}."
subject: '{0} password modificata'
password_reset:
if_you_did: 'Se sei stato tu,'
please_click: 'premi qui per reimpostare la password'
subject: 'Reimposta Password'
we_received_a_request: 'Abbiamo ricevuto una richiesta di reimpostare la password. Se non sei stato tu a richiedere la modifica puoi ignorare questa email.'
suspended:
subject: 'Il tuo account è stato sospeso'
embed_comments_tab: Commenti
embedlink:
copy: 'Copiato negli appunti'
copied: 'Copiato'
error:
ALREADY_EXISTS: 'Risorsa già esistente'
AUTHENTICATION: 'Si è verificato un errore durante il tentativo di autenticazione del tuo account.'
CANNOT_IGNORE_STAFF: 'Non puoi ignorare membri dello stuff.'
COMMENT_PARENT_NOT_VISIBLE: 'Il commento al quale stai rispondendo è stato rimosso o non esiste.'
COMMENT_TOO_SHORT: 'I commenti devono avere più di un carattere, ricontrolla il tuo commento e riprova.'
COMMENT_TOO_LONG: 'Il testo eccede il limite permesso.'
COMMENTING_CLOSED: 'I commenti sono stati chiusi.'
COMMENTING_DISABLED: 'Attualmente non è possibile commentare in questo sito.'
confirm_password: 'Le password non coincidono. Perfavore riprova.'
DELETION_NOT_SCHEDULED: 'L''eliminazione non è stata pianificata.'
EDIT_USERNAME_NOT_AUTHORIZED: 'Non hai l''autorizzazione per modificare il tuo username.'
EDIT_WINDOW_ENDED: 'Non puoi più modificare questo commento. Il periodo di tempo disponibile per farlo è terminato.'
email: 'Inserisci un indirizzo email valido.'
EMAIL_ALREADY_VERIFIED: 'Indirizzo email già verificato.'
EMAIL_IN_USE: 'Indirizzo email già in uso.'
email_not_verified: 'L''indirizzo email {0} non è verificato.'
EMAIL_NOT_VERIFIED: 'Indirizzo email non verificato.'
email_password: 'Email e/o password errate/a.'
EMAIL_REQUIRED: 'È richiesto un indirizzo email'
EMAIL_VERIFICATION_TOKEN_INVALID: 'Il token di verifica dell''e-mail non è valido.'
INCORRECT_PASSWORD: 'Password errata'
INVALID_ASSET_URL: 'URL non valido'
LOGIN_MAXIMUM_EXCEEDED: 'Hai effettuato troppi tentativi falliti. Si prega di attendere.'
network_error: 'Errore durante la connessione con il server. Controlla la tua connessione internet e riprova.'
NO_SPECIAL_CHARACTERS: 'L''username può contenere solo lettere numeri o _'
NOT_AUTHORIZED: 'Non sei autorizzato a compiere questa azione.'
NOT_FOUND: 'Risorsa non trovata'
organization_contact_email: 'L''email dell''organizzazione è invalida.'
organization_name: 'Il nome dell''organizzazione può contenere solo lettere o numeri.'
password: 'La password deve avere almeno 8 caratteri'
PAGE_NOT_AVAILABLE_ROLE: 'Questa pagina può essere utilizzata solo dal team. Contatta l''amministratore se vuoi entrare a far parte di questo team.'
PASSWORD_INCORRECT: 'La password inserita non è corretta'
PASSWORD_LENGTH: 'La password è troppo corta'
PASSWORD_REQUIRED: 'Inserire una password'
PASSWORD_RESET_TOKEN_INVALID: 'Il link per reimpostare la tua password non è valido.'
PROFANITY_ERROR: 'Gli username non devono contenere volgarità. Contatta l''amministratore se ritieni ci sia stato un errore.'
RATE_LIMIT_EXCEEDED: 'Limite di tentativi superato'
required_field: 'Questo campo è obbligatorio'
SAME_USERNAME_PROVIDED: 'Devi inserire un username diverso.'
temporarily_suspended: 'Il tuo account è attualmente sospeso. Sarà riattivato {0}. Contattaci se hai qualche domanda.'
unexpected: 'Si è verificato un errore imprevisto. Ci scusiamo!'
username: 'L''username può contenere solo lettere numeri o _'
USERNAME_IN_USE: 'Username già in uso'
USERNAME_REQUIRED: 'Inserire un username'
flag_comment: 'Segnala commento'
flag_reason: 'Motivi segnalazione (Opzionale)'
flag_reasons:
username:
impersonating: 'Questo utente sta impersonando'
nolike: 'Non mi piace il suo username'
offensive: 'Username offensivo'
other: Altro
spam: 'Sembra si tratti di pubblicità/marketing'
flag_username: 'Segnala username'
flagged_usernames:
notify_approved: '{0} username approvato {1}'
notify_changed: 'utente {0} ha cambiato il suo username in {1}'
notify_flagged: '{0} ha segnalato l''username {1}'
notify_rejected: '{0} ha rifiutato l''username {1}'
flags:
reasons:
comment:
banned_word: 'Parola vietata'
comment_noagree: Disaccordo
comment_offensive: Offensivo
comment_other: Altro
comment_spam: Spam
links: Link
suspect_word: 'Parola sospetta'
trust: Karma
user:
username_impersonating: Impersonificazione
username_nolike: 'Non mi piace'
username_offensive: Offensivo
username_other: Altro
username_spam: Spam
framework:
banned_account_body: 'Significa che non potrai votare, segnalare o scrivere commenti.'
banned_account_header: 'Il tuo account è attualmente bloccato.'
changed_name:
msg: 'Il tuo nuovo username è sotto esame da parte del nostro team di moderatori.'
comment: commento
comment_is_deleted: 'Questo utente ha eliminato il suo account.'
comment_is_hidden: 'Questo commento non è disponibile.'
comment_is_ignored: 'Questo commento è nascosto perchè hai ignorato questo utente.'
comment_is_rejected: 'Hai rifiutato questo commento.'
comments: commenti
configure_stream: Configura
content_not_available: 'Questo contenuto non è disponibile'
edit_name:
button: Invia
error: 'L''username può contenere solo lettere numeri o _'
label: 'Nuovo username'
msg: 'Il tuo account è attualmente sospeso perchè il tuo username è considerato inappropriato. Per ripristinare il tuo account inserisci un nuovo username. Contattaci se hai qualche domanda.'
my_comments: 'I miei commenti'
my_profile: 'Il mio profilo'
new_count: 'Visualizza {0} e {1}'
profile: Profilo
show_all_comments: 'Visualizza tutti i commenti'
show_all_replies: 'Visualizza tutte le risposte'
show_more_replies: 'Visualizza più risposte'
success_bio_update: 'La tua descrizione è stata aggiornata'
success_name_update: 'Il tuo username è stato aggiornato'
success_update_settings: 'Le modifiche apportate sono state applicate al thread dei commenti in questo articolo'
view_more_comments: 'visualizza più commenti'
view_reply: 'visualizza risposta'
from_settings_page: 'Dalla pagina del profilo puoi vedere la tua cronologia dei commenti.'
install:
add_organization:
description: 'Inserisci il nome della tua organizzazione. Questa apparirà nelle email di invito ai nuovi membri.'
label: 'Nome dell''organizzazione'
save: Salva
create:
confirm_password: 'Conferma password'
email: 'Indirizzo email'
organization_contact_email: 'Indirizzo email dell''organizzazione'
password: Password
save: Salva
username: Username
final:
close: 'Chiudi l''installer'
description: 'Grazie per aver installato Talk! Ti abbiamo inviato una email per verificare il tuo indirizzo email. Mentre termini la configurazione del tuo account, puoi iniziare a interagire con i tuoi lettori.'
launch: 'Avvia Talk'
initial:
description: 'Configura la tua comunità Talk in pochi passi.'
submit: Inizia
permitted_domains:
description: 'Inserisci i domini che vuoi autorizzare per Talk. Per esempio i tuoi ambienti di sviluppo, test o produzione (ex. localhost:3000 staging.domain.com domain.com).'
submit: 'Termina installazione'
title: 'Domini autorizzati'
like: 'Mi piace'
loading_results: 'Caricamento risultati'
login:
email_address: 'Indirizzo email'
forgot_password: 'Hai dimenticato la tua password?'
go_back: Indietro
sign_in: Accedi
sign_in_button: Accedi
sign_in_message: 'Accedi per interagire con la comunità.'
password: Password
reset_password_send_button: 'Reimposta Password'
request_passowrd: 'Richiedi una nuova password.'
team_sign_in: 'Accesso Team'
marketing: 'Sembra si tratti di pubblicità/marketing'
moderate_all_streams: 'Modera i commenti in Tutte le storie'
moderate_this_stream: 'Modera questo thread dei commenti'
modqueue:
account: 'segnalazioni dell''account'
actions: Azioni
all: tutto
all_streams: 'Tutti i thread di commenti'
approve: Approva
approved: Approvato
ban_user: Bloccato
ban_user_actions: 'Blocca utente'
billion: M
close: Chiudi
empty_queue: 'Non ci sono più commenti da moderare.'
flagged: contrassegnato
jump_to_queue: 'Vai a una determinata coda'
less_detail: 'Meno dettagli'
likes: 'mi piace'
million: m
mod_faster: 'Modera più velocemente con le scorciatoie da tastiera'
moderate: 'Modera →'
more_detail: 'Più dettagli'
navigation: Navigazione
new: Nuovo
newest_first: 'Nuovo primo'
next_comment: 'Vai al prossimo commento'
next_queue: 'Cambia coda'
notify_accepted: '{0} ha accettato il commento "{1}"'
notify_edited: '{0} ha modificato il commento "{1}"'
notify_flagged: '{0} ha contrassegnato il commento "{1}"'
notify_rejected: '{0} ha rifiutato il commento "{1}"'
notify_reset: '{0} ha ripristinato lo stato del commento "{1}"'
oldest_first: 'Primo più vecchio'
premod: pre-moderazione
prev_comment: 'Vai al commento precedente'
reject: Rifiuta
rejected: Rifiutato
reply: Rispondi
reported: Segnalato
select_stream: 'Seleziona thread dei commenti'
shift_key:
shortcuts: Scorciatoie
show_shortcuts: 'Visualizza scorciatoie'
singleview: 'Modalità zen'
sort: Ordina
suspend: 'Sospendi utente'
system_withheld: 'Sistema bloccato'
thismenu: 'Apri questo menu'
thousand: k
toggle_search: 'Apri ricerca'
try_these: 'Prova queste'
view_more_shortcuts: 'Visualizza più scorciatoie'
my_comment_history: 'La mia cronologia di commenti'
name: Nome
no_agree_comment: 'Non concordo con questo commento'
no_like_bio: 'Non apprezzo questa descrizione'
no_like_username: 'Non apprezzo questo username'
other: Altro
password_reset:
change_password: 'Cambia password'
change_password_help: 'Inserisci una nuova password per accedere'
confirm_new_password: 'Conferma la nuova password'
mail_sent: 'Se hai già un account ti è stata inviata una email con un link per reimpostare la password'
new_password: 'Nuova password'
new_password_help: 'La password deve avere almeno 8 caratteri'
set_new_password: 'Cambia la tua password'
permalink: Condividi
personal_info: 'Questo commento contiene dati personali'
post: Pubblica
profile: Profilo
profile_settings: Impostazioni
reject_username:
description_notify: 'La sospensione di questo utente disabiliterà temporaneamente il loro account.'
description_reject: 'Sei sicuro di volere bloccare temporaneamente questo utente perchè {0}? Accettando sospenderai temporaneamente questo utente finché non riscriveranno il loro {0}.'
email_message_reject: 'Un''altro membro della comunità ha recentemente segnalato il tuo username consigliando una revisione. A causa del suo contenuto, il tuo username è stato rifiutato. Significa che non potrai più commentare, apprezzare o segnalare contenuti fino a quando non modificherai il tuo username. Inviaci una email per qualsiasi domanda o dubbio.'
no_cancel: 'Non annullare'
send: Invia
suspend_user: 'Sospendi utente'
title_notify: 'Avvisa l''utente della sua temporanea sospensione'
title_reject: 'Abbiamo notato che hai rifiutato un nome utente'
username: username
write_message: 'Scrivi un messaggio'
yes_suspend: 'Si, sospendi'
reject_username_dialog:
cancel: Annulla
description: 'Aiutaci a capire'
message: 'Motivi per la segnalazione (Opzionale)'
reason: Ragione
reject_username: 'Rifiuta username'
title: 'Rifiuta username'
reply: Rispondi
report: Segnala
report_notif: 'Grazie per aver segnalato questo commento. Il nostro team di moderatori è stato avvisato e lo revisionerà presto.'
report_notif_remove: 'La tua segnalazione è stata rimossa.'
reported: Segnalato
settings:
from_settings_page: 'Dalla pagina del profilo puoi vedere la tua cronologia dei commenti.'
my_comment_history: 'La mia cronologia dei commenti'
profile: Profilo
profile_settings: 'Impostazioni profilo'
sign_in: Accedi
to_access: 'per accedere al profilo'
user_no_comment: 'Non hai ancora pubblicato un commento. Partecipa alla conversazione!'
step_1_header: 'Segnala un problema'
step_2_header: 'Aiutaci a capire'
step_3_header: 'Grazie per la tua partecipazione'
stream:
all_comments: 'Tutti i commenti'
comment_not_found: 'Questo commento è stato rimosso o non esiste.'
no_comments: 'Non ci sono ancora commenti. Perchè non ne pubblichi uno?'
no_comments_and_closed: 'Non c''era alcun commento in questo articolo.'
temporarily_suspended: 'In conformità con le linee guida della comunità {0}, il tuo account è stato temporaneamente sospeso. Torna a partecipare alla conversazione {1}.'
streams:
all: Tutti
article: Storia
closed: Chiuso
empty_result: 'Questa ricerca non ha ottenuto risultati. Prova ad amplicare la tua ricerca.'
filter_streams: 'Filtra i thread dei commenti'
most_recent_stories: 'Storie più recenti'
newest: Nuovi
no_results: 'Nessun risultato'
oldest: Vecchi
open: Apri
pubdate: 'Data di pubblicazione'
search: Cerca
search_results: 'Risultati della ricerca'
sort_by: 'Ordina per'
status: 'Status del thread dei commenti'
stream_status: 'Status del thread dei commenti'
suspenduser:
cancel: Annulla
day: '{0} giorno'
days: '{0} giorni'
description_notify: 'La sospensione di questo utente disabiliterà temporaneamente il loro account.'
description_suspend: 'Stai sospendendo {0}. Questo commento sarà inserito tra quelli rifiutati, e {0} non potrà apprezzare, segnalare, rispondere o pubblicare fino al termine del periodo di sospensione.'
email_message_suspend: "Caro {0},\n\nIn conformità con le linee guida della comunità, il tuo account è stato temporaneamente sospenso. Durante il periodo di sospension non potrai commentare, segnalare o interagire con la comunità. Puoi tornare a partecipare dal {2}."
hour: '{0} ora'
hours: '{0} ore'
notify_suspend_until: 'L''utente {0} è stato temporaneamente sospeso. Questa sospensione finirà automaticamente {1}.'
one_hour: '1 ora'
select_duration: 'Seleziona la durata della sospensione'
send: Invia
suspend_user: 'Sospendi utente'
title_notify: 'Avvisa l''utente della sua temporanea sospensione'
title_suspend: 'Sospendi utente'
write_message: 'Scrivi un messaggio'
thank_you: 'Apprezziamo il tuo feedback. Un moderatore revisionerà la tua segnalazione.'
user:
bio_flags: 'segnalazioni per questa descrizione'
user_bio: 'Descrizione utente'
username_flags: 'segnalazioni per questo utente'
user_detail:
all: Tutti
ban: 'Blocca utente'
banned: Bloccato
email: Email
id: ID
karma: Karma
karma_docs_link: 'https://docs.coralproject.net/talk/trust/#user-karma-score'
learn_more: 'Maggiori informazioni'
member_since: 'Membro dal'
reject_rate: 'Tasso di rifiuto'
reject_username: 'Username rifiutato'
rejected: Rifiutato
remove_ban: 'Ban rimosso'
remove_suspension: 'Sospensione rimossa'
suspend: 'Utente sospeso'
suspended: Sospeso
total_comments: 'Commenti totali'
unreliable: Inaffidabile
user_history: 'Cronologia utente'
user_karma_score: 'Punteggio Karma dell''utente'
username: Username
username_needs_approval: 'L''username necessita di approvazione'
username_rejected: 'Username rifiutato'
user_history:
action: Azione
ban_removed: 'Ban rimosso'
date: Data
suspended: 'Sospeso, {0}'
suspension_removed: 'Sospensione rimossa'
system: Sistema
taken_by: 'Preso da'
user_banned: 'Utente bloccato'
username_status: 'Username {0}'
user_impersonating: 'Questo utente sta impersonando'
user_no_comment: 'Non hai ancora pubblicato un commento. Partecipa alla discussione!'
username_offensive: 'Questo username è offensivo'
validators:
confirm_email: 'Le email non coincidono. Perfavore riprova.'
confirm_password: 'Le password non coincidono. Perfavore riprova.'
required: 'Questo campo è obbligatorio'
verify_email: 'Inserisci un email valida.'
verify_organization_name: 'Il nome dell''organizzazione può contenere solo lettere o numeri.'
verify_password: 'La password deve contenere almeno 8 caratteri'
verify_username: 'Gli username possono contenere solo lettere numeri e _'
view_conversation: 'Visualizza conversazione'
your_account_has_been_banned: 'Il tuo account è stato bloccato.'
your_account_has_been_suspended: 'Il tuo account è stato temporaneamente sospeso.'
your_username_has_been_rejected: 'Il tuo account è stato sospeso perchè il tuo username è ritenuto inappropriato. Per ripristinare il tuo account inserisci un nuovo username.'
+96 -17
View File
@@ -38,6 +38,9 @@ nl_NL:
name: Naam
post: Plaats
reply: Beantwoord
comment_history_blank:
info: 'Reacties die je hebt geplaatst zullen hier verschijnen'
title: 'Je hebt nog geen reacties geplaatst'
comment_offensive: 'Deze reactie is aanstootgevend'
comment_plural: Reacties
comment_post_banned_word: 'Je reactie bevat een of meerdere termen die wij niet toestaan. Neem contact op wanneer je het er niet mee eens bent.'
@@ -45,7 +48,10 @@ nl_NL:
comment_post_notif_premod: 'Dank voor je reactie! Deze wordt zo snel mogelijk gemodereerd.'
comment_singular: Reactie
common:
contains_link: 'Bevat link'
copy: Kopieer
copied: Gekopieerd
notsupported: 'Niet ondersteund'
error: 'Er is een fout opgetreden.'
reaction: reactie
reactions: reacties
@@ -80,9 +86,11 @@ nl_NL:
spam_ads: Spam/Ads
staff: Staff
status: Status
suspended: Geschorst
username_and_email: 'Gebruikersnaam en e-mailadres'
yes_ban_user: 'Ja, verban gebruiker'
configure:
access_message: 'Je moet een admin zijn om toegang te krijgen tot de configuratie instellingen. Vraag de dichtsbijzijnde Admin om toegang!'
apply: Toepassen
banned_word_text: 'Reacties die deze woorden of zinnen bevatten (niet hoofdlettergevoelig) worden automatisch uit de conversatie verwijderd. Voer een woord in, en druk op enter of Tab om toe te passen. Je kunt ook een kommagescheiden lijst plakken.'
banned_words_title: 'Lijst met verbannen woorden'
@@ -107,6 +115,8 @@ nl_NL:
custom_css_url_desc: 'URL van CSS stylesheet die de standaard Embed Conversatie stylesheet overschrijft. Kan intern of extern zijn.'
days: Dagen
description: 'Als admin kun je de instellingen aanpassen voor de conversatie bij dit verhaal:'
disable_commenting_desc: 'Schrijf een bericht dat wordt weergeven wanneer reageren is afgesloten.'
disable_commenting_title: 'Reacties afsluiten voor de gehele site'
domain_list_text: 'Voer de domeinen in die je wil toestaan voor Talk, bijvoorbeeld je staging en productie-omgevingen (bijv. localhost:3000 staging.domeinnaam.com domeinnaam.com).'
domain_list_title: 'Toegestane domeinnamen'
edit_comment_timeframe_heading: 'Wijzig reactie tijdsduur'
@@ -123,8 +133,8 @@ nl_NL:
enable_questionbox: 'Stel een vraag aan de lezers'
enable_questionbox_description: 'Deze vraag zal bovenaan de conversatie verschijnen. Vraag lezers naar een probleem in het artikel, poneer discussie vragen, enzovoort.'
hours: Uren
include_comment_stream: 'Neem conversatie beschrijving op voor lezers.'
include_comment_stream_desc: 'Schrijf een bericht voor boven je conversatie. Poneer een stelling, wijs op de community richtlijnen, etc.'
code_of_conduct_summary: 'Neem conversatie beschrijving op voor lezers.'
code_of_conduct_summary_desc: 'Schrijf een bericht voor boven je conversatie. Poneer een stelling, wijs op de community richtlijnen, etc.'
include_question_here: 'Schrijf je vraag hier:'
include_text: 'Schrijf hier je tekst.'
moderate: Modereer
@@ -132,9 +142,23 @@ nl_NL:
open: Open
open_stream: 'Open Stream'
open_stream_configuration: 'Deze converstatie is momenteel open. Door deze te sluiten kunnen geen nieuwe reacties meer worden geplaatst, maar de oude zullen nog steeds worden weergegeven.'
organization_contact_email: 'Organisatie Contact E-mailadres'
organization_info_copy: 'We gebruiken deze informatie in e-mail notificaties die door Talk worden verstuurd. Dit verbind de berichten met je organisatie en biedt gebruikers een manier om contact op te nemen mochten ze een probleem met hun account ondervinden.'
organization_info_copy_2: 'We raden aan om een algemeen e-mail account (bijvoorbeeld community@yournewsroom.com) te gebruiken voor dit doeleinde. Dit betekent dat dit adres onveranderd kan blijven en deze geen naam blootstelt die gebruikers kunnen gebruiken mocht hun account geblokkeerd worden.'
organization_information: 'Organisatie Informatie'
organization_name: 'Organisatie Naam'
suspect_or_forbidden_words_placeholder: 'Woord of uitdrukking'
product_guide_link: 'Product Handleiding'
report_bug_or_feedback: 'Rapporteer een bug of geef feedback'
require_email_verification: 'Maak e-mail verificatie vereist'
require_email_verification_text: 'Nieuwe gebruikers moeten hun e-mailadres verifiëren voordat ze door kunnen gaan.'
save_changes: 'Wijzigingen opslaan'
save_changes_dialog:
cancel: Annuleren
copy: 'Je hebt een of meerdere wijzigingen gedaan zonder op te slaan. Wil je je wijzigingen opslaan of ongedaan maken?'
discard: 'Ongedaan maken'
save_settings: 'Instellingen Opslaan'
unsaved_changes: 'Niet-opgeslagen wijzigingen'
shortcuts: Sneltoetsen
sign_out: Uitloggen
stories: Conversaties
@@ -142,7 +166,7 @@ nl_NL:
suspect_word_text: 'Reacties die deze woorden of zinsdelen bevatten (niet hoofdlettergevoelig) zullen worden gemarkeerd in de conversatie. Type een woord en druk op enter of tab om toe te voegen. Je kunt optioneel een komma-gescheiden lijst plakken.'
suspect_word_title: 'Lijst met verdachte woorden'
tech_settings: 'Technische instellingen'
title: 'Conversatie cnfigureren'
title: 'Conversatie configureren'
weeks: Weken
wordlist: 'Zwarte woordenlijst'
confirm_email:
@@ -183,6 +207,9 @@ nl_NL:
if_you_did_not: 'Wanneer jij dit niet hebt aangevraagd, kun je deze e-mail negeren.'
subject: 'Email bevestiging'
to_confirm: 'Ga naar de volgende link om het account te bevestigen:'
password_change:
body: 'Het wachtwoord voor je account is gewijzigd.\n\nAls je deze wijziging niet hebt aangevraagd, neem dan contact met ons op: {0}'
subject: '{0} wachtwoord wijziging'
password_reset:
if_you_did: 'Wanneer je dit zelf was,'
please_click: 'klik dan hier om je wachtwoord te veranderen'
@@ -192,43 +219,61 @@ nl_NL:
embed_comments_tab: Reacties
embedlink:
copy: 'Kopieer naar klembord'
copied: Gekopieerd
error:
ALREADY_EXISTS: 'Bron bestaat al'
AUTHENTICATION: 'Er is een fout opgetreden tijdens het aanmelden van je account'
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.'
COMMENTING_DISABLED: 'Reageren is momenteel uigeschakeld op deze site'
confirm_password: 'Wachtwoorden komen niet overeen. Controleer opnieuw.'
DELETION_NOT_SCHEDULED: 'Verwijdering was niet ingepland'
EDIT_USERNAME_NOT_AUTHORIZED: 'Je bent niet gemachtigd om je gebruikersnaam te wijzigen.'
EDIT_WINDOW_ENDED: 'Dit commentaar kan niet langer worden gewijzigd. Het tijdsbestek is verstreken.'
email: 'Geen geldig e-mailadres'
EMAIL_ALREADY_VERIFIED: 'E-mailadres is reeds geverifiëerd'
EMAIL_IN_USE: 'E-mailadres al in gebruik'
email_not_verified: 'E-mailadres {0} is niet geverifiëerd'
EMAIL_NOT_VERIFIED: 'E-mailadres is niet geverifiëerd'
email_password: 'E-mail en/of wachtwoord combinatie onjuist'
EMAIL_REQUIRED: 'Een e-mailadres is vereist'
EMAIL_VERIFICATION_TOKEN_INVALID: 'Ongeldig e-mail verificatie token'
INCORRECT_PASSWORD: 'Ongeldig wachtwoord'
INVALID_ASSET_URL: 'Ongeldige URL'
LOGIN_MAXIMUM_EXCEEDED: 'Je hebt te veel mislukte inlogpogingen gedaan. Even geduld aub.'
network_error: 'Kon geen verbinding maken met server. Controleer je internetverbinding en probeer het opnieuw.'
NO_SPECIAL_CHARACTERS: 'Gebruikersnamen kunnen alleen cijfers, letters en _ bevatten.'
NOT_AUTHORIZED: 'Je bent niet geauthoriseerd om dit te doen.'
NOT_FOUND: 'Bron niet gevonden'
organization_contact_email: 'Organisatie e-mailadres is niet geldig.'
organization_name: 'Organisatienaam kan alleen cijfers en letters bevatten.'
password: 'Wachtwoord moet minimaal 8 karakters lang zijn'
PAGE_NOT_AVAILABLE_ROLE: 'Deze pagina is enkel voor toegankelijk voor teamleden. Neem contact op met een admin als je toegang tot dit team wilt.'
PASSWORD_INCORRECT: 'Je huidige wachtwoord is incorrect ingevuld'
PASSWORD_LENGTH: 'Wachtwoord is te kort'
PASSWORD_REQUIRED: 'Je moet een wachtwoord invoeren.'
PASSWORD_RESET_TOKEN_INVALID: 'Je wachtwoord-herstel token is ongeldig.'
PROFANITY_ERROR: 'Gebruikersnamen mogen niet aanstootgevend zijn. Neem contact op met de administrator wanneer je denkt dat dit niet klopt.'
RATE_LIMIT_EXCEEDED: 'Gebruikslimiet overschreden'
required_field: 'Dit veld is verplicht'
SAME_USERNAME_PROVIDED: 'Je moet een andere gebruikersnaam invoeren.'
temporarily_suspended: 'Your account is currently suspended. It will be reactivated {0}. Please contact us if you have any questions.'
temporarily_suspended: 'Je account is momenteel geschorst. Deze zal worden gereactiveerd {0}. Neem s.v.p. contact met ons op voor vragen.'
unexpected: 'Onverwachte fout opgetreden. Sorry!'
username: 'Gebruikersnamen kunnen alleen cijfers, letters en _ bevatten.'
USERNAME_IN_USE: 'Gebruikersnaam al in gebruik'
USERNAME_REQUIRED: 'Gebruikersnaam is vereist'
flag_comment: 'Rapporteer reactie'
flag_reason: 'Reden voor rapporteren (optioneel)'
flag_reasons:
username:
impersonating: 'Deze gebruiker doet zich voor als iemand anders'
nolike: 'Ik vind deze gebruikersnaam niet leuk'
offensive: 'Deze gebruikersnaam is aanstootgevend'
other: Andere
spam: 'Dit lijkt op een advertentie/marketing'
flag_username: 'Rapporteer gebruikersnaam'
flagged_usernames:
notify_approved: '{0} heeft gebruikersnaam {1} goedgekeurd'
@@ -239,7 +284,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
@@ -258,6 +302,7 @@ nl_NL:
changed_name:
msg: 'De verandering van je gebruikersnaam wordt door ons moderatieteam gecontroleerd.'
comment: reactie
comment_is_deleted: 'De reageerder heeft zijn account verwijderd.'
comment_is_hidden: 'Deze reactie is niet beschikbaar.'
comment_is_ignored: 'Deze reactie is verborgen omdat je de gebruiker negeert.'
comment_is_rejected: 'Je hebt deze reactie afgewezen.'
@@ -306,6 +351,17 @@ nl_NL:
title: 'Toegestane domeinnamen'
like: Like
loading_results: 'Resultaten worden geladen'
login:
email_address: E-mailadres
forgot_password: 'Wachtwoord vergeten?'
go_back: 'Ga terug'
sign_in: Inloggen
sign_in_button: Inloggen
sign_in_message: 'Log in om te communiceren met je community'
password: Wachtwoord
reset_password_send_button: 'Wachtwoord ophalen'
request_passowrd: 'Vraag een nieuwe aan.'
team_sign_in: 'Team login'
marketing: 'Dit lijkt op een advertentie/marketing'
moderate_this_stream: 'Modereer deze conversatie'
modqueue:
@@ -350,6 +406,8 @@ nl_NL:
show_shortcuts: 'Toon sneltoetsen'
singleview: Zen-modus
sort: Sorteer
suspend: 'Schors gebruiker'
system_withheld: 'Achtergehouden door systeem'
thismenu: 'Open dit menu'
thousand: k
toggle_search: 'Open zoekvenster'
@@ -363,6 +421,7 @@ nl_NL:
other: Ander
password_reset:
change_password: 'Verander wachtwoord'
change_password_help: 'Voer een nieuw wachtwoord in om mee in te loggen. Gebruik een veilig wachtwoord!'
confirm_new_password: 'Bevestig nieuw wachtwoord'
new_password: 'Nieuw wachtwoord'
new_password_help: 'Wachtwoord moet minimaal 8 karakters lang zijn.'
@@ -384,6 +443,13 @@ nl_NL:
username: gebruikersnaam
write_message: 'Schrijf een bericht'
yes_suspend: 'Ja, schors'
reject_username_dialog:
cancel: Annuleren
description: 'Help ons dit te begrijpen'
message: 'Reden voor rapporteren (Optioneel)'
reason: Reden
reject_username: 'Gebruikersnaam afwijzen'
title: 'Gebruikersnaam afwijzen'
reply: Beantwoord
report: Rapporteer
report_notif: 'Dank voor het rapporteren van deze reactie. Deze wordt zo snel mogelijk gemodereerd.'
@@ -443,30 +509,43 @@ nl_NL:
user_detail:
all: Alle
ban: 'Gebruiker verbannen'
email: Email
email: E-mailadres
id: ID
karma: Karma
karma_docs_link: 'https://docs.coralproject.net/talk/trust/#user-karma-score'
learn_more: 'Meer informatie'
member_since: 'Lid sinds'
reject_rate: 'Beoordeling verwijderen'
reject_username: 'Gebruikersnaam afwijzen'
rejected: Afgewezen
remove_ban: 'Verbanning verwijderen'
remove_suspension: 'Schorsing opheffen'
suspend: 'Gebruiker schorsen'
suspended: Geschorst
total_comments: 'Totaal aantal reacties'
unreliable: Onbetrouwbaar
user_history: Accountgeschiedenis
user_karma_score: 'Gebruiker Karma Score'
username: Gebruikersnaam
username_needs_approval: 'Gebruikersnaam heeft goedkeuring nodig'
username_rejected: 'Gebruikersnaam afgewezen'
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: 'Deze gebruiker imiteert'
action: Actie
ban_removed: 'Schorsing opgeheven'
date: Datum
suspended: 'Geschorst, {0}'
suspension_removed: 'Schorsing opgeheven'
system: Systeem
taken_by: 'Genomen door'
user_banned: 'Gebruiker geschorst'
username_status: 'Gebruikersnaam {0}'
user_impersonating: 'Deze gebruiker doet zich voor als iemand anders'
user_no_comment: 'Je hebt nog niet eerder gereageerd. Laat je mening horen!'
username_offensive: 'Dit is een aanstootgevende gebruikersnaam.'
validators:
confirm_password: 'Wachtwoorden komen niet overeen. Controleer opnieuw.'
confirm_email: 'E-mailadressen komen niet overeen. Controleer opnieuw.'
confirm_password: Wachtwoorden komen niet overeen. Controleer opnieuw.
required: 'Dit veld is verplicht'
verify_email: 'Geen geldig e-mailadres'
verify_organization_name: 'Organisatienaam kan alleen cijfers en letters bevatten.'
verify_password: 'Wachtwoord moet minimaal 8 karakters lang zijn'
+77 -9
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'
@@ -126,8 +130,8 @@ pt_BR:
enable_questionbox: 'Faça uma pergunta aos leitores'
enable_questionbox_description: 'Esta pergunta aparecerá no topo da lista de comentários. Pergunte aos leitores sobre um determinado problema no artigo, abra o assunto para discussão, etc.'
hours: Horas
include_comment_stream: 'Incluir descrição da lista comentários para leitores'
include_comment_stream_desc: 'Escreva uma mensagem para adicionar ao topo do seu hilo de comentários. Posicionar um tópico, incluem diretrizes comunitárias, etc.'
code_of_conduct_summary: 'Incluir descrição da lista comentários para leitores'
code_of_conduct_summary_desc: 'Escreva uma mensagem para adicionar ao topo do seu hilo de comentários. Posicionar um tópico, incluem diretrizes comunitárias, etc.'
include_question_here: 'Escreva sua pergunta aqui:'
include_text: 'Inclua seu texto aqui:'
moderate: Moderar
@@ -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'
+2 -2
View File
@@ -114,8 +114,8 @@ zh_CN:
enable_questionbox: 向读者提问
enable_questionbox_description: 该问题将在评论流顶部出现。可以就文章中特定内容发问,也可以设置一个讨论问题,等等。
hours: 小时
include_comment_stream: 开启评论流描述
include_comment_stream_desc: 填写将在评论流顶部出现的描述。可以写评论主题、社群指引方针,等等。
code_of_conduct_summary: 开启评论流描述
code_of_conduct_summary_desc: 填写将在评论流顶部出现的描述。可以写评论主题、社群指引方针,等等。
include_question_here: 此处输入问题:
include_text: 此处输入文本。
moderate: 审查
+2 -2
View File
@@ -113,8 +113,8 @@ zh_TW:
enable_questionbox: 向讀者提問
enable_questionbox_description: 該問題將會在評論流置頂。您可以詢問其他讀者有關文章中的具體時間或是提出討論等
hours: 小時
include_comment_stream: 包含讀者的評論流描述
include_comment_stream_desc: 留言將在您的評論流中置頂。發布一個社區規則等
code_of_conduct_summary: 包含讀者的評論流描述
code_of_conduct_summary_desc: 留言將在您的評論流中置頂。發布一個社區規則等
include_question_here: 請在此處輸入問題:
include_text: 在此插入您的文本
moderate: 審核
+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);
};
+44
View File
@@ -0,0 +1,44 @@
const express = require('express');
const path = require('path');
const staticServer = require('express-static-gzip');
const { merge } = require('lodash');
const { EMBED_EXPIRY_TIME, STATIC_EXPIRY_TIME } = require('../config');
// EMBED_CACHE_CONTROL_HEADER is the header sent when the file is embed.js.
const EMBED_CACHE_CONTROL_HEADER = [
'public',
`max-age=${Math.floor(EMBED_EXPIRY_TIME / 1000)}`,
'immutable',
].join(', ');
// Define the options to be applied to all static files, the embed itself has a
// separate override.
const defaultOpts = {
maxAge: STATIC_EXPIRY_TIME,
immutable: true,
setHeaders: (res, path) => {
if (path.includes('/dist/embed.js')) {
// embed.js has a different max-age then the rest of the static files.
res.setHeader('Cache-Control', EMBED_CACHE_CONTROL_HEADER);
}
},
};
// Setup the configuration for the compression options on how to serve files.
const compressionOpts = {
indexFromEmptyFile: false,
enableBrotli: true,
customCompressions: [{ encodingName: 'deflate', fileExtension: 'zz' }],
};
// Serve the directories under ../dist.
const dist = path.resolve(path.join(__dirname, '../dist'));
/**
* middleware in production will serve compressed files if available, otherwise
* it will use express's static middleware.
*/
module.exports =
process.env.NODE_ENV === 'production'
? staticServer(dist, merge(compressionOpts, defaultOpts))
: express.static(dist, defaultOpts);
+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,
}
);
+16 -6
View File
@@ -1,11 +1,21 @@
const { HTTP_X_REQUEST_ID } = require('../config');
const uuid = require('uuid/v1');
// Trace middleware attaches a request id to each incoming request.
module.exports = (req, res, next) => {
req.id = uuid();
module.exports = HTTP_X_REQUEST_ID
? (req, res, next) => {
req.id = req.get(HTTP_X_REQUEST_ID) || uuid();
// Add the context ID to the request as an HTTP header.
res.set('X-Talk-Trace-ID', req.id);
// Add the context ID to the request as an HTTP header.
res.set('X-Talk-Trace-ID', req.id);
next();
};
next();
}
: (req, res, next) => {
req.id = uuid();
// Add the context ID to the request as an HTTP header.
res.set('X-Talk-Trace-ID', req.id);
next();
};
+9 -6
View File
@@ -1,6 +1,6 @@
{
"name": "talk",
"version": "4.6.2",
"version": "4.7.0",
"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",
@@ -58,6 +58,7 @@
"dependencies": {
"@coralproject/gql-merge": "^0.1.0",
"@coralproject/graphql-anywhere-optimized": "^0.1.0",
"@ungap/url-search-params": "^0.1.2",
"apollo-client": "^1.9.1",
"apollo-engine": "^0.8.1",
"apollo-server-express": "^1.2.0",
@@ -105,7 +106,7 @@
"eventemitter2": "^4.1.2",
"exports-loader": "^0.6.4",
"express": "4.16.0",
"express-static-gzip": "^0.3.1",
"express-static-gzip": "^1.1.1",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^0.11.2",
"final-form": "^4.8.1",
@@ -126,11 +127,11 @@
"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",
"inquirer-autocomplete-prompt": "^0.12.1",
"intersection-observer": "^0.5.1",
"ioredis": "3.1.4",
"ip": "^1.1.5",
"jest": "^23.0.0",
@@ -171,8 +172,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",
@@ -203,13 +205,14 @@
"style-loader": "^0.16.0",
"subscriptions-transport-ws": "^0.7.2",
"supports-color": "^4",
"throng": "^4.0.0",
"timeago.js": "^2.0.3",
"timekeeper": "^1.0.0",
"tlds": "^1.196.0",
"uglifyjs-webpack-plugin": "^1.1.6",
"url-join": "^2.0.2",
"url-loader": "^0.6.0",
"url-search-params": "^0.9.0",
"url-parse": "^1.4.3",
"uuid": "^3.1.0",
"webpack": "^3.10.0",
"webpack-manifest-plugin": "^2.0.0-rc.2",
+1
View File
@@ -10,4 +10,5 @@ export {
getShallowChanges,
createDefaultResponseFragments,
handlePopupAuth,
buildCommentURL,
} from 'coral-framework/utils';
@@ -219,7 +219,7 @@ es:
register: "Registrar"
sign_up: "Registro"
confirm_password: "Confirmar Contraseña"
username: "Nombre"
username: "Nombre de Usuario"
already_have_an_account: "¿Ya tienes una cuenta?"
recover_password: "Recuperar la contraseña"
email_in_use: "Este correo se encuentra en uso"
@@ -297,94 +297,103 @@ fr:
write_your_username: "Modifier votre nom d'utilisateur"
your_username: "Votre nom d'utilisateur apparaît sur chaque commentaire que vous publiez."
nl_NL:
sign_in:
email_verify_cta: "Controleer je e-mailadres."
request_new_verify_email: "Vraag nieuwe bevestigingsemail aan"
verify_email: "Bedankt voor het aanmaken van een account! We hebben een email verstuurd naar het adres dat je hebt opgegeven om je account te verifiëren."
verify_email2: "Je account moet worden geverifiëerd voordat je kunt deelnemen in de community."
not_you: "Ben je dit niet?"
logged_in_as: "Ingelogd als"
facebook_sign_in: "Inloggen met Facebook"
facebook_sign_up: "Registreren met Facebook"
logout: "Uitloggen"
sign_in: "Inloggen"
sign_in_to_join: "Log in om deel te nemen"
or: "Of"
email: "E-mailadres"
password: "Wachtwoord"
forgot_your_pass: "Wachtwoord vergeten?"
need_an_account: "Heb je een account nodig?"
register: "Registreren"
sign_up: "Aanmelden"
confirm_password: "Bevestig wachtwoord"
username: "Gebruikersnaam"
already_have_an_account: "Heb je al een account?"
recover_password: "Wachtwoord herstellen"
email_in_use: "E-mailadres is al in gebruik"
email_or_username_in_use: "E-mailadres of gebruikersnaam is al in gebruik"
required_field: "Dit is een vereist veld"
passwords_dont_match: "Wachtwoorden komen niet overeen"
special_characters: "Gebruikersnamen kunnen alleen letters, cijfers en _ bevatten"
sign_in_to_comment: "Aanmelden om te reageren"
check_the_form: "Het formulier bevat ongeldige invoer, controleer je invoer"
createdisplay:
check_the_form: "Het formulier bevat ongeldige invoer, controleer je invoer"
continue: "Doorgaan met dezelfde Facebook gebruikersnaam"
error_create: "Fout opgetreden bij het wijzigen van de gebruikersnaam"
fake_comment_body: "Dit is een voorbeeldreactie. Lezers kunnen hun gedachten en meningen met newsrooms delen in het reactie-gedeelte"
fake_comment_date: "1 minuut geleden"
if_you_dont_change_your_name: "Wanneer je je gebruikersnaam nu niet wijzigt, zal je Facebook naam bij al je reacties komen te staan."
required_field: "Vereist veld"
save: Opslaan
special_characters: "Gebruikersnamen kunnen alleen letters, cijfers en _ bevatten"
username: Gebruikersnaam
write_your_username: "Wijzig je gebruikersnaam"
your_username: "Je gebruikersnaam verschijnt bij al je reacties."
talk-plugin-auth:
login:
email_verify_cta: "Controleer je e-mailadres."
request_new_verify_email: "Vraag nieuwe bevestigingsemail aan"
verify_email: "Bedankt voor het aanmaken van een account! We hebben een email verstuurd naar het adres dat je hebt opgegeven om je account te verifiëren."
verify_email2: "Je account moet worden geverifiëerd voordat je kunt deelnemen in de community."
not_you: "Ben je dit niet?"
logged_in_as: "Ingelogd als"
logout: "Uitloggen"
sign_in: "Inloggen"
sign_in_to_join: "Log in om deel te nemen"
or: "Of"
email: "E-mailadres"
password: "Wachtwoord"
password_error: "Wachtwoord moet minstens 8 karakters bevatten."
forgot_your_pass: "Wachtwoord vergeten?"
need_an_account: "Heb je een account nodig?"
register: "Registreren"
sign_up: "Aanmelden"
confirm_password: "Bevestig wachtwoord"
username: "Gebruikersnaam"
already_have_an_account: "Heb je al een account?"
recover_password: "Wachtwoord herstellen"
email_in_use: "E-mailadres is al in gebruik"
email_or_username_in_use: "E-mailadres of gebruikersnaam is al in gebruik"
required_field: "Dit is een vereist veld"
passwords_dont_match: "Wachtwoorden komen niet overeen"
special_characters: "Gebruikersnamen kunnen alleen letters, cijfers en _ bevatten"
sign_in_to_comment: "Aanmelden om te reageren"
check_the_form: "Het formulier bevat ongeldige invoer, controleer je invoer"
set_username_dialog:
check_the_form: "Het formulier bevat ongeldige invoer, controleer je invoer"
continue: "Doorgaan met dezelfde Facebook gebruikersnaam"
error_create: "Fout opgetreden bij het wijzigen van de gebruikersnaam"
fake_comment_body: "Dit is een voorbeeldreactie. Lezers kunnen hun gedachten en meningen met newsrooms delen in het reactie-gedeelte"
fake_comment_date: "1 minuut geleden"
if_you_dont_change_your_name: "Wanneer je je gebruikersnaam nu niet wijzigt, zal je Facebook naam bij al je reacties komen te staan."
required_field: "Vereist veld"
save: Opslaan
special_characters: "Gebruikersnamen kunnen alleen letters, cijfers en _ bevatten"
username: Gebruikersnaam
write_your_username: "Wijzig je gebruikersnaam"
your_username: "Je gebruikersnaam verschijnt bij al je reacties."
change_password:
change_password: "Wachtwoord Wijzigen"
passwords_dont_match: "Wachtwoorden komen niet overeen"
required_field: "Dit veld is verplicht"
forgot_password: "Wachtwoord vergeten?"
save: "Opslaan"
cancel: "Annuleren"
edit: "Wijzigen"
changed_password_msg: "Je wachtwoord is succesvol gewijzigd"
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:
@@ -2,6 +2,10 @@ ar:
talk-plugin-facebook-auth:
sign_in: "تسجيل الدخول عبر حساب الفيسبوك"
sign_up: "اشترك عبر حساب الفيسبوك"
de:
talk-plugin-facebook-auth:
sign_in: "Mit Facebook anmelden"
sign_up: "Mit Facebook registrieren"
en:
talk-plugin-facebook-auth:
sign_in: "Sign in with Facebook"
@@ -14,6 +18,10 @@ fr:
talk-plugin-facebook-auth:
sign_in: "Connectez-vous avec Facebook"
sign_up: "Inscrivez-vous avec Facebook"
nl_NL:
talk-plugin-facebook-auth:
sign_in: "Inloggen met Facebook"
sign_up: "Registreren met Facebook"
zh_CN:
talk-plugin-facebook-auth:
sign_in: "使用 Facebook 帐号"
@@ -22,7 +30,3 @@ zh_TW:
talk-plugin-facebook-auth:
sign_in: "使用 Facebook 帳號"
sign_up: "使用 Facebook 帳號"
de:
talk-plugin-facebook-auth:
sign_in: "Mit Facebook anmelden"
sign_up: "Mit Facebook registrieren"
@@ -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,21 @@ 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"
nl_NL:
talk-plugin-google-auth:
sign_in: "Inloggen met Google"
sign_up: "Registeren met 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 帳號"
@@ -52,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"
@@ -90,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: "被忽略用户"
@@ -178,10 +178,12 @@ class Profile extends React.Component {
},
},
notify,
success: hasChangedUsername,
} = this.props;
const { editing, formData, showDialog } = this.state;
const usernameCanBeUpdated = canUsernameBeUpdated(status);
const usernameCanBeUpdated =
canUsernameBeUpdated(status) && !hasChangedUsername;
return (
<section
@@ -232,6 +234,7 @@ class Profile extends React.Component {
validationType="username"
disabled={!usernameCanBeUpdated}
columnDisplay
errorMsg={this.state.errors.newUsername}
>
<div className={styles.bottomText}>
<span>
@@ -307,6 +310,7 @@ Profile.propTypes = {
notify: PropTypes.func.isRequired,
username: PropTypes.string,
emailAddress: PropTypes.string,
success: PropTypes.bool.isRequired,
};
export default Profile;
@@ -1,4 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import { compose, gql } from 'react-apollo';
import { bindActionCreators } from 'redux';
@@ -23,6 +24,14 @@ class AddEmailAddressDialog extends React.Component {
componentDidMount() {
this.props.startAttach();
document.body.style.minHeight = `${
document.getElementById('talk-plugin-local-auth-email-dialog')
.clientHeight
}px`;
}
componentWillUnmount() {
document.body.style.removeProperty('min-height');
}
handleDone = () => {
@@ -63,7 +72,7 @@ class AddEmailAddressDialog extends React.Component {
} = this.props;
return (
<Dialog open={true}>
<Dialog open={true} id="talk-plugin-local-auth-email-dialog">
{step === 0 && <AddEmailForm onSubmit={this.handleSubmit} />}
{step === 1 &&
!requireEmailConfirmation && (

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