Unverified Commit e331c20d authored by Paul Kaplan's avatar Paul Kaplan Committed by GitHub

Merge pull request #5649 from paulkaplan/no-report-own-comments

Fix permissions issues on studio comments
parents 5b17870c 810465bf
...@@ -18,14 +18,18 @@ const selectCanAddProjects = state => ...@@ -18,14 +18,18 @@ const selectCanAddProjects = state =>
// This isn't "canComment" since they could be muted, but comment composer handles that // This isn't "canComment" since they could be muted, but comment composer handles that
const selectShowCommentComposer = state => selectIsSocial(state); const selectShowCommentComposer = state => selectIsSocial(state);
const selectCanReportComment = state => selectIsSocial(state); const selectCanReportComment = (state, commentUsername) =>
selectIsLoggedIn(state) && selectUsername(state) !== commentUsername;
const selectCanRestoreComment = state => selectIsAdmin(state); const selectCanRestoreComment = state => selectIsAdmin(state);
// On the project page, project owners can delete comments with a confirmation, // On the project page, project owners can delete comments with a confirmation,
// and admins can delete comments without 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. // 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. // Admins can delete comments without a confirmation.
const selectCanDeleteAnyComment = state => selectIsAdmin(state); const selectCanDeleteComment = (state, commentUsername) => {
const selectCanDeleteOwnComment = state => isCreator(state) || isManager(state); if (selectIsAdmin(state)) return true;
if (isManager(state) && selectUsername(state) === commentUsername) return true;
return false;
};
const selectCanDeleteCommentWithoutConfirm = state => selectIsAdmin(state); const selectCanDeleteCommentWithoutConfirm = state => selectIsAdmin(state);
const selectCanFollowStudio = state => selectIsLoggedIn(state); const selectCanFollowStudio = state => selectIsLoggedIn(state);
...@@ -82,8 +86,7 @@ export { ...@@ -82,8 +86,7 @@ export {
selectCanAddProjects, selectCanAddProjects,
selectCanFollowStudio, selectCanFollowStudio,
selectShowCommentComposer, selectShowCommentComposer,
selectCanDeleteAnyComment, selectCanDeleteComment,
selectCanDeleteOwnComment,
selectCanDeleteCommentWithoutConfirm, selectCanDeleteCommentWithoutConfirm,
selectCanReportComment, selectCanReportComment,
selectCanRestoreComment, selectCanRestoreComment,
......
...@@ -131,7 +131,7 @@ class TopLevelComment extends React.Component { ...@@ -131,7 +131,7 @@ class TopLevelComment extends React.Component {
return ( return (
<FlexRow className="comment-container"> <FlexRow className="comment-container">
<Comment <this.props.commentComponent
highlighted={highlightedCommentId === id} highlighted={highlightedCommentId === id}
postURI={postURI} postURI={postURI}
onAddComment={this.handleAddComment} onAddComment={this.handleAddComment}
...@@ -173,7 +173,7 @@ class TopLevelComment extends React.Component { ...@@ -173,7 +173,7 @@ class TopLevelComment extends React.Component {
<React.Fragment <React.Fragment
key={`reply-and-status-${reply.id}`} key={`reply-and-status-${reply.id}`}
> >
<Comment <this.props.commentComponent
author={reply.author} author={reply.author}
canDelete={canDelete} canDelete={canDelete}
canDeleteWithoutConfirm={canDeleteWithoutConfirm} canDeleteWithoutConfirm={canDeleteWithoutConfirm}
...@@ -233,6 +233,7 @@ TopLevelComment.propTypes = { ...@@ -233,6 +233,7 @@ TopLevelComment.propTypes = {
canReply: PropTypes.bool, canReply: PropTypes.bool,
canReport: PropTypes.bool, canReport: PropTypes.bool,
canRestore: PropTypes.bool, canRestore: PropTypes.bool,
commentComponent: PropTypes.func,
content: PropTypes.string, content: PropTypes.string,
datetimeCreated: PropTypes.string, datetimeCreated: PropTypes.string,
defaultExpanded: PropTypes.bool, defaultExpanded: PropTypes.bool,
...@@ -260,7 +261,8 @@ TopLevelComment.defaultProps = { ...@@ -260,7 +261,8 @@ TopLevelComment.defaultProps = {
defaultExpanded: false, defaultExpanded: false,
hasThreadLimit: false, hasThreadLimit: false,
moreRepliesToLoad: false, moreRepliesToLoad: false,
threadHasReplyStatus: false threadHasReplyStatus: false,
commentComponent: Comment
}; };
module.exports = TopLevelComment; module.exports = TopLevelComment;
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'; ...@@ -10,19 +10,17 @@ import TopLevelComment from '../preview/comment/top-level-comment.jsx';
import studioCommentActions from '../../redux/studio-comment-actions.js'; import studioCommentActions from '../../redux/studio-comment-actions.js';
import StudioCommentsAllowed from './studio-comments-allowed.jsx'; import StudioCommentsAllowed from './studio-comments-allowed.jsx';
import StudioCommentsNotAllowed from './studio-comments-not-allowed.jsx'; import StudioCommentsNotAllowed from './studio-comments-not-allowed.jsx';
import {selectIsAdmin, selectHasFetchedSession, selectUsername} from '../../redux/session'; import {selectIsAdmin, selectHasFetchedSession} from '../../redux/session';
import { import {
selectShowCommentComposer, selectShowCommentComposer,
selectCanDeleteAnyComment,
selectCanDeleteOwnComment,
selectCanDeleteCommentWithoutConfirm, selectCanDeleteCommentWithoutConfirm,
selectCanReportComment,
selectCanRestoreComment, selectCanRestoreComment,
selectCanEditCommentsAllowed, selectCanEditCommentsAllowed,
selectShowCommentsList, selectShowCommentsList,
selectShowCommentsGloballyOffError selectShowCommentsGloballyOffError
} from '../../redux/studio-permissions'; } from '../../redux/studio-permissions';
import {selectStudioCommentsAllowed} from '../../redux/studio.js'; import {selectStudioCommentsAllowed} from '../../redux/studio.js';
import StudioComment from './studio-comment.js';
const StudioComments = ({ const StudioComments = ({
comments, comments,
...@@ -37,12 +35,8 @@ const StudioComments = ({ ...@@ -37,12 +35,8 @@ const StudioComments = ({
shouldShowCommentComposer, shouldShowCommentComposer,
shouldShowCommentsList, shouldShowCommentsList,
shouldShowCommentsGloballyOffError, shouldShowCommentsGloballyOffError,
username,
canDeleteAnyComment,
canDeleteOwnComment,
canDeleteCommentWithoutConfirm, canDeleteCommentWithoutConfirm,
canEditCommentsAllowed, canEditCommentsAllowed,
canReportComment,
canRestoreComment, canRestoreComment,
handleDeleteComment, handleDeleteComment,
handleRestoreComment, handleRestoreComment,
...@@ -127,12 +121,9 @@ const StudioComments = ({ ...@@ -127,12 +121,9 @@ const StudioComments = ({
<TopLevelComment <TopLevelComment
hasThreadLimit hasThreadLimit
author={comment.author} author={comment.author}
canDelete={canDeleteAnyComment ||
(canDeleteOwnComment && comment.author.username === username)}
canDeleteWithoutConfirm={canDeleteCommentWithoutConfirm} canDeleteWithoutConfirm={canDeleteCommentWithoutConfirm}
canReply={shouldShowCommentComposer && commentsAllowed}
canReport={canReportComment}
canRestore={canRestoreComment} canRestore={canRestoreComment}
commentComponent={StudioComment}
content={comment.content} content={comment.content}
datetimeCreated={comment.datetime_created} datetimeCreated={comment.datetime_created}
defaultExpanded={singleCommentId} defaultExpanded={singleCommentId}
...@@ -190,12 +181,8 @@ StudioComments.propTypes = { ...@@ -190,12 +181,8 @@ StudioComments.propTypes = {
shouldShowCommentComposer: PropTypes.bool, shouldShowCommentComposer: PropTypes.bool,
shouldShowCommentsGloballyOffError: PropTypes.bool, shouldShowCommentsGloballyOffError: PropTypes.bool,
shouldShowCommentsList: PropTypes.bool, shouldShowCommentsList: PropTypes.bool,
username: PropTypes.string,
canDeleteAnyComment: PropTypes.bool,
canDeleteOwnComment: PropTypes.bool,
canDeleteCommentWithoutConfirm: PropTypes.bool, canDeleteCommentWithoutConfirm: PropTypes.bool,
canEditCommentsAllowed: PropTypes.bool, canEditCommentsAllowed: PropTypes.bool,
canReportComment: PropTypes.bool,
canRestoreComment: PropTypes.bool, canRestoreComment: PropTypes.bool,
handleDeleteComment: PropTypes.func, handleDeleteComment: PropTypes.func,
handleRestoreComment: PropTypes.func, handleRestoreComment: PropTypes.func,
...@@ -217,16 +204,12 @@ export default connect( ...@@ -217,16 +204,12 @@ export default connect(
isAdmin: selectIsAdmin(state), isAdmin: selectIsAdmin(state),
moreCommentsToLoad: state.comments.moreCommentsToLoad, moreCommentsToLoad: state.comments.moreCommentsToLoad,
replies: state.comments.replies, replies: state.comments.replies,
username: selectUsername(state),
commentsAllowed: selectStudioCommentsAllowed(state), commentsAllowed: selectStudioCommentsAllowed(state),
shouldShowCommentComposer: selectShowCommentComposer(state), shouldShowCommentComposer: selectShowCommentComposer(state),
shouldShowCommentsGloballyOffError: selectShowCommentsGloballyOffError(state), shouldShowCommentsGloballyOffError: selectShowCommentsGloballyOffError(state),
shouldShowCommentsList: selectShowCommentsList(state), shouldShowCommentsList: selectShowCommentsList(state),
canDeleteAnyComment: selectCanDeleteAnyComment(state),
canDeleteOwnComment: selectCanDeleteOwnComment(state),
canDeleteCommentWithoutConfirm: selectCanDeleteCommentWithoutConfirm(state), canDeleteCommentWithoutConfirm: selectCanDeleteCommentWithoutConfirm(state),
canEditCommentsAllowed: selectCanEditCommentsAllowed(state), canEditCommentsAllowed: selectCanEditCommentsAllowed(state),
canReportComment: selectCanReportComment(state),
canRestoreComment: selectCanRestoreComment(state), canRestoreComment: selectCanRestoreComment(state),
postURI: `/proxy/comments/studio/${state.studio.id}` postURI: `/proxy/comments/studio/${state.studio.id}`
}), }),
......
import React from 'react'; import React from 'react';
import {mountWithIntl} from '../../helpers/intl-helpers.jsx'; import {mountWithIntl} from '../../helpers/intl-helpers.jsx';
import {StudioComments} from '../../../src/views/studio/studio-comments.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', () => { describe('Studio comments', () => {
test('if there are no comments, they get loaded', () => { test('if there are no comments, they get loaded', () => {
const loadComments = jest.fn(); const loadComments = jest.fn();
......
...@@ -2,8 +2,7 @@ import { ...@@ -2,8 +2,7 @@ import {
selectCanEditInfo, selectCanEditInfo,
selectCanAddProjects, selectCanAddProjects,
selectShowCommentComposer, selectShowCommentComposer,
selectCanDeleteAnyComment, selectCanDeleteComment,
selectCanDeleteOwnComment,
selectCanDeleteCommentWithoutConfirm, selectCanDeleteCommentWithoutConfirm,
selectCanReportComment, selectCanReportComment,
selectCanRestoreComment, selectCanRestoreComment,
...@@ -189,17 +188,22 @@ describe('studio comments', () => { ...@@ -189,17 +188,22 @@ describe('studio comments', () => {
describe('can report comment', () => { describe('can report comment', () => {
test.each([ test.each([
['logged in', true], ['logged in', true],
['unconfirmed', false], ['unconfirmed', true],
['logged out', false], ['logged out', false],
['muted creator', true], ['muted creator', true],
['muted logged in', true] ['muted logged in', true]
])('%s: %s', (role, expected) => { ])('%s: %s', (role, expected) => {
setStateByRole(role); 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([ test.each([
['admin', true], ['admin', true],
['curator', false], ['curator', false],
...@@ -212,13 +216,13 @@ describe('studio comments', () => { ...@@ -212,13 +216,13 @@ describe('studio comments', () => {
['muted logged in', false] ['muted logged in', false]
])('%s: %s', (role, expected) => { ])('%s: %s', (role, expected) => {
setStateByRole(role); 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([ test.each([
['admin', false], // This is false here because we check for `canDeleteAnyComment` separately ['admin', true],
['curator', false], ['curator', false],
['manager', true], ['manager', true],
['creator', true], ['creator', true],
...@@ -231,7 +235,8 @@ describe('studio comments', () => { ...@@ -231,7 +235,8 @@ describe('studio comments', () => {
['muted logged in', false] ['muted logged in', false]
])('%s: %s', (role, expected) => { ])('%s: %s', (role, expected) => {
setStateByRole(role); setStateByRole(role);
expect(selectCanDeleteOwnComment(state)).toBe(expected); const loggedInUsername = selectUsername(state);
expect(selectCanDeleteComment(state, loggedInUsername)).toBe(expected);
}); });
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment