mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-27 17:45:52 -05:00
Merge pull request #5649 from paulkaplan/no-report-own-comments
Fix permissions issues on studio comments
This commit is contained in:
commit
e331c20d99
6 changed files with 53 additions and 37 deletions
|
@ -18,14 +18,18 @@ const selectCanAddProjects = state =>
|
|||
// This isn't "canComment" since they could be muted, but comment composer handles that
|
||||
const selectShowCommentComposer = state => selectIsSocial(state);
|
||||
|
||||
const selectCanReportComment = state => selectIsSocial(state);
|
||||
const selectCanReportComment = (state, commentUsername) =>
|
||||
selectIsLoggedIn(state) && selectUsername(state) !== commentUsername;
|
||||
const selectCanRestoreComment = state => selectIsAdmin(state);
|
||||
// On the project page, project owners can delete comments with a confirmation,
|
||||
// and admins can delete comments without a confirmation.
|
||||
// On the studio page, studio creators and managers have the ability to delete *their own* comments with confirmation.
|
||||
// Admins can delete comments without a confirmation.
|
||||
const selectCanDeleteAnyComment = state => selectIsAdmin(state);
|
||||
const selectCanDeleteOwnComment = state => isCreator(state) || isManager(state);
|
||||
const selectCanDeleteComment = (state, commentUsername) => {
|
||||
if (selectIsAdmin(state)) return true;
|
||||
if (isManager(state) && selectUsername(state) === commentUsername) return true;
|
||||
return false;
|
||||
};
|
||||
const selectCanDeleteCommentWithoutConfirm = state => selectIsAdmin(state);
|
||||
|
||||
const selectCanFollowStudio = state => selectIsLoggedIn(state);
|
||||
|
@ -82,8 +86,7 @@ export {
|
|||
selectCanAddProjects,
|
||||
selectCanFollowStudio,
|
||||
selectShowCommentComposer,
|
||||
selectCanDeleteAnyComment,
|
||||
selectCanDeleteOwnComment,
|
||||
selectCanDeleteComment,
|
||||
selectCanDeleteCommentWithoutConfirm,
|
||||
selectCanReportComment,
|
||||
selectCanRestoreComment,
|
||||
|
|
|
@ -131,7 +131,7 @@ class TopLevelComment extends React.Component {
|
|||
|
||||
return (
|
||||
<FlexRow className="comment-container">
|
||||
<Comment
|
||||
<this.props.commentComponent
|
||||
highlighted={highlightedCommentId === id}
|
||||
postURI={postURI}
|
||||
onAddComment={this.handleAddComment}
|
||||
|
@ -173,7 +173,7 @@ class TopLevelComment extends React.Component {
|
|||
<React.Fragment
|
||||
key={`reply-and-status-${reply.id}`}
|
||||
>
|
||||
<Comment
|
||||
<this.props.commentComponent
|
||||
author={reply.author}
|
||||
canDelete={canDelete}
|
||||
canDeleteWithoutConfirm={canDeleteWithoutConfirm}
|
||||
|
@ -233,6 +233,7 @@ TopLevelComment.propTypes = {
|
|||
canReply: PropTypes.bool,
|
||||
canReport: PropTypes.bool,
|
||||
canRestore: PropTypes.bool,
|
||||
commentComponent: PropTypes.func,
|
||||
content: PropTypes.string,
|
||||
datetimeCreated: PropTypes.string,
|
||||
defaultExpanded: PropTypes.bool,
|
||||
|
@ -260,7 +261,8 @@ TopLevelComment.defaultProps = {
|
|||
defaultExpanded: false,
|
||||
hasThreadLimit: false,
|
||||
moreRepliesToLoad: false,
|
||||
threadHasReplyStatus: false
|
||||
threadHasReplyStatus: false,
|
||||
commentComponent: Comment
|
||||
};
|
||||
|
||||
module.exports = TopLevelComment;
|
||||
|
|
17
src/views/studio/studio-comment.js
Normal file
17
src/views/studio/studio-comment.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import {connect} from 'react-redux';
|
||||
import Comment from '../preview/comment/comment.jsx';
|
||||
|
||||
import {
|
||||
selectCanDeleteComment,
|
||||
selectCanReportComment,
|
||||
selectShowCommentComposer
|
||||
} from '../../redux/studio-permissions';
|
||||
import {selectStudioCommentsAllowed} from '../../redux/studio.js';
|
||||
|
||||
export default connect(
|
||||
(state, ownProps) => ({
|
||||
canReport: selectCanReportComment(state, ownProps.author.username),
|
||||
canDelete: selectCanDeleteComment(state, ownProps.author.username),
|
||||
canReply: selectShowCommentComposer(state) && selectStudioCommentsAllowed(state)
|
||||
})
|
||||
)(Comment);
|
|
@ -10,19 +10,17 @@ import TopLevelComment from '../preview/comment/top-level-comment.jsx';
|
|||
import studioCommentActions from '../../redux/studio-comment-actions.js';
|
||||
import StudioCommentsAllowed from './studio-comments-allowed.jsx';
|
||||
import StudioCommentsNotAllowed from './studio-comments-not-allowed.jsx';
|
||||
import {selectIsAdmin, selectHasFetchedSession, selectUsername} from '../../redux/session';
|
||||
import {selectIsAdmin, selectHasFetchedSession} from '../../redux/session';
|
||||
import {
|
||||
selectShowCommentComposer,
|
||||
selectCanDeleteAnyComment,
|
||||
selectCanDeleteOwnComment,
|
||||
selectCanDeleteCommentWithoutConfirm,
|
||||
selectCanReportComment,
|
||||
selectCanRestoreComment,
|
||||
selectCanEditCommentsAllowed,
|
||||
selectShowCommentsList,
|
||||
selectShowCommentsGloballyOffError
|
||||
} from '../../redux/studio-permissions';
|
||||
import {selectStudioCommentsAllowed} from '../../redux/studio.js';
|
||||
import StudioComment from './studio-comment.js';
|
||||
|
||||
const StudioComments = ({
|
||||
comments,
|
||||
|
@ -37,12 +35,8 @@ const StudioComments = ({
|
|||
shouldShowCommentComposer,
|
||||
shouldShowCommentsList,
|
||||
shouldShowCommentsGloballyOffError,
|
||||
username,
|
||||
canDeleteAnyComment,
|
||||
canDeleteOwnComment,
|
||||
canDeleteCommentWithoutConfirm,
|
||||
canEditCommentsAllowed,
|
||||
canReportComment,
|
||||
canRestoreComment,
|
||||
handleDeleteComment,
|
||||
handleRestoreComment,
|
||||
|
@ -127,12 +121,9 @@ const StudioComments = ({
|
|||
<TopLevelComment
|
||||
hasThreadLimit
|
||||
author={comment.author}
|
||||
canDelete={canDeleteAnyComment ||
|
||||
(canDeleteOwnComment && comment.author.username === username)}
|
||||
canDeleteWithoutConfirm={canDeleteCommentWithoutConfirm}
|
||||
canReply={shouldShowCommentComposer && commentsAllowed}
|
||||
canReport={canReportComment}
|
||||
canRestore={canRestoreComment}
|
||||
commentComponent={StudioComment}
|
||||
content={comment.content}
|
||||
datetimeCreated={comment.datetime_created}
|
||||
defaultExpanded={singleCommentId}
|
||||
|
@ -190,12 +181,8 @@ StudioComments.propTypes = {
|
|||
shouldShowCommentComposer: PropTypes.bool,
|
||||
shouldShowCommentsGloballyOffError: PropTypes.bool,
|
||||
shouldShowCommentsList: PropTypes.bool,
|
||||
username: PropTypes.string,
|
||||
canDeleteAnyComment: PropTypes.bool,
|
||||
canDeleteOwnComment: PropTypes.bool,
|
||||
canDeleteCommentWithoutConfirm: PropTypes.bool,
|
||||
canEditCommentsAllowed: PropTypes.bool,
|
||||
canReportComment: PropTypes.bool,
|
||||
canRestoreComment: PropTypes.bool,
|
||||
handleDeleteComment: PropTypes.func,
|
||||
handleRestoreComment: PropTypes.func,
|
||||
|
@ -217,16 +204,12 @@ export default connect(
|
|||
isAdmin: selectIsAdmin(state),
|
||||
moreCommentsToLoad: state.comments.moreCommentsToLoad,
|
||||
replies: state.comments.replies,
|
||||
username: selectUsername(state),
|
||||
commentsAllowed: selectStudioCommentsAllowed(state),
|
||||
shouldShowCommentComposer: selectShowCommentComposer(state),
|
||||
shouldShowCommentsGloballyOffError: selectShowCommentsGloballyOffError(state),
|
||||
shouldShowCommentsList: selectShowCommentsList(state),
|
||||
canDeleteAnyComment: selectCanDeleteAnyComment(state),
|
||||
canDeleteOwnComment: selectCanDeleteOwnComment(state),
|
||||
canDeleteCommentWithoutConfirm: selectCanDeleteCommentWithoutConfirm(state),
|
||||
canEditCommentsAllowed: selectCanEditCommentsAllowed(state),
|
||||
canReportComment: selectCanReportComment(state),
|
||||
canRestoreComment: selectCanRestoreComment(state),
|
||||
postURI: `/proxy/comments/studio/${state.studio.id}`
|
||||
}),
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import React from 'react';
|
||||
import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
|
||||
|
||||
import {StudioComments} from '../../../src/views/studio/studio-comments.jsx';
|
||||
|
||||
// Replace customized studio comment with default comment to avoid redux issues in the test
|
||||
jest.mock('../../../src/views/studio/studio-comment.js', () => (
|
||||
jest.requireActual('../../../src/views/preview/comment/comment.jsx')
|
||||
));
|
||||
|
||||
describe('Studio comments', () => {
|
||||
test('if there are no comments, they get loaded', () => {
|
||||
const loadComments = jest.fn();
|
||||
|
|
|
@ -2,8 +2,7 @@ import {
|
|||
selectCanEditInfo,
|
||||
selectCanAddProjects,
|
||||
selectShowCommentComposer,
|
||||
selectCanDeleteAnyComment,
|
||||
selectCanDeleteOwnComment,
|
||||
selectCanDeleteComment,
|
||||
selectCanDeleteCommentWithoutConfirm,
|
||||
selectCanReportComment,
|
||||
selectCanRestoreComment,
|
||||
|
@ -189,17 +188,22 @@ describe('studio comments', () => {
|
|||
describe('can report comment', () => {
|
||||
test.each([
|
||||
['logged in', true],
|
||||
['unconfirmed', false],
|
||||
['unconfirmed', true],
|
||||
['logged out', false],
|
||||
['muted creator', true],
|
||||
['muted logged in', true]
|
||||
])('%s: %s', (role, expected) => {
|
||||
setStateByRole(role);
|
||||
expect(selectCanReportComment(state)).toBe(expected);
|
||||
expect(selectCanReportComment(state, 'not me')).toBe(expected);
|
||||
});
|
||||
test('cannot report your own comment', () => {
|
||||
setStateByRole('logged in');
|
||||
const loggedInUsername = selectUsername(state);
|
||||
expect(selectCanReportComment(state, loggedInUsername)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('can delete any comment', () => {
|
||||
describe('can delete others comments', () => {
|
||||
test.each([
|
||||
['admin', true],
|
||||
['curator', false],
|
||||
|
@ -212,13 +216,13 @@ describe('studio comments', () => {
|
|||
['muted logged in', false]
|
||||
])('%s: %s', (role, expected) => {
|
||||
setStateByRole(role);
|
||||
expect(selectCanDeleteAnyComment(state)).toBe(expected);
|
||||
expect(selectCanDeleteComment(state, 'not me')).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('can delete own comment', () => {
|
||||
describe('can delete my own comment', () => {
|
||||
test.each([
|
||||
['admin', false], // This is false here because we check for `canDeleteAnyComment` separately
|
||||
['admin', true],
|
||||
['curator', false],
|
||||
['manager', true],
|
||||
['creator', true],
|
||||
|
@ -231,7 +235,8 @@ describe('studio comments', () => {
|
|||
['muted logged in', false]
|
||||
])('%s: %s', (role, expected) => {
|
||||
setStateByRole(role);
|
||||
expect(selectCanDeleteOwnComment(state)).toBe(expected);
|
||||
const loggedInUsername = selectUsername(state);
|
||||
expect(selectCanDeleteComment(state, loggedInUsername)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue