diff --git a/LICENSE b/LICENSE index 597d8fa73..e0a687532 100644 --- a/LICENSE +++ b/LICENSE @@ -4,8 +4,12 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. -See the License for the specific language governing permissions and limitations under the License. +See the License for the specific language governing permissions +and limitations under the License. diff --git a/README.md b/README.md index a089f040c..89474bf6d 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,37 @@ -# Talk +# Talk · [![CircleCI](https://circleci.com/gh/coralproject/talk.svg?style=svg)](https://circleci.com/gh/coralproject/talk) · [![NSP Status](https://nodesecurity.io/orgs/coralproject/projects/07ce2e4c-99fb-48f8-b50b-69d2d2c081b8/badge)](https://nodesecurity.io/orgs/coralproject/projects/07ce2e4c-99fb-48f8-b50b-69d2d2c081b8) · [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md#pull-requests) -[![CircleCI](https://circleci.com/gh/coralproject/talk.svg?style=svg)](https://circleci.com/gh/coralproject/talk) -[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://dashboard.heroku.com/new?template=https%3A%2F%2Fgithub.com%2Fcoralproject%2Ftalk&env[TALK_FACEBOOK_APP_ID]=ignore&env[TALK_FACEBOOK_APP_SECRET]=ignore) -[![NSP Status](https://nodesecurity.io/orgs/coralproject/projects/07ce2e4c-99fb-48f8-b50b-69d2d2c081b8/badge)](https://nodesecurity.io/orgs/coralproject/projects/07ce2e4c-99fb-48f8-b50b-69d2d2c081b8) - -Online comments are broken. Our open-source Talk tool rethinks how moderation, comment display, and conversation function, creating the opportunity for safer, smarter discussions around your work. [Read more about Talk here](https://coralproject.net/products/talk.html). +Online comments are broken. Our open-source commenting platform, Talk, rethinks how moderation, comment display, and conversation function, creating the opportunity for safer, smarter discussions around your work. [Read more about Talk here](https://coralproject.net/products/talk.html). Built with <3 by The Coral Project & Mozilla. -## Getting Started +## Try Talk! -Check out our Docs: https://coralproject.github.io/talk/ +You're just one click away from trying Talk - all you need is a Heroku account and a few minutes of your time. + +[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://dashboard.heroku.com/new?template=https%3A%2F%2Fgithub.com%2Fcoralproject%2Ftalk&env[TALK_FACEBOOK_APP_ID]=ignore&env[TALK_FACEBOOK_APP_SECRET]=ignore) + +## Technical Documentation + +From getting up and running, to advanced configuration, to how to scale Talk, our [Talk Technical Docs](https://coralproject.github.io/talk/) have everything you need to know. + +## Product Guide + +Learn more about Talk, including a deep dive into features for commenters and moderators, and FAQs in our [Talk Product Guide](https://coralproject.github.io/talk/how-talk-works). ## Relevant Links -- Blog: https://blog.coralproject.net/ -- Community Forums: https://community.coralproject.net/ -- Community Guides for Journalism: https://guides.coralproject.net/ -- Project: https://coralproject.net/ -- Roadmap: https://www.pivotaltracker.com/n/projects/1863625 +- [Our Blog](https://blog.coralproject.net/) +- [Community Forums](https://community.coralproject.net/) +- [Community Guides for Journalism](https://guides.coralproject.net/) +- [More About Us](https://coralproject.net/) +- [Talk Roadmap](https://www.pivotaltracker.com/n/projects/1863625) ## End-to-End Testing -Talk uses [Nightwatch](http://nightwatchjs.org/) to write e2e tests. The testing infrastructure that allows us to run our tests in real browsers is provided with love by our friends at Browserstack. +Talk uses [Nightwatch](https://nightwatchjs.org/) as our e2e testing framework. The testing infrastructure that allows us to run our tests in real browsers is provided with love by our friends at [Browserstack](https://browserstack.com). - -![](/public/img/browserstack_logo.png) +[![Browserstack](/public/img/browserstack_logo.png)](https://browserstack.com) ## License - Copyright 2017 Mozilla Foundation - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - either express or implied. - - See the License for the specific language governing permissions - and limitations under the License. +Talk is released under the [Apache License, v2.0](/LICENSE). diff --git a/circle.yml b/circle.yml index 932643ff6..9f35736c6 100644 --- a/circle.yml +++ b/circle.yml @@ -50,7 +50,7 @@ test: - MOCHA_FILE=$CIRCLE_TEST_REPORTS/junit/test-results.xml MOCHA_REPORTER=mocha-junit-reporter yarn test # Check dependancies using nsp. - nsp check - # - yarn e2e-ci + - yarn e2e-ci deployment: release: diff --git a/client/coral-admin/src/components/CommentLabels.js b/client/coral-admin/src/components/CommentLabels.js index 7af7e9a7e..bff4f575a 100644 --- a/client/coral-admin/src/components/CommentLabels.js +++ b/client/coral-admin/src/components/CommentLabels.js @@ -6,10 +6,21 @@ import FlagLabel from 'coral-ui/components/FlagLabel'; import cn from 'classnames'; import styles from './CommentLabels.css'; +const staffRoles = ['ADMIN', 'STAFF', 'MODERATOR']; + function isUserFlagged(actions) { return actions.some((action) => action.__typename === 'FlagAction' && action.user); } +function getUserFlaggedType(actions) { + return actions + .some((action) => + action.__typename === 'FlagAction' && + action.user && + action.user.roles.some((role) => staffRoles.includes(role)) + ) ? 'Staff' : 'User'; +} + function hasSuspectedWords(actions) { return actions.some((action) => action.__typename === 'FlagAction' && action.reason === 'Matched suspect word filter'); } @@ -24,7 +35,7 @@ const CommentLabels = ({comment, comment: {className, status, actions, hasParent
{hasParent && } {status === 'PREMOD' && } - {isUserFlagged(actions) && User} + {isUserFlagged(actions) && {getUserFlaggedType(actions)}} {hasSuspectedWords(actions) && Suspect} {hasHistoryFlag(actions) && History}
diff --git a/client/coral-admin/src/components/ModerationKeysModal.js b/client/coral-admin/src/components/ModerationKeysModal.js index 45304992c..5f43daf09 100644 --- a/client/coral-admin/src/components/ModerationKeysModal.js +++ b/client/coral-admin/src/components/ModerationKeysModal.js @@ -4,34 +4,38 @@ import Modal from 'components/Modal'; import styles from './ModerationKeysModal.css'; import t from 'coral-framework/services/i18n'; -const shortcuts = [ - { - title: 'modqueue.navigation', - shortcuts: { - 'j': 'modqueue.next_comment', - 'k': 'modqueue.prev_comment', - 'ctrl+f': 'modqueue.toggle_search', - 't': 'modqueue.next_queue', - 's': 'modqueue.singleview', - '?': 'modqueue.thismenu' - } - }, - { - title: 'modqueue.actions', - shortcuts: { - 'd': 'modqueue.approve', - 'f': 'modqueue.reject' - } - } -]; - export default class ModerationKeysModal extends React.Component { static propTypes = { open: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, hideShortcutsNote: PropTypes.func.isRequired, - shortcutsNoteVisible: PropTypes.string.isRequired + shortcutsNoteVisible: PropTypes.string.isRequired, + queueCount: PropTypes.number.isRequired + } + + buildShortcuts = () => { + return [ + { + title: 'modqueue.navigation', + shortcuts: { + 'j': 'modqueue.next_comment', + 'k': 'modqueue.prev_comment', + 'ctrl+f': 'modqueue.toggle_search', + 't': 'modqueue.next_queue', + [`1...${this.props.queueCount}`]: 'modqueue.jump_to_queue', + 's': 'modqueue.singleview', + '?': 'modqueue.thismenu' + } + }, + { + title: 'modqueue.actions', + shortcuts: { + 'd': 'modqueue.approve', + 'f': 'modqueue.reject' + } + } + ]; } render () { @@ -51,7 +55,7 @@ export default class ModerationKeysModal extends React.Component {

{t('modqueue.shortcuts')}

- {shortcuts.map((shortcut, i) => ( + {this.buildShortcuts().map((shortcut, i) => ( diff --git a/client/coral-admin/src/containers/CommentLabels.js b/client/coral-admin/src/containers/CommentLabels.js index 1942d1ad0..1d57aa526 100644 --- a/client/coral-admin/src/containers/CommentLabels.js +++ b/client/coral-admin/src/containers/CommentLabels.js @@ -25,6 +25,7 @@ export default withFragments({ } user { id + roles } } ${getSlotFragmentSpreads(slots, 'comment')} diff --git a/client/coral-admin/src/routes/Moderation/components/Moderation.js b/client/coral-admin/src/routes/Moderation/components/Moderation.js index 64ccb1857..392db36e3 100644 --- a/client/coral-admin/src/routes/Moderation/components/Moderation.js +++ b/client/coral-admin/src/routes/Moderation/components/Moderation.js @@ -34,6 +34,8 @@ class Moderation extends Component { key('k', () => this.select(false)); key('f', () => this.moderate(false)); key('d', () => this.moderate(true)); + this.getMenuItems() + .forEach((menuItem, idx) => key(`${idx + 1}`, () => this.selectQueue(menuItem))); } onClose = () => { @@ -41,20 +43,23 @@ class Moderation extends Component { } nextQueue = () => { - const queueConfig = this.props.queueConfig; const activeTab = this.props.activeTab; - const assetId = this.props.data.variables.asset_id; - const menuItems = Object.keys(queueConfig).map((queue) => ({ - key: queue - })); + const menuItems = this.getMenuItems(); - const activeTabIndex = menuItems.findIndex((item) => item.key === activeTab); + const activeTabIndex = menuItems.findIndex((item) => item === activeTab); const nextQueueIndex = (activeTabIndex === menuItems.length - 1) ? 0 : activeTabIndex + 1; - this.props.router.push(this.props.getModPath(menuItems[nextQueueIndex].key, assetId)); + this.selectQueue(menuItems[nextQueueIndex]); } + selectQueue = (key) => { + const assetId = this.props.data.variables.asset_id; + this.props.router.push(this.props.getModPath(key, assetId)); + } + + getMenuItems = () => Object.keys(this.props.queueConfig); + closeSearch = () => { const {toggleStorySearch} = this.props; toggleStorySearch(false); @@ -175,6 +180,8 @@ class Moderation extends Component { key.unbind('k'); key.unbind('f'); key.unbind('d'); + this.getMenuItems() + .forEach((menuItem, idx) => key.unbind(`${idx + 1}`)); } componentWillReceiveProps(nextProps) { @@ -276,6 +283,7 @@ class Moderation extends Component { shortcutsNoteVisible={moderation.shortcutsNoteVisible} open={moderation.modalOpen} onClose={this.onClose} + queueCount={this.getMenuItems().length} />