Add option to report abuse (#1608)

Closes #1580

Signed-off-by: Sergio Castaño Arteaga <tegioz@icloud.com>
Signed-off-by: Cintia Sanchez Garcia <cynthiasg@icloud.com>
Co-authored-by: Sergio Castaño Arteaga <tegioz@icloud.com>
Co-authored-by: Cintia Sanchez Garcia <cynthiasg@icloud.com>
This commit is contained in:
Sergio C. Arteaga 2021-10-12 17:52:12 +02:00 committed by GitHub
parent 81adbb28fb
commit f86585a80d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 155 additions and 3 deletions

14
.github/ISSUE_TEMPLATE/report-abuse.md vendored Normal file
View File

@ -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.

View File

@ -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

View File

@ -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 }}

View File

@ -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"

View File

@ -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",

View File

@ -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"),

View File

@ -19,6 +19,7 @@
<meta name="artifacthub:googleAuth" content="true" />
<meta name="artifacthub:oidcAuth" content="false" />
<meta name="artifacthub:sampleQueries" content='[{"name":"OLM operators for databases","queryString":"kind=3\u0026ts_query_web=database"},{"name":"Helm Charts provided by Bitnami","queryString":"kind=0\u0026org=bitnami"},{"name":"Packages of any kind related to etcd","queryString":"ts_query_web=etcd"},{"name":"Falco rules for CVE","queryString":"kind=1\u0026ts_query_web=cve"},{"name":"OLM operators in the monitoring category","queryString":"kind=3\u0026ts_query=monitoring"},{"name":"Packages from verified publishers","queryString":"verified_publisher=true"},{"name":"Official Prometheus packages","queryString":"ts_query_web=prometheus\u0026official=true"},{"name":"Operators with auto pilot capabilities","queryString":"capabilities=auto+pilot"},{"name":"Helm Charts in the storage category","queryString":"kind=0\u0026ts_query=storage"},{"name":"Packages with Apache-2.0 license","queryString":"license=Apache-2.0"},{"name":"OPA policies with MIT license","queryString":"kind=2\u0026license=MIT"},{"name":"Helm plugins","queryString":"kind=6"},{"name":"Kubectl plugins","queryString":"kind=5"},{"name":"Tekton tasks","queryString":"kind=7"}]' />
<meta name="artifacthub:reportURL" content="https://github.com/artifacthub/hub/issues/new?labels=abuse+report&template=report-abuse.md" />
<% } else { %>
<title>{{ .title }}</title>
<meta name="description" content="{{ .description }}" />
@ -34,6 +35,7 @@
<meta name="artifacthub:gaTrackingID" content="{{ .gaTrackingID }}" />
<meta name="artifacthub:motd" content="{{ .motd }}" />
<meta name="artifacthub:motdSeverity" content="{{ .motdSeverity }}" />
<meta name="artifacthub:reportURL" content="{{ .reportURL }}" />
<% } %>
<meta property="og:type" content="website" />
<meta property="og:title" content="{{ .title }}" />

View File

@ -18,6 +18,10 @@
right: 0.3rem !important;
}
.icon {
margin-top: 2px;
}
@media only screen and (max-width: 767.98px) {
.dropdown {
width: 18rem;

View File

@ -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(<MoreActionsButton {...defaultProps} />);
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(<MoreActionsButton {...defaultProps} />);
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(<MoreActionsButton {...defaultProps} />);
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();
});
});
});

View File

@ -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<boolean>(props.visibleWidget);
const [currentPkgId, setCurrentPkgId] = useState<string>(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"
>
<div className="d-flex flex-row align-items-center">
<BiCode className="mr-2" />
<BiCode className={`mr-2 position-relative ${styles.icon}`} />
<div>Embed widget</div>
</div>
</button>
{!isNull(reportURL) && reportURL !== '' && (
<button
className="dropdown-item btn btn-sm rounded-0 text-dark"
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
window.open(reportURL, '_blank');
setOpenStatus(false);
}}
aria-label="Open report abuse url"
>
<div className="d-flex flex-row align-items-center">
<GoStop className={`mr-2 position-relative ${styles.icon}`} />
<div>Report abuse</div>
</div>
</button>
)}
</div>
</div>

View File

@ -48,7 +48,7 @@ exports[`MoreActionsButton creates snapshot 1`] = `
class="d-flex flex-row align-items-center"
>
<svg
class="mr-2"
class="mr-2 position-relative icon"
fill="currentColor"
height="1em"
stroke="currentColor"