From bdd70dd73910c11f63508977b314dca95dc0213e Mon Sep 17 00:00:00 2001 From: MariusDoe <41512063+MariusDoe@users.noreply.github.com> Date: Fri, 9 Aug 2024 14:32:43 +0200 Subject: [PATCH] 624 permissions enhancements 2 (#640) * Remove translation for essential permission * Add iconClassName prop to HelpTooltip * Add tooltips to PermissionsView * Fix spelling mistake * Adjust UsersView title * Adjust translation of comment pinning * Remove unused function * Add warning when editing permissions of the public user * Add tip to "invalid forgot-password link" error message * Show password requirements in change/reset password views --- .../src/components/common/HelpTooltip.tsx | 20 ++- .../views/admin/user/ChangePasswordView.tsx | 1 + .../views/admin/user/PermissionsView.tsx | 168 ++++++++++++------ .../views/admin/user/ResetPasswordView.tsx | 1 + .../user/permissions/BooleanParamater.tsx | 14 +- .../views/admin/user/permissions/presets.ts | 6 +- .../bp-gallery/src/hooks/sequences.hook.tsx | 8 - .../bp-gallery/src/shared/locales/de.json | 75 +++++++- 8 files changed, 209 insertions(+), 84 deletions(-) diff --git a/projects/bp-gallery/src/components/common/HelpTooltip.tsx b/projects/bp-gallery/src/components/common/HelpTooltip.tsx index 81625e5c9..1a8d6ad3d 100644 --- a/projects/bp-gallery/src/components/common/HelpTooltip.tsx +++ b/projects/bp-gallery/src/components/common/HelpTooltip.tsx @@ -1,11 +1,21 @@ -import { ReactNode } from 'react'; import { HelpOutline } from '@mui/icons-material'; import { IconButton, Tooltip, Typography } from '@mui/material'; -import { styled } from '@mui/material/styles'; import { tooltipClasses } from '@mui/material/Tooltip'; +import { styled } from '@mui/material/styles'; +import { ReactNode } from 'react'; export const HelpTooltip = styled( - ({ title, content, className }: { title: ReactNode; content: ReactNode; className?: string }) => ( + ({ + title, + content, + popupClassName, + iconClassName, + }: { + title: ReactNode; + content: ReactNode; + popupClassName?: string; + iconClassName?: string; + }) => ( @@ -13,9 +23,9 @@ export const HelpTooltip = styled(

{content}

} - classes={{ popper: className }} + classes={{ popper: popupClassName }} > - +
diff --git a/projects/bp-gallery/src/components/views/admin/user/ChangePasswordView.tsx b/projects/bp-gallery/src/components/views/admin/user/ChangePasswordView.tsx index b4e4762e3..ebaa9d201 100644 --- a/projects/bp-gallery/src/components/views/admin/user/ChangePasswordView.tsx +++ b/projects/bp-gallery/src/components/views/admin/user/ChangePasswordView.tsx @@ -40,6 +40,7 @@ export const ChangePasswordView = () => { +

{t('admin.passwordRequirements')}

; +export type PermissionParameters = Pick; const PermissionsView = ({ userId }: { userId: string }) => { const { t } = useTranslation(); @@ -127,8 +128,26 @@ const PermissionsView = ({ userId }: { userId: string }) => { const dialog = useDialog(); + const warnedAboutPublicUserEditing = useRef(false); + const warnAboutPublicUserEditing = useCallback(async () => { + if (warnedAboutPublicUserEditing.current || !isPublic) { + return; + } + const really = await dialog({ + preset: DialogPreset.CONFIRM, + title: t('admin.permissions.reallyEditPublicUserTitle'), + content: t('admin.permissions.reallyEditPublicUserContent'), + }); + if (really) { + warnedAboutPublicUserEditing.current = true; + return; + } + return Promise.race([]); // never resolves - prevent the actual editing from happening + }, [warnedAboutPublicUserEditing, isPublic, dialog, t]); + const addPermission = useCallback( - async (operation: Operation, { archive_tag, ...parameters }: Parameters) => { + async (operation: Operation, { archive_tag, ...parameters }: PermissionParameters) => { + await warnAboutPublicUserEditing(); await createPermission({ variables: { operation_name: operation.document.name, @@ -138,14 +157,23 @@ const PermissionsView = ({ userId }: { userId: string }) => { }, }); }, - [createPermission, parsedUserId] + [warnAboutPublicUserEditing, createPermission, parsedUserId] + ); + + const removePermission = useCallback( + async (options: Parameters[0]) => { + await warnAboutPublicUserEditing(); + await deletePermission(options); + }, + [warnAboutPublicUserEditing, deletePermission] ); const addPreset = useCallback( async ( - operations: { operation: Operation; parameters: ParametersWithoutArchive }[], + operations: { operation: Operation; parameters: PermissionParametersWithoutArchive }[], archive: FlatArchiveTag | null ) => { + await warnAboutPublicUserEditing(); await Promise.all( operations.map(async ({ operation, parameters }) => { await addPermission(operation, { archive_tag: archive ?? undefined, ...parameters }); @@ -153,7 +181,7 @@ const PermissionsView = ({ userId }: { userId: string }) => { ); await refetchPermissions(); }, - [addPermission, refetchPermissions] + [warnAboutPublicUserEditing, addPermission, refetchPermissions] ); const toggleOperations = useCallback( @@ -164,6 +192,7 @@ const PermissionsView = ({ userId }: { userId: string }) => { header: string, prompt = false ) => { + await warnAboutPublicUserEditing(); const removeAll = coverage !== Coverage.NONE; if (prompt) { const really = await dialog({ @@ -184,7 +213,7 @@ const PermissionsView = ({ userId }: { userId: string }) => { if (!permission) { return; } - await deletePermission({ + await removePermission({ variables: { id: permission.id, }, @@ -200,7 +229,15 @@ const PermissionsView = ({ userId }: { userId: string }) => { } await refetchPermissions(); }, - [dialog, t, findPermission, deletePermission, addPermission, refetchPermissions] + [ + warnAboutPublicUserEditing, + dialog, + t, + findPermission, + removePermission, + addPermission, + refetchPermissions, + ] ); const [filter, setFilter] = useState(''); @@ -220,6 +257,12 @@ const PermissionsView = ({ userId }: { userId: string }) => { : archive ? archive.name : t('admin.permissions.withoutArchive'); + const summaryTooltip = + type === 'system' + ? t('admin.permissions.systemPermissionsTooltip') + : archive + ? t('admin.permissions.archiveTooltip', { archiveName: archive.name }) + : t('admin.permissions.withoutArchiveTooltip'); if (!summary.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) { return null; } @@ -287,6 +330,7 @@ const PermissionsView = ({ userId }: { userId: string }) => { clickThrough /> +
{relevantPresets.length > 0 && ( @@ -304,46 +348,62 @@ const PermissionsView = ({ userId }: { userId: string }) => { )}
- {sectionsWithCoverages.map(section => ( - - }> - group.coverage))} - operations={section.groups.flatMap(group => group.operations)} - archive={archive} - label={t(`admin.permissions.section.${section.name}`, { context: type })} - prompt - toggleOperations={toggleOperations} - clickThrough - /> - - - {section.groups - .map(group => [t(`admin.permissions.group.${group.name}`), group] as const) - .sort(([a], [b]) => a.localeCompare(b)) - .map(([name, group]) => ( - // div forces every checkbox on a separate line -
- - -
- ))} -
-
- ))} + {sectionsWithCoverages.map(section => { + const label = t(`admin.permissions.section.${section.name}`, { context: type }); + return ( + + }> + group.coverage))} + operations={section.groups.flatMap(group => group.operations)} + archive={archive} + label={label} + prompt + toggleOperations={toggleOperations} + clickThrough + /> + + + + {section.groups + .map(group => [t(`admin.permissions.group.${group.name}`), group] as const) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([name, group]) => ( + // div forces every checkbox on a separate line +
+ + + +
+ ))} +
+
+ ); + })}
@@ -357,7 +417,7 @@ const PermissionsView = ({ userId }: { userId: string }) => { addPreset, findPermission, addPermission, - deletePermission, + removePermission, refetchPermissions, ] ); @@ -420,7 +480,7 @@ const ParameterInputs = ({ group, findPermission, archive, - deletePermission, + removePermission, addPermission, refetchPermissions, }: { @@ -430,8 +490,8 @@ const ParameterInputs = ({ archive: FlatArchiveTag | null ) => FlatParameterizedPermission | null; archive: FlatArchiveTag | null; - deletePermission: (parameters: { variables: { id: string } }) => Promise; - addPermission: (operation: Operation, parameters: Parameters) => Promise; + removePermission: (parameters: { variables: { id: string } }) => Promise; + addPermission: (operation: Operation, parameters: PermissionParameters) => Promise; refetchPermissions: () => Promise; }) => { const { t } = useTranslation(); @@ -440,7 +500,7 @@ const ParameterInputs = ({ operations: group.operations, findPermission, addPermission, - deletePermission, + removePermission, refetchPermissions, archive, }; diff --git a/projects/bp-gallery/src/components/views/admin/user/ResetPasswordView.tsx b/projects/bp-gallery/src/components/views/admin/user/ResetPasswordView.tsx index 12a64237b..052adc69e 100644 --- a/projects/bp-gallery/src/components/views/admin/user/ResetPasswordView.tsx +++ b/projects/bp-gallery/src/components/views/admin/user/ResetPasswordView.tsx @@ -44,6 +44,7 @@ export const ResetPasswordView = () => { +

{t('admin.passwordRequirements')}

| undefined>; + parameter: KeysWithValueExtending | undefined>; operations: Operation[]; archive: FlatArchiveTag | null; findPermission: ( operation: Operation, archive: FlatArchiveTag | null ) => FlatParameterizedPermission | null; - deletePermission: (options: { variables: { id: string } }) => Promise; - addPermission: (operation: Operation, parameters: Parameters) => Promise; + removePermission: (options: { variables: { id: string } }) => Promise; + addPermission: (operation: Operation, parameters: PermissionParameters) => Promise; refetchPermissions: () => Promise; falseTitle: string; trueTitle: string; @@ -39,7 +39,7 @@ export const BooleanParameter = ({ operations.forEach(async operation => { const permission = findPermission(operation, archive); if (permission) { - await deletePermission({ + await removePermission({ variables: { id: permission.id, }, @@ -60,7 +60,7 @@ export const BooleanParameter = ({ archive, addPermission, parameter, - deletePermission, + removePermission, refetchPermissions, ] ); diff --git a/projects/bp-gallery/src/components/views/admin/user/permissions/presets.ts b/projects/bp-gallery/src/components/views/admin/user/permissions/presets.ts index d1e388138..086a56b6c 100644 --- a/projects/bp-gallery/src/components/views/admin/user/permissions/presets.ts +++ b/projects/bp-gallery/src/components/views/admin/user/permissions/presets.ts @@ -1,16 +1,16 @@ import { PermissionName } from 'bp-graphql/build'; -import { Parameters } from '../PermissionsView'; +import { PermissionParameters } from '../PermissionsView'; export type PresetType = 'system' | 'archive'; -export type ParametersWithoutArchive = Omit; +export type PermissionParametersWithoutArchive = Omit; export type Preset = { type: PresetType; name: string; permissions: readonly ( | PermissionName - | { name: PermissionName; parameters: ParametersWithoutArchive } + | { name: PermissionName; parameters: PermissionParametersWithoutArchive } )[]; }; diff --git a/projects/bp-gallery/src/hooks/sequences.hook.tsx b/projects/bp-gallery/src/hooks/sequences.hook.tsx index f5bc27ab3..6a9e981c9 100644 --- a/projects/bp-gallery/src/hooks/sequences.hook.tsx +++ b/projects/bp-gallery/src/hooks/sequences.hook.tsx @@ -4,7 +4,6 @@ import { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useDialog } from '../components/provider/DialogProvider'; import { - useCanRunUpdatePictureMutation, useCreatePictureSequenceMutation, useUpdatePictureSequenceDataMutation, } from '../graphql/APIConnector'; @@ -140,13 +139,6 @@ export const useRemovePictureFromSequence = () => { return removePictureFromSequence; }; -export const useCanRemovePictureFromSequence = (pictureId: string | undefined) => - useCanRunUpdatePictureMutation({ - variables: { - pictureId, - }, - }).canRun; - export const collapseSequences = (pictures: FlatPicture[] | undefined) => { if (!pictures) { return undefined; diff --git a/projects/bp-gallery/src/shared/locales/de.json b/projects/bp-gallery/src/shared/locales/de.json index f664f62c4..7044268ce 100755 --- a/projects/bp-gallery/src/shared/locales/de.json +++ b/projects/bp-gallery/src/shared/locales/de.json @@ -116,13 +116,13 @@ "The provided current password is invalid": "Das aktuelle Passwort ist falsch", "Your new password must be different than your current password": "Ihr neues Passwort muss anders als ihr aktuelles Passwort sein", "Passwords do not match": "Das neue Passwort und dessen Wiederholung stimmen nicht überein", - "Incorrect code provided": "Dieser Link ist ungültig", + "Incorrect code provided": "Dieser Link ist nicht mehr gültig, bitte erzeugen Sie einen neuen Link mithilfe der 'Passwort vergessen?' Funktion im Login-Dialog", "Invalid token": "Dieser Link ist ungültig", "Email or Username are already taken": "Die E-Mail oder der Nutzername ist bereits vergeben", "Username already taken": "Der Nutzername ist bereits vergeben", "Email already taken": "Die E-Mail ist bereits vergeben", "email must be a valid email": "Die E-Mail ist keine gültige E-Mail", - "password must be at least 12 characters long and must contain at least a small letter, a capital letter, a number and a special character": "Das Passwort muss mindestens 12 Zeichen lang sein und muss mindestens einen Kleinbuchstaben, einen Großbuchstaben, eine Ziffer und ein Sonderzeichen enthalten", + "password must be at least 12 characters long and must contain at least a small letter, a capital letter, a number and a special character": "$t(admin.passwordRequirementsNoPeriod)", "password must be at least 6 characters": "Das Passwort muss mindestens 6 Zeichen lang sein" }, "comment": { @@ -214,10 +214,15 @@ "publicTitle": "Rechte eines unangemeldeten Nutzers", "filterPlaceholder": "Archive filtern", "systemPermissions": "System-Rechte", + "systemPermissionsTooltip": "Die Rechte in dieser Kategorie gelten für die gesamte Plattform, also archivübergreifend.", "withoutArchive": "Bilder ohne Archivzuordnung", + "withoutArchiveTooltip": "Die Rechte in dieser Kategorie gelten für Bilder, die keinem Archiv zugeordnet sind. Dieser Fall tritt selten auf, die Rechte in dieser Kategorie werden also selten gebraucht.", + "archiveTooltip": "Die Rechte in dieser Kategorie gelten nur im Kontext des Archivs \"{{archiveName}}\".", "reallyGrantAllPermissions": "Wirklich alle Rechte aus \"{{header}}\" vergeben?", "reallyRemoveAllPermissions": "Wirklich alle Rechte aus \"{{header}}\" entziehen?", "reallyEditAllPermissionsContent": "Sie können dies nicht rückgängig machen, sondern müssen den vorigen Stand manuell wiederherstellen.", + "reallyEditPublicUserTitle": "Wirklich die Rechte eines UNANGEMELDETEN Nutzers bearbeiten?", + "reallyEditPublicUserContent": "Wenn Sie diese Rechte bearbeiten, wirkt sich das auf alle Nutzer aus, die NICHT angemeldet sind. Wollen Sie fortfahren?", "section": { "picture": "Bilder", "collection": "Collections", @@ -229,6 +234,17 @@ "games": "Spiele", "user": "Nutzer" }, + "section-tooltip": { + "picture": "Die Rechte in dieser Kategorie betreffen die Verwaltung von Bildern und Texten.", + "collection": "Die Rechte in dieser Kategorie betreffen die Verwaltung von Collections.", + "tags": "Die Rechte in dieser Kategorie betreffen die Verwaltung von Tags. Mit Tags sind Schlagworte, Personen und Orte gemeint.", + "locations": "Die Rechte in dieser Kategorie betreffen speziell die Verwaltung von Orten. Diese Rechte gehören nicht zur \"Tags\"-Kategorie, weil es keine äquivalenten Rechte für Schlagworte und Personen gibt.", + "comment": "Die Rechte in dieser Kategorie betreffen die Verwaltung von Kommentaren unter Bildern.", + "archive_system": "Die Rechte in dieser Kategorie betreffen die Verwaltung von den Archiven auf der Plattform.", + "archive_archive": "Die Rechte in dieser Kategorie betreffen die Verwaltung des Archivs \"{{archiveName}}\".", + "games": "Die Rechte in dieser Kategorie betreffen die Verwaltung der Spiele auf der Plattform.", + "user": "Die Rechte in dieser Kategorie betreffen die Verwaltung der Nutzer, die auf der Plattform angemeldet sind." + }, "group": { "createPicture": "Bild hochladen", "createPictureSequence": "Serie erstellen", @@ -257,8 +273,7 @@ "declineComment": "Kommentar ablehnen", "fixCommentText": "Kommentar editieren", "getUnverifiedComments": "Liste aller noch nicht verifizierten Kommentare ansehen", - "pinComment": "Kommentar anpinnen", - "postComment": "Kommentar erstellen", + "pinComment": "Kommentar fixieren", "unpinComment": "Kommentar loslösen", "addArchiveTag": "Archiv anlegen", "editArchive": "Archiv-Eigenschaften bearbeiten", @@ -274,6 +289,50 @@ "login": "Sich anmelden", "removeUser": "Nutzer löschen" }, + "group-tooltip": { + "createPicture": "Erlaubt dem Nutzer, Bilder hochzuladen und mit dem Bildbearbeitungstool zu bearbeiten. Das Recht wird auch benötigt, um ein neues Logo für das Archiv hochzuladen.", + "createPictureSequence": "Erlaubt dem Nutzer, mehrere (ähnliche) Bilder zu einer Serie zusammenzufassen. Das Recht wird auch benötigt, um Bilder wieder aus Serien zu entfernen und um die Reihenfolge von Bildern in Serien zu ändern.", + "editFaceTags": "Erlaubt dem Nutzer, Personenmarkierungen zu erstellen, zu bearbeiten und zu löschen.", + "editPicture": "Erlaubt dem Nutzer, die mit Bildern assoziierten Daten zu bearbeiten. Dazu gehören Zeit, Beschreibungen, Personen, Orte, Schlagworte, verlinkte Bilder/Texte, Collections und Archiv. Das Recht wird auch benötigt, um Bilder als Texte zu markieren und um das Bildbearbeitungstool zu nutzen.", + "unpublishPicture": "Erlaubt dem Nutzer, Bilder zu löschen.", + "createSubCollection": "Erlaubt dem Nutzer, neue Collections zu erstellen.", + "deleteCollection": "Erlaubt dem Nutzer, Collections zu löschen.", + "getAllCollections": "Erlaubt dem Nutzer, in verschiedenen Dialogen eine Liste aller Collections zu sehen. Das Rechte wird benötigt, um die Liste der Collections, zu denen ein Bild gehört, zu bearbeiten, Collections zu verschieben, Collections zu verlinken, Collections zusammenzuführen und um Bilder in Collections zu kopieren/verschieben.", + "getCollectionInfoByName": "Erlaubt dem Nutzer, unsichtbare Collections, wie die Collection für das GeoQuiz, zu sehen.", + "mergeCollections": "Erlaubt dem Nutzer, zwei Collections zu einer Collection zusammenzuführen.", + "setPicturesForCollection": "Erlaubt dem Nutzer, Bilder zu Collections hinzuzufügen und wieder aus diesen zu entfernen.", + "updateCollection": "Erlaubt dem Nutzer, den Namen und die Beschreibung einer Collection zu bearbeiten. Das Recht wird auch benötigt, um Collections zu verstecken und wieder sichtbar zu machen.", + "updateCollectionParents": "Erlaubt dem Nutzer, Collections innerhalb der Hierarchie zu verschieben, zu verlinken und Verlinkungen aufzuheben.", + "createTag": "Erlaubt dem Nutzer, neue Tags anzulegen.", + "deleteTag": "Erlaubt dem Nutzer, bestehende Tags zu löschen.", + "getAllTags": "Erlaubt dem Nutzer, in verschiedenen Dialogen eine Liste aller Tags zu sehen. Das Rechte wird benötigt, um bestehende Tags zu Bildern hinzuzufügen und um die Tag-Verwaltungsseiten zu nutzen.", + "mergeTags": "Erlaubt dem Nutzer, zwei Tags zu einem Tag zusammenzuführen.", + "updateTagName": "Erlaubt dem Nutzer, bestehende Tags umzubenennen.", + "updateTagSynonyms": "Erlaubt dem Nutzer, Synonyme zu bestehenden Tags hinzuzufügen und zu entfernen.", + "updateTagVisibility": "Erlaubt dem Nutzer, Tags zu verstecken und wieder sichtbar zu machen.", + "updateLocationAcceptance": "Erlaubt dem Nutzer, einen neu erstellten Ort zu akzeptieren, also in der Orteverwaltung abzuhaken.", + "updateLocationHierarchy": "Erlaubt dem Nutzer, die Ober- und Unterorte eines Ortes zu verwalten, Orte zu verschieben, zu kopieren, aus der Hierarchie zu lösen und Orte als Wurzeln zu markieren. Das Rechte wird auch benötigt, um einen Ort zu löschen, ohne seine Unterorte zu löschen.", + "updateLocationCoordinates": "Erlaubt dem Nutzer, die Koordinaten eines Ortes auf der Karte zu setzen und diese wieder zu löschen.", + "acceptComment": "Erlaubt dem Nutzer, einen Kommentar zu akzeptieren.", + "declineComment": "Erlaubt dem Nutzer, einen Kommentar abzulehnen.", + "fixCommentText": "Erlaubt dem Nutzer, bestehende Kommentare zu editieren.", + "getUnverifiedComments": "Erlaubt dem Nutzer, eine Übersicht über alle noch nicht verifizierten Kommentare auf der Plattform zu sehen.", + "pinComment": "Erlaubt dem Nutzer, einen Kommentar zu fixieren, also ihn anzupinnen.", + "unpinComment": "Erlaubt dem Nutzer, einen Kommentar loszulösen, also ein Fixieren/Anpinnen rückgängig zu machen.", + "addArchiveTag": "Erlaubt dem Nutzer, ein neues Archiv/eine neue Sammlung anzulegen.", + "editArchive": "Erlaubt dem Nutzer, den Namen, die Kurz- und Archiv-Beschreibung, die E-Mail-Adresse, die Links und die Einstellungen zu PayPal-Spenden zu bearbeiten. Das Recht wird auch benötigt, um das Herunterladen von Bildern aus dem Archiv einzuschränken.", + "exhibition": "Erlaubt dem Nutzer, Geschichten zu erstellen, zu bearbeiten, zu veröffentlichen, privat zu stellen und zu löschen.", + "removeArchiveTag": "Erlaubt dem Nutzer, dieses Archiv zu unwiderruflich löschen.", + "addUser": "Erlaubt dem Nutzer, neue Nutzer zu registrieren.", + "addPermission": "Erlaubt dem Nutzer, anderen Nutzern Rechte zu vergeben. Der Nutzer kann anderen Nutzern nur die Rechte vergeben, die er selbst hat.", + "getParameterizedPermissions": "Erlaubt dem Nutzer, die Rechte der angegebenen Nutzer anzusehen. Das Recht wird benötigt, um Rechte zu vergeben.", + "deleteParameterizedPermission": "Erlaubt dem Nutzer, anderen Nutzern Rechte zu entziehen. Der Nutzer kann anderen Nutzern nur die Rechte entziehen, die er selbst hat. Der Nutzer kann sich nicht selbst Rechte entziehen.", + "getUser": "Erlaubt dem Nutzer, die Nutzernamen und E-Mail-Adressen aller Nutzer zu sehen. Das Recht wird benötigt, um die Nutzerseiten aller Nutzer zu benutzen.", + "updateUser": "Erlaubt dem Nutzer, die Nutzernamen und E-Mail-Adressen aller Nutzer zu bearbeiten.", + "getUsers": "Erlaubt dem Nutzer, eine Übersicht über alle Nutzer, die auf der Plattform angemeldet sind. Das Recht wird benötigt, um die Nutzerverwaltung zu benutzen.", + "login": "Erlaubt dem Nutzer, sich anzumelden. Wenn Sie dieses Recht nicht vergeben, wird dem Nutzer jeder Loginversuch verwehrt.", + "removeUser": "Erlaubt dem Nutzer, die angegebenen Nutzer unwiderruflich zu löschen." + }, "parameter": { "on_other_users": { "removeUser": { @@ -297,7 +356,7 @@ "email": "E-Mail", "permissions": "Rechteverwaltung", "remove": { - "button": "Nutzer unwiederruflich löschen", + "button": "Nutzer unwiderruflich löschen", "title": "Nutzer {{name}} wirklich löschen?", "content": "Diese Aktion kann nicht rückgängig gemacht werden. Um zu bestätigen, dass Sie wirklich den Nutzer {{name}} löschen möchten, geben Sie hier den Namen des Nutzers ein:", "success": "Nutzer erfolgreich gelöscht", @@ -305,7 +364,7 @@ } }, "users": { - "title": "Nutzerverwaltung", + "title": "Nutzer- und Rechteverwaltung", "add": "Nutzer erstellen", "add-content": "An die angegebene E-Mail wird ein Link zum Passwortsetzen verschickt.", "publicUsername": "Unangemeldeter Nutzer" @@ -315,7 +374,7 @@ "add": "Archiv erstellen", "add-content": "Name des neuen Archivs:", "remove": { - "button": "Archiv unwiederruflich löschen", + "button": "Archiv unwiderruflich löschen", "title": "Archiv {{name}} wirklich löschen?", "content": "Diese Aktion kann nicht rückgängig gemacht werden. Um zu bestätigen, dass Sie wirklich das Archiv {{name}} löschen möchten, geben Sie hier den Namen des Archivs ein:", "success": "Archiv erfolgreich gelöscht", @@ -332,6 +391,8 @@ "email": "E-Mail", "success": "E-Mail versendet" }, + "passwordRequirementsNoPeriod": "Das neue Passwort muss mindestens 12 Zeichen lang sein und muss mindestens einen Kleinbuchstaben, einen Großbuchstaben, eine Ziffer und ein Sonderzeichen enthalten", + "passwordRequirements": "$t(admin.passwordRequirementsNoPeriod).", "resetPassword": { "title": "Passwort setzen/zurücksetzen", "password": "Neues Passwort",