diff --git a/scm-ui/ui-webapp/public/locales/de/users.json b/scm-ui/ui-webapp/public/locales/de/users.json index a71757f29e..49c5049cc0 100644 --- a/scm-ui/ui-webapp/public/locales/de/users.json +++ b/scm-ui/ui-webapp/public/locales/de/users.json @@ -83,10 +83,13 @@ "addKey": "Schlüssel hinzufügen", "delete": "Löschen", "download": "Herunterladen", + "text1": "Erstelle und verwalte Personal Access Token um auf die REST API zuzugreifen oder diese als Passwort für SCM-Clients zu nutzen. Die Rechte der Token sind auf Repositories und die gewählte Rolle beschränkt. Sie können die Rollenberechtigungen in der Administration unter „Berechtigungsrollen“ einsehen und neue Rollen anlegen.", + "text2": "Um den Token in REST-Abfragen zu nutzen, übergeben Sie diesen als Cookie mit dem Namen „X-Bearer-Token“. Sie können den Token auch anstelle Ihres Passworts nutzen, um sich mit SCM-Clients anzumelden.", "modal": { "title": "Schlüssel erzeugt", - "text1": "Hier ist der neue Schlüssel. Er kann als \"Bearer Token\" für REST Zugriffe oder als Passwort für SCM Clients genutzt werden. Sie erhalten Ihre Zugriffsrechte, eingeschränkt auf Repositories und die gewählte Rolle.", - "text2": "Dieses ist der einzige Zeitpunkt, an dem der Schlüssel angezeigt wird. Er kann im Nachhinein nicht mehr hergestellt werden. Bewahren Sie ihn an einem sicheren Ort auf.", + "text1": "Ihr neuer API-Schlüssel ist bereit. Sie können diesen als Token für Zugriffe auf die REST-Schnittstelle nutzen oder anstelle Ihres Passworts zum Login mit SCM-Clients nutzen.", + "text2": "Sichern Sie Ihren API-Schlüssel jetzt! Er wird hier einmalig angezeigt und kann später nicht mehr wiederbeschafft werden.", + "clipboard": "In die Zwischenablage kopieren", "close": "Schließen" } } diff --git a/scm-ui/ui-webapp/public/locales/en/users.json b/scm-ui/ui-webapp/public/locales/en/users.json index 7a93c324f3..e5eb934987 100644 --- a/scm-ui/ui-webapp/public/locales/en/users.json +++ b/scm-ui/ui-webapp/public/locales/en/users.json @@ -83,10 +83,13 @@ "addKey": "Add key", "delete": "Delete", "download": "Download", + "text1": "Create and manage personal access tokens to access the REST API or use as a password for SCM clients. The privileges of these tokens are limited to repositories and the selected role. You may view and create roles in the administration view “Permission Roles”.", + "text2": "To use the token in a REST request, pass it as a cookie named “X-Bearer-Token”. You may use the token as your password for SCM clients, too.", "modal": { "title": "Key created", - "text1": "Here is your new key. You can use it as a bearer token for rest calls or as a password for scm clients. Doing so you will have your permissions limited to repositories and the selected role.", - "text2": "This is the only time it will be shown, it cannot be recovered afterwards. Keep it in a safe place.", + "text1": "Your new API key is ready. You can use it as a bearer token for REST calls or as a password for SCM clients.", + "text2": "Store your API key in a safe place now! It is only displayed now and cannot be recovered later.", + "clipboard": "Copy to clipboard", "close": "Close" } } diff --git a/scm-ui/ui-webapp/src/users/components/apiKeys/AddApiKey.tsx b/scm-ui/ui-webapp/src/users/components/apiKeys/AddApiKey.tsx index 8ac9f31039..9698a3af6e 100644 --- a/scm-ui/ui-webapp/src/users/components/apiKeys/AddApiKey.tsx +++ b/scm-ui/ui-webapp/src/users/components/apiKeys/AddApiKey.tsx @@ -34,7 +34,7 @@ import { import { RepositoryRole } from "@scm-manager/ui-types"; import { getRepositoryRolesLink, getRepositoryVerbsLink } from "../../../modules/indexResource"; import RoleSelector from "../../../repos/permissions/components/RoleSelector"; -import { Button, Modal } from "@scm-manager/ui-components"; +import ApiKeyCreatedModal from "./ApiKeyCreatedModal"; type Props = { createLink: string; @@ -58,7 +58,7 @@ const AddApiKey: FC = ({ const [error, setError] = useState(); const [displayName, setDisplayName] = useState(""); const [permissionRole, setPermissionRole] = useState(""); - const [addedPassphrase, setAddedPassphrase] = useState(""); + const [addedKey, setAddedKey] = useState(""); useEffect(() => { if (!availableRepositoryRoles) { @@ -80,7 +80,7 @@ const AddApiKey: FC = ({ apiClient .post(createLink, { displayName: displayName, permissionRole: permissionRole }, CONTENT_TYPE_API_KEY) .then(response => response.text()) - .then(setAddedPassphrase) + .then(setAddedKey) .then(() => setLoading(false)) .catch(setError); }; @@ -98,30 +98,14 @@ const AddApiKey: FC = ({ const closeModal = () => { resetForm(); refresh(); - setAddedPassphrase(""); + setAddedKey(""); }; - const newPassphraseModalContent = ( -
-

{t("apiKey.modal.text1")}

-

{t("apiKey.modal.text2")}

-
{addedPassphrase}
-
- ); - - const newPassphraseModal = addedPassphrase && ( - } - active={true} - /> - ); + const newKeyModal = addedKey && ; return ( <> - {newPassphraseModal} + {newKeyModal} void; +}; + +const KeyArea = styled.textarea` + white-space: nowrap; + overflow: auto; + font-family: "Courier New", Monaco, Menlo, "Ubuntu Mono", "source-code-pro", monospace; +`; + +const ApiKeyCreatedModal: FC = ({ addedKey, close }) => { + const [t] = useTranslation("users"); + const [copied, setCopied] = useState(false); + const keyRef = useRef(null); + + const copy = () => { + keyRef.current.select(); + document.execCommand("copy"); + setCopied(true); + }; + + const newPassphraseModalContent = ( +
+

{t("apiKey.modal.text1")}

+

+ {t("apiKey.modal.text2")} +

+
+
+
+ +
+
+ +
+
+
+ ); + + return ( + } + active={true} + /> + ); +}; + +export default ApiKeyCreatedModal; diff --git a/scm-ui/ui-webapp/src/users/components/apiKeys/ApiKeyEntry.tsx b/scm-ui/ui-webapp/src/users/components/apiKeys/ApiKeyEntry.tsx index 64449e9ab0..ff349d11bf 100644 --- a/scm-ui/ui-webapp/src/users/components/apiKeys/ApiKeyEntry.tsx +++ b/scm-ui/ui-webapp/src/users/components/apiKeys/ApiKeyEntry.tsx @@ -23,9 +23,10 @@ */ import React, { FC } from "react"; -import { DateFromNow } from "@scm-manager/ui-components"; +import { DateFromNow, Icon } from "@scm-manager/ui-components"; import { ApiKey } from "./SetApiKeys"; import { Link } from "@scm-manager/ui-types"; +import { useTranslation } from "react-i18next"; type Props = { apiKey: ApiKey; @@ -33,12 +34,13 @@ type Props = { }; export const ApiKeyEntry: FC = ({ apiKey, onDelete }) => { + const [t] = useTranslation("users"); let deleteButton; if (apiKey?._links?.delete) { deleteButton = ( onDelete((apiKey._links.delete as Link).href)}> - + ); diff --git a/scm-ui/ui-webapp/src/users/components/apiKeys/SetApiKeys.tsx b/scm-ui/ui-webapp/src/users/components/apiKeys/SetApiKeys.tsx index b7c8d8aace..7bab38e685 100644 --- a/scm-ui/ui-webapp/src/users/components/apiKeys/SetApiKeys.tsx +++ b/scm-ui/ui-webapp/src/users/components/apiKeys/SetApiKeys.tsx @@ -27,6 +27,7 @@ import React, { FC, useEffect, useState } from "react"; import { apiClient, ErrorNotification, Loading } from "@scm-manager/ui-components"; import ApiKeyTable from "./ApiKeyTable"; import AddApiKey from "./AddApiKey"; +import { useTranslation } from "react-i18next"; export type ApiKeysCollection = Collection & { _embedded: { @@ -49,6 +50,7 @@ type Props = { }; const SetApiKeys: FC = ({ user }) => { + const [t] = useTranslation("users"); const [error, setError] = useState(); const [loading, setLoading] = useState(false); const [apiKeys, setApiKeys] = useState(undefined); @@ -86,6 +88,11 @@ const SetApiKeys: FC = ({ user }) => { return ( <> +
+

{t("apiKey.text1")}

+

{t("apiKey.text2")}

+
+
{createLink && }