From 36498c86f78a61c5f4790420a47cac85a5effa7b Mon Sep 17 00:00:00 2001 From: Kathryn Beaty Date: Tue, 19 Dec 2023 10:54:23 -0500 Subject: [PATCH 1/4] enable anonymous dsa reporting --- .../routes/Reports/ReportRowContainer.tsx | 15 +++++- .../routes/Reports/SingleReportRoute.tsx | 16 +++++-- .../Comments/Comment/CommentContainer.tsx | 2 + .../Comment/ReportFlow/ReportButton.tsx | 24 +++++++++- .../Comment/ReportFlow/ReportCommentForm.tsx | 46 ++++++++++++++++++- .../ReportFlow/ReportCommentFormContainer.tsx | 3 ++ .../ReportFlow/ReportFlowContainer.tsx | 12 ++++- .../IllegalContentReportViewContainer.tsx | 6 +-- locales/en-US/admin.ftl | 3 ++ locales/en-US/stream.ftl | 1 + .../core/server/graph/schema/schema.graphql | 3 +- .../src/core/server/locales/en-US/common.ftl | 2 + .../core/server/models/dsaReport/report.ts | 5 +- .../server/services/dsaReports/download.ts | 11 ++++- 14 files changed, 132 insertions(+), 17 deletions(-) diff --git a/client/src/core/client/admin/routes/Reports/ReportRowContainer.tsx b/client/src/core/client/admin/routes/Reports/ReportRowContainer.tsx index 9650179c6b..bfd522321a 100644 --- a/client/src/core/client/admin/routes/Reports/ReportRowContainer.tsx +++ b/client/src/core/client/admin/routes/Reports/ReportRowContainer.tsx @@ -1,3 +1,4 @@ +import { Localized } from "@fluent/react/compat"; import { useRouter } from "found"; import React, { useCallback } from "react"; import { graphql } from "react-relay"; @@ -89,7 +90,19 @@ const ReportRowContainer: React.FunctionComponent = ({ dsaReport }) => {
-
)} - {dsaReport.reporter?.username} + + {dsaReport.reporter ? ( + dsaReport.reporter.username ?? ( + + Username not available + + ) + ) : ( + + Anonymous user + + )} + {dsaReport.referenceID}
diff --git a/client/src/core/client/admin/routes/Reports/SingleReportRoute.tsx b/client/src/core/client/admin/routes/Reports/SingleReportRoute.tsx index 78914c36ae..e1c1ac8e29 100644 --- a/client/src/core/client/admin/routes/Reports/SingleReportRoute.tsx +++ b/client/src/core/client/admin/routes/Reports/SingleReportRoute.tsx @@ -193,13 +193,19 @@ const SingleReportRoute: FunctionComponent & { uppercase={false} onClick={() => onShowUserDrawer(dsaReport.reporter?.id)} > -
{dsaReport.reporter.username}
+ {dsaReport.reporter.username ? ( +
{dsaReport.reporter.username}
+ ) : ( + +
+ Reporter name not available +
+
+ )} ) : ( - -
- Reporter name not available -
+ +
Anonymous user
)} diff --git a/client/src/core/client/stream/tabs/Comments/Comment/CommentContainer.tsx b/client/src/core/client/stream/tabs/Comments/Comment/CommentContainer.tsx index 17c455350b..56ad7cba76 100644 --- a/client/src/core/client/stream/tabs/Comments/Comment/CommentContainer.tsx +++ b/client/src/core/client/stream/tabs/Comments/Comment/CommentContainer.tsx @@ -749,6 +749,7 @@ export const CommentContainer: FunctionComponent = ({ open={showReportFlow} viewer={viewer} comment={comment} + settings={settings} /> )} @@ -920,6 +921,7 @@ const enhanced = withShowAuthPopupMutation( ...UserTagsContainer_settings ...ArchivedReportFlowContainer_settings ...AuthorBadgesContainer_settings + ...ReportButton_settings } `, })(CommentContainer) diff --git a/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportButton.tsx b/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportButton.tsx index 25938cf450..fd9d5419ce 100644 --- a/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportButton.tsx +++ b/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportButton.tsx @@ -15,6 +15,7 @@ import { Flex, MatchMedia } from "coral-ui/components/v2"; import { Button } from "coral-ui/components/v3"; import { ReportButton_comment } from "coral-stream/__generated__/ReportButton_comment.graphql"; +import { ReportButton_settings } from "coral-stream/__generated__/ReportButton_settings.graphql"; import { ReportButton_viewer } from "coral-stream/__generated__/ReportButton_viewer.graphql"; import styles from "./ReportButton.css"; @@ -26,6 +27,7 @@ interface Props { showAuthPopup: MutationProp; comment: ReportButton_comment; viewer: ReportButton_viewer | null; + settings: ReportButton_settings; } const ReportButton: FunctionComponent = ({ @@ -33,6 +35,7 @@ const ReportButton: FunctionComponent = ({ showAuthPopup, comment, viewer, + settings, open, }) => { const isLoggedIn = !!viewer; @@ -47,6 +50,18 @@ const ReportButton: FunctionComponent = ({ void showAuthPopup({ view: "SIGN_IN" }); }, [showAuthPopup]); + const handleOnClick = useCallback(() => { + if (settings.dsa?.enabled) { + onClick(); + return; + } + if (isLoggedIn) { + onClick(); + } else { + signIn(); + } + }, [isLoggedIn, onClick, signIn, settings]); + if (isReported) { return ( = ({ fontSize="small" fontWeight="semiBold" paddingSize="extraSmall" - onClick={isLoggedIn ? onClick : signIn} + onClick={handleOnClick} data-testid="comment-report-button" > @@ -131,6 +146,13 @@ const enhanced = withShowAuthPopupMutation( } } `, + settings: graphql` + fragment ReportButton_settings on Settings { + dsa { + enabled + } + } + `, })(ReportButton) ); diff --git a/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportCommentForm.tsx b/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportCommentForm.tsx index 6e1b252eb4..8228de2c23 100644 --- a/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportCommentForm.tsx +++ b/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportCommentForm.tsx @@ -18,7 +18,7 @@ import { ButtonSvgIcon, ShareExternalLinkIcon, } from "coral-ui/components/icons"; -import { Flex, RadioButton } from "coral-ui/components/v2"; +import { Flex, HorizontalGutter, RadioButton } from "coral-ui/components/v2"; import { Button, ValidationMessage } from "coral-ui/components/v3"; import { ReportCommentFormLocal } from "coral-stream/__generated__/ReportCommentFormLocal.graphql"; @@ -51,6 +51,7 @@ interface Props { onSubmit: OnSubmit; biosEnabled: boolean; reportLink: string; + anonymousWithDSA: boolean; } export interface FormProps { @@ -68,6 +69,7 @@ const ReportCommentForm: FunctionComponent = ({ id, reportLink, biosEnabled, + anonymousWithDSA, }) => { const [{ dsaFeaturesEnabled }] = useLocal( graphql` @@ -84,6 +86,48 @@ const ReportCommentForm: FunctionComponent = ({ emitReportEvent({ commentID: id }); }, []); + if (anonymousWithDSA) { + return ( +
+
+ +
Report This Comment
+
+ + +
You have to sign in to report a comment
+
+ +
+
+
+ ); + } + return (
void; + anonymousWithDSA: boolean; } const ReportCommentFormContainer: FunctionComponent = ({ comment, settings, onClose, + anonymousWithDSA, }) => { const [done, setDone] = useState(false); const dontAgreeMutation = useMutation(CreateCommentDisagreeMutation); @@ -109,6 +111,7 @@ const ReportCommentFormContainer: FunctionComponent = ({ onCancel={onClose} biosEnabled={settings.memberBios} reportLink={reportLink} + anonymousWithDSA={anonymousWithDSA} /> ); } diff --git a/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportFlowContainer.tsx b/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportFlowContainer.tsx index 171b3ac50f..a6173e5771 100644 --- a/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportFlowContainer.tsx +++ b/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportFlowContainer.tsx @@ -25,8 +25,14 @@ const ReportFlowContainer: FunctionComponent = ({ const onFormClose = useCallback(() => { onClose(); }, [onClose]); + + let anonymousWithDSA = false; if (!viewer) { - return null; + if (!settings.dsa?.enabled) { + return null; + } else { + anonymousWithDSA = true; + } } return ( @@ -34,6 +40,7 @@ const ReportFlowContainer: FunctionComponent = ({ comment={comment} onClose={onFormClose} settings={settings} + anonymousWithDSA={anonymousWithDSA} /> ); }; @@ -41,6 +48,9 @@ const ReportFlowContainer: FunctionComponent = ({ const enhanced = withFragmentContainer({ settings: graphql` fragment ReportFlowContainer_settings on Settings { + dsa { + enabled + } ...ReportCommentFormContainer_settings } `, diff --git a/client/src/core/client/stream/tabs/Comments/IllegalContentReportView/IllegalContentReportViewContainer.tsx b/client/src/core/client/stream/tabs/Comments/IllegalContentReportView/IllegalContentReportViewContainer.tsx index 41f80a16ff..046373b9b5 100644 --- a/client/src/core/client/stream/tabs/Comments/IllegalContentReportView/IllegalContentReportViewContainer.tsx +++ b/client/src/core/client/stream/tabs/Comments/IllegalContentReportView/IllegalContentReportViewContainer.tsx @@ -92,12 +92,12 @@ const IllegalContentReportViewContainer: FunctionComponent = (props) => { const onSubmit = useCallback( async (input: FormProps, form: FormApi) => { const statuses = []; - if (viewer && comment) { + if (comment) { if (additionalComments) { for (const c of additionalComments) { try { await createDSAReport({ - userID: viewer.id, + userID: viewer?.id ?? null, commentID: c.id, lawBrokenDescription: input.lawBrokenDescription, additionalInformation: input.additionalInformation, @@ -117,7 +117,7 @@ const IllegalContentReportViewContainer: FunctionComponent = (props) => { const url = getURLWithCommentID(story.url, comment.id); try { await createDSAReport({ - userID: viewer.id, + userID: viewer?.id ?? null, commentID: comment.id, lawBrokenDescription: input.lawBrokenDescription, additionalInformation: input.additionalInformation, diff --git a/locales/en-US/admin.ftl b/locales/en-US/admin.ftl index e2c5bff43f..91401f1ba9 100644 --- a/locales/en-US/admin.ftl +++ b/locales/en-US/admin.ftl @@ -1937,6 +1937,9 @@ reports-decisionModal-detailedExplanationTextarea = reports-relatedReports-label = Related reports reports-relatedReports-reportIDLabel = Report ID +reports-anonymousUser = Anonymous user +reports-username-not-available = Username not available + # Control panel controlPanel-redis-redis = Redis diff --git a/locales/en-US/stream.ftl b/locales/en-US/stream.ftl index 3c65e33f98..199f049bed 100644 --- a/locales/en-US/stream.ftl +++ b/locales/en-US/stream.ftl @@ -749,6 +749,7 @@ comments-reportPopover-receivedMessage = comments-reportPopover-dismiss = Dismiss comments-reportForm-reportIllegalContent-button = This comment contains illegal content +comments-reportForm-signInToReport = You have to sign in to report a comment ## Archived Report Comment Popover diff --git a/server/src/core/server/graph/schema/schema.graphql b/server/src/core/server/graph/schema/schema.graphql index 3345039c46..c3388a0970 100644 --- a/server/src/core/server/graph/schema/schema.graphql +++ b/server/src/core/server/graph/schema/schema.graphql @@ -6464,7 +6464,6 @@ input SettingsInput { """ flairBadges: FlairBadgeConfigurationInput - """ dsa specifies the configuration for DSA European Union moderation and reporting features. @@ -6910,7 +6909,7 @@ input CreateDSAReportInput { """ id of the user who submitted the DSAReport """ - userID: ID! + userID: ID """ lawBrokenDescription is the text entered by the submitting user to describe diff --git a/server/src/core/server/locales/en-US/common.ftl b/server/src/core/server/locales/en-US/common.ftl index 81fedc75f1..ec4f48384b 100644 --- a/server/src/core/server/locales/en-US/common.ftl +++ b/server/src/core/server/locales/en-US/common.ftl @@ -43,6 +43,8 @@ dsaReportCSV-status-awaitingReview = Awaiting review dsaReportCSV-status-inReview = In review dsaReportCSV-status-completed = Completed dsaReportCSV-status-void = Void +dsaReportCSV-anonymousUser = Anonymous user +dsaReportCSV-usernameNotAvailable = Username not available # Notifications diff --git a/server/src/core/server/models/dsaReport/report.ts b/server/src/core/server/models/dsaReport/report.ts index 4b5b6d18a2..991eab9dd1 100644 --- a/server/src/core/server/models/dsaReport/report.ts +++ b/server/src/core/server/models/dsaReport/report.ts @@ -227,9 +227,10 @@ export async function createDSAReport( submissionIDToUse = uuid(); } - // shorter, url-friendly referenceID generated from the report id, userID, and commentID + // shorter, url-friendly referenceID generated from the report id, userID / submissionID, and commentID + const firstID = userID ?? submissionIDToUse; const referenceID = - userID.slice(0, 4) + "-" + commentID.slice(0, 4) + "-" + id.slice(0, 4); + firstID.slice(0, 4) + "-" + commentID.slice(0, 4) + "-" + id.slice(0, 4); // defaults are the properties set by the application when a new DSAReport is // created. diff --git a/server/src/core/server/services/dsaReports/download.ts b/server/src/core/server/services/dsaReports/download.ts index a4cc7dd54c..5f2efde957 100644 --- a/server/src/core/server/services/dsaReports/download.ts +++ b/server/src/core/server/services/dsaReports/download.ts @@ -141,7 +141,16 @@ export async function sendReportDownload( // Write report info cell data to CSV csv.write([ formatter.format(report.createdAt), - reporter?.username, + reporter + ? reporter.username + ? reporter.username + : translate( + bundle, + "Username not available", + "dsaReportCSV-usernameNotAvailable" + ) + : translate(bundle, "Anonymous user", "dsaReportCSV-anonymousUser"), + translate(bundle, "Anonymous user", "dsaReportCSV-anonymousUser"), translate(bundle, "Report submitted", "dsaReportCSV-reportSubmitted"), reportInfo, ]); From 84d99bb8c6023db918e1a38a0f99d35ae2d64871 Mon Sep 17 00:00:00 2001 From: Kathryn Beaty Date: Tue, 19 Dec 2023 11:23:12 -0500 Subject: [PATCH 2/4] add tests; update to account for potential null reporter --- .../comments/stream/reportComment.spec.tsx | 22 +++++++++++++++++++ .../core/server/models/dsaReport/report.ts | 2 +- .../server/services/dsaReports/download.ts | 5 ++++- .../server/services/dsaReports/reports.ts | 4 ++-- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/client/src/core/client/stream/test/comments/stream/reportComment.spec.tsx b/client/src/core/client/stream/test/comments/stream/reportComment.spec.tsx index 3d845f5374..19bcea850f 100644 --- a/client/src/core/client/stream/test/comments/stream/reportComment.spec.tsx +++ b/client/src/core/client/stream/test/comments/stream/reportComment.spec.tsx @@ -212,3 +212,25 @@ it("report comment includes link to report illegal content", async () => { "https://www.test.com/story-0?commentID=comment-0&view=illegalContentReport" ); }); + +it("can report illegal content when not signed in", async () => { + const commentID = stories[0].comments.edges[0].node.id; + await createTestRenderer({ Query: { viewer: () => null } }); + const comment = await screen.findByTestId(`comment-${commentID}`); + const reportButton = screen.getByRole("button", { + name: "Report comment by Markus", + }); + userEvent.click(reportButton); + const form = within(comment).getByTestId("report-comment-form"); + const reportIllegalContentButton = within(form).getByRole("link", { + name: "This comment contains illegal content share-external-link-1", + }); + expect(reportIllegalContentButton).toHaveAttribute( + "href", + "https://www.test.com/story-0?commentID=comment-0&view=illegalContentReport" + ); + const signInToReport = within(form).getByText( + "You have to sign in to report a comment" + ); + expect(signInToReport).toBeVisible(); +}); diff --git a/server/src/core/server/models/dsaReport/report.ts b/server/src/core/server/models/dsaReport/report.ts index 991eab9dd1..d6505c10b8 100644 --- a/server/src/core/server/models/dsaReport/report.ts +++ b/server/src/core/server/models/dsaReport/report.ts @@ -72,7 +72,7 @@ export interface DSAReport extends TenantResource { /** * userID is the id of the user who reported this comment for illegal content. */ - userID: string; + userID?: string | null; /** * createdAt is the date that this DSAReport was created diff --git a/server/src/core/server/services/dsaReports/download.ts b/server/src/core/server/services/dsaReports/download.ts index 5f2efde957..94eeedf55d 100644 --- a/server/src/core/server/services/dsaReports/download.ts +++ b/server/src/core/server/services/dsaReports/download.ts @@ -59,7 +59,10 @@ export async function sendReportDownload( name: `report-${report.referenceID}-${Math.abs(now.getTime())}.csv`, }); - const reporter = await retrieveUser(mongo, tenant.id, report.userID); + let reporter = null; + if (report.userID) { + reporter = await retrieveUser(mongo, tenant.id, report.userID); + } let reportedComment = await retrieveComment( mongo.comments(), diff --git a/server/src/core/server/services/dsaReports/reports.ts b/server/src/core/server/services/dsaReports/reports.ts index caa303adba..3e49ba9733 100644 --- a/server/src/core/server/services/dsaReports/reports.ts +++ b/server/src/core/server/services/dsaReports/reports.ts @@ -31,7 +31,7 @@ import { AugmentedRedis } from "../redis"; export interface CreateDSAReportInput { commentID: string; - userID: string; + userID?: string | null; lawBrokenDescription: string; additionalInformation: string; submissionID?: string; @@ -253,7 +253,7 @@ export async function makeDSAReportDecision( } const report = await retrieveDSAReport(mongo, tenant.id, reportID); - if (report) { + if (report && report.userID) { await notifications.create(tenant.id, tenant.locale, { targetUserID: report.userID, type: GQLNOTIFICATION_TYPE.DSA_REPORT_DECISION_MADE, From 4b4f8a6b02924d9477e84ca2331ddb00317669a1 Mon Sep 17 00:00:00 2001 From: Kathryn Beaty Date: Tue, 19 Dec 2023 14:21:06 -0500 Subject: [PATCH 3/4] remove extra line in csv download --- server/src/core/server/services/dsaReports/download.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/core/server/services/dsaReports/download.ts b/server/src/core/server/services/dsaReports/download.ts index 94eeedf55d..f41abbf4c1 100644 --- a/server/src/core/server/services/dsaReports/download.ts +++ b/server/src/core/server/services/dsaReports/download.ts @@ -153,7 +153,6 @@ export async function sendReportDownload( "dsaReportCSV-usernameNotAvailable" ) : translate(bundle, "Anonymous user", "dsaReportCSV-anonymousUser"), - translate(bundle, "Anonymous user", "dsaReportCSV-anonymousUser"), translate(bundle, "Report submitted", "dsaReportCSV-reportSubmitted"), reportInfo, ]); From 72fbdee86d39a7995a7334f93bb7e01824d472db Mon Sep 17 00:00:00 2001 From: Kathryn Beaty Date: Tue, 19 Dec 2023 14:33:29 -0500 Subject: [PATCH 4/4] refactor --- .../Comment/ReportFlow/ReportCommentForm.tsx | 67 +++++++------------ 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportCommentForm.tsx b/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportCommentForm.tsx index 8228de2c23..e3037366e9 100644 --- a/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportCommentForm.tsx +++ b/client/src/core/client/stream/tabs/Comments/Comment/ReportFlow/ReportCommentForm.tsx @@ -45,6 +45,29 @@ const RadioField: FunctionComponent< ); +const IllegalContentLink: FunctionComponent<{ reportLink: string }> = ({ + reportLink, +}) => ( + +); + interface Props { id: string; onCancel: () => void; @@ -101,27 +124,7 @@ const ReportCommentForm: FunctionComponent = ({
You have to sign in to report a comment
- +
@@ -228,27 +231,7 @@ const ReportCommentForm: FunctionComponent = ({ {dsaFeaturesEnabled && ( - + )}