diff --git a/.github/ISSUE_TEMPLATE/report-abuse.md b/.github/ISSUE_TEMPLATE/report-abuse.md new file mode 100644 index 00000000..75740e87 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/report-abuse.md @@ -0,0 +1,14 @@ +--- +name: Report abuse +about: Report abuse or inappropriate content +title: "[ABUSE-REPORT] Repository or package name" +labels: abuse report +assignees: '' + +--- + +**Describe the problem** +Please explain why are you reporting this content. + +**Additional context** +Add any other context about the problem here. diff --git a/charts/artifact-hub/Chart.yaml b/charts/artifact-hub/Chart.yaml index b833650d..5ac69c4f 100644 --- a/charts/artifact-hub/Chart.yaml +++ b/charts/artifact-hub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: artifact-hub description: Artifact Hub is a web-based application that enables finding, installing, and publishing Kubernetes packages. type: application -version: 1.3.1-1 +version: 1.3.1-2 appVersion: 1.3.0 kubeVersion: ">= 1.14.0-0" home: https://artifacthub.io diff --git a/charts/artifact-hub/templates/hub_secret.yaml b/charts/artifact-hub/templates/hub_secret.yaml index 7a648029..dd31add9 100644 --- a/charts/artifact-hub/templates/hub_secret.yaml +++ b/charts/artifact-hub/templates/hub_secret.yaml @@ -85,6 +85,7 @@ stringData: openGraphImage: {{ .Values.hub.theme.images.openGraphImage | quote }} shortcutIcon: {{ .Values.hub.theme.images.shortcutIcon | quote }} websiteLogo: {{ .Values.hub.theme.images.websiteLogo | quote }} + reportURL: {{ .Values.hub.theme.reportURL | quote }} sampleQueries: {{- range .Values.hub.theme.sampleQueries }} - name: {{ .name }} diff --git a/charts/artifact-hub/values-production.yaml b/charts/artifact-hub/values-production.yaml index d732b2c9..a1b21794 100644 --- a/charts/artifact-hub/values-production.yaml +++ b/charts/artifact-hub/values-production.yaml @@ -58,6 +58,7 @@ hub: enabled: true redirectURL: https://artifacthub.io/oauth/google/callback theme: + reportURL: https://github.com/artifacthub/hub/issues/new?labels=abuse+report&template=report-abuse.md sampleQueries: - name: OLM operators for databases queryString: "kind=3&ts_query_web=database" diff --git a/charts/artifact-hub/values.schema.json b/charts/artifact-hub/values.schema.json index c21ca930..d246bd08 100644 --- a/charts/artifact-hub/values.schema.json +++ b/charts/artifact-hub/values.schema.json @@ -588,6 +588,11 @@ }, "required": ["appleTouchIcon192", "appleTouchIcon512", "openGraphImage", "shortcutIcon", "websiteLogo"] }, + "reportURL": { + "title": "Abuse report URL", + "description": "URL to report abuses.", + "type": "string" + }, "sampleQueries": { "title": "Sample search queries used in home and no results found pages", "type": "array", diff --git a/internal/handlers/static/handlers.go b/internal/handlers/static/handlers.go index ed061ec1..94e3f92d 100644 --- a/internal/handlers/static/handlers.go +++ b/internal/handlers/static/handlers.go @@ -156,6 +156,7 @@ func (h *Handlers) Index(w http.ResponseWriter, r *http.Request) { "oidcAuth": h.cfg.IsSet("server.oauth.oidc"), "openGraphImage": openGraphImage, "primaryColor": h.cfg.GetString("theme.colors.primary"), + "reportURL": h.cfg.GetString("theme.reportURL"), "secondaryColor": h.cfg.GetString("theme.colors.secondary"), "shortcutIcon": h.cfg.GetString("theme.images.shortcutIcon"), "siteName": h.cfg.GetString("theme.siteName"), diff --git a/web/public/index.html b/web/public/index.html index f59fcd23..aa17b1f6 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -19,6 +19,7 @@ + <% } else { %> {{ .title }} @@ -34,6 +35,7 @@ + <% } %> diff --git a/web/src/layout/package/MoreActionsButton.module.css b/web/src/layout/package/MoreActionsButton.module.css index 50f4c4e3..7126606c 100644 --- a/web/src/layout/package/MoreActionsButton.module.css +++ b/web/src/layout/package/MoreActionsButton.module.css @@ -18,6 +18,10 @@ right: 0.3rem !important; } +.icon { + margin-top: 2px; +} + @media only screen and (max-width: 767.98px) { .dropdown { width: 18rem; diff --git a/web/src/layout/package/MoreActionsButton.test.tsx b/web/src/layout/package/MoreActionsButton.test.tsx index b98a969c..8c021401 100644 --- a/web/src/layout/package/MoreActionsButton.test.tsx +++ b/web/src/layout/package/MoreActionsButton.test.tsx @@ -20,6 +20,9 @@ jest.mock('react-router-dom', () => ({ }), })); +const openMock = jest.fn(); +window.open = openMock; + describe('MoreActionsButton', () => { afterEach(() => { jest.resetAllMocks(); @@ -78,5 +81,104 @@ describe('MoreActionsButton', () => { }, }); }); + + describe('report abuse', () => { + it('opens url', () => { + Object.defineProperty(document, 'querySelector', { + value: (selector: any) => { + switch (selector) { + case `meta[name='artifacthub:reportURL']`: + return { + getAttribute: () => 'http://test.com', + }; + default: + return false; + } + }, + writable: true, + }); + + render(); + + const dropdown = screen.getByRole('menu'); + expect(dropdown).toBeInTheDocument(); + expect(dropdown).not.toHaveClass('show'); + + const btn = screen.getByRole('button', { name: 'Open menu' }); + expect(btn).toBeInTheDocument(); + + userEvent.click(btn); + + expect(dropdown).toHaveClass('show'); + + const reportURLBtn = screen.getByRole('button', { name: 'Open report abuse url' }); + expect(reportURLBtn).toBeInTheDocument(); + + userEvent.click(reportURLBtn); + + expect(openMock).toHaveBeenCalledTimes(1); + expect(openMock).toHaveBeenCalledWith('http://test.com', '_blank'); + }); + + it('does not render when url is undefined', () => { + Object.defineProperty(document, 'querySelector', { + value: (selector: any) => { + switch (selector) { + case `meta[name='artifacthub:reportURL']`: + return { + getAttribute: () => null, + }; + default: + return false; + } + }, + writable: true, + }); + + render(); + + const dropdown = screen.getByRole('menu'); + expect(dropdown).toBeInTheDocument(); + expect(dropdown).not.toHaveClass('show'); + + const btn = screen.getByRole('button', { name: 'Open menu' }); + expect(btn).toBeInTheDocument(); + + userEvent.click(btn); + + expect(dropdown).toHaveClass('show'); + expect(screen.queryByRole('button', { name: 'Open report abuse url' })).toBeNull(); + }); + }); + + it('does not render when url is an empty string', () => { + Object.defineProperty(document, 'querySelector', { + value: (selector: any) => { + switch (selector) { + case `meta[name='artifacthub:reportURL']`: + return { + getAttribute: () => '', + }; + default: + return false; + } + }, + writable: true, + }); + + render(); + + const dropdown = screen.getByRole('menu'); + expect(dropdown).toBeInTheDocument(); + expect(dropdown).not.toHaveClass('show'); + + const btn = screen.getByRole('button', { name: 'Open menu' }); + expect(btn).toBeInTheDocument(); + + userEvent.click(btn); + + expect(dropdown).toHaveClass('show'); + expect(screen.queryByRole('button', { name: 'Open report abuse url' })).toBeNull(); + }); }); }); diff --git a/web/src/layout/package/MoreActionsButton.tsx b/web/src/layout/package/MoreActionsButton.tsx index 127b2ec3..39da3bff 100644 --- a/web/src/layout/package/MoreActionsButton.tsx +++ b/web/src/layout/package/MoreActionsButton.tsx @@ -1,10 +1,13 @@ import classnames from 'classnames'; +import { isNull } from 'lodash'; import React, { useEffect, useRef, useState } from 'react'; import { BiCode } from 'react-icons/bi'; +import { GoStop } from 'react-icons/go'; import { HiDotsVertical } from 'react-icons/hi'; import useOutsideClick from '../../hooks/useOutsideClick'; import { SearchFiltersURL } from '../../types'; +import getMetaTag from '../../utils/getMetaTag'; import styles from './MoreActionsButton.module.css'; import WidgetModal from './WidgetModal'; @@ -21,6 +24,7 @@ const MoreActionsButton = (props: Props) => { const [openStatus, setOpenStatus] = useState(false); const [visibleWidget, setVisibleWidget] = useState(props.visibleWidget); const [currentPkgId, setCurrentPkgId] = useState(props.packageId); + const reportURL = getMetaTag('reportURL'); const ref = useRef(null); useOutsideClick([ref], openStatus, () => setOpenStatus(false)); @@ -69,10 +73,28 @@ const MoreActionsButton = (props: Props) => { aria-label="Open embed widget modal" >
- +
Embed widget
+ + {!isNull(reportURL) && reportURL !== '' && ( + + )} diff --git a/web/src/layout/package/__snapshots__/MoreActionsButton.test.tsx.snap b/web/src/layout/package/__snapshots__/MoreActionsButton.test.tsx.snap index 85f790a8..4315116c 100644 --- a/web/src/layout/package/__snapshots__/MoreActionsButton.test.tsx.snap +++ b/web/src/layout/package/__snapshots__/MoreActionsButton.test.tsx.snap @@ -48,7 +48,7 @@ exports[`MoreActionsButton creates snapshot 1`] = ` class="d-flex flex-row align-items-center" >