enhancement(permissions): show alert when permission is overridden by another

Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson 2023-08-13 23:56:41 +02:00
parent 626a6c7426
commit dfcf662a85
5 changed files with 64 additions and 18 deletions

View file

@ -423,7 +423,8 @@
"error": "There was an error changing the owner of this note.", "error": "There was an error changing the owner of this note.",
"placeholder": "Enter username of new note owner", "placeholder": "Enter username of new note owner",
"button": "Change the owner of this note" "button": "Change the owner of this note"
} },
"inconsistent": "This permission is overridden by another permission"
}, },
"shareLink": { "shareLink": {
"title": "Share link", "title": "Share link",

View file

@ -15,10 +15,12 @@ import React, { useCallback, useMemo } from 'react'
import { ToggleButtonGroup } from 'react-bootstrap' import { ToggleButtonGroup } from 'react-bootstrap'
import { Eye as IconEye, Pencil as IconPencil, SlashCircle as IconSlashCircle } from 'react-bootstrap-icons' import { Eye as IconEye, Pencil as IconPencil, SlashCircle as IconSlashCircle } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { PermissionInconsistentAlert } from './permission-inconsistent-alert'
export interface PermissionEntrySpecialGroupProps { export interface PermissionEntrySpecialGroupProps {
level: AccessLevel level: AccessLevel
type: SpecialGroup type: SpecialGroup
inconsistent?: boolean
} }
/** /**
@ -27,11 +29,13 @@ export interface PermissionEntrySpecialGroupProps {
* @param level The access level that is currently set for the group. * @param level The access level that is currently set for the group.
* @param type The type of the special group. Must be either {@link SpecialGroup.EVERYONE} or {@link SpecialGroup.LOGGED_IN}. * @param type The type of the special group. Must be either {@link SpecialGroup.EVERYONE} or {@link SpecialGroup.LOGGED_IN}.
* @param disabled If the user is not the owner, functionality is disabled. * @param disabled If the user is not the owner, functionality is disabled.
* @param inconsistent Whether to show the inconsistency alert icon or not
*/ */
export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupProps & PermissionDisabledProps> = ({ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupProps & PermissionDisabledProps> = ({
level, level,
type, type,
disabled disabled,
inconsistent
}) => { }) => {
const noteId = useApplicationState((state) => state.noteDetails?.primaryAddress) const noteId = useApplicationState((state) => state.noteDetails?.primaryAddress)
const { t } = useTranslation() const { t } = useTranslation()
@ -88,6 +92,7 @@ export const PermissionEntrySpecialGroup: React.FC<PermissionEntrySpecialGroupPr
<li className={'list-group-item d-flex flex-row justify-content-between align-items-center'}> <li className={'list-group-item d-flex flex-row justify-content-between align-items-center'}>
<span>{name}</span> <span>{name}</span>
<div> <div>
<PermissionInconsistentAlert show={inconsistent ?? false} />
<ToggleButtonGroup type='radio' name='edit-mode'> <ToggleButtonGroup type='radio' name='edit-mode'>
<IconButton <IconButton
icon={IconSlashCircle} icon={IconSlashCircle}

View file

@ -13,9 +13,10 @@ import { useUiNotifications } from '../../../../../notifications/ui-notification
import type { PermissionDisabledProps } from './permission-disabled.prop' import type { PermissionDisabledProps } from './permission-disabled.prop'
import { PermissionEntryButtons, PermissionType } from './permission-entry-buttons' import { PermissionEntryButtons, PermissionType } from './permission-entry-buttons'
import type { NoteUserPermissionEntry } from '@hedgedoc/commons' import type { NoteUserPermissionEntry } from '@hedgedoc/commons'
import { AccessLevel } from '@hedgedoc/commons' import { AccessLevel, SpecialGroup } from '@hedgedoc/commons'
import React, { useCallback } from 'react' import React, { useCallback, useMemo } from 'react'
import { useAsync } from 'react-use' import { useAsync } from 'react-use'
import { PermissionInconsistentAlert } from './permission-inconsistent-alert'
export interface PermissionEntryUserProps { export interface PermissionEntryUserProps {
entry: NoteUserPermissionEntry entry: NoteUserPermissionEntry
@ -33,6 +34,16 @@ export const PermissionEntryUser: React.FC<PermissionEntryUserProps & Permission
}) => { }) => {
const noteId = useApplicationState((state) => state.noteDetails?.primaryAddress) const noteId = useApplicationState((state) => state.noteDetails?.primaryAddress)
const { showErrorNotification } = useUiNotifications() const { showErrorNotification } = useUiNotifications()
const groupPermissions = useApplicationState((state) => state.noteDetails.permissions.sharedToGroups)
const permissionInconsistent = useMemo(() => {
const everyonePermission = groupPermissions.find((group) => group.groupName === (SpecialGroup.EVERYONE as string))
const loggedInPermission = groupPermissions.find((group) => group.groupName === (SpecialGroup.LOGGED_IN as string))
return (
(everyonePermission && everyonePermission.canEdit && !entry.canEdit) ||
(loggedInPermission && loggedInPermission.canEdit && !entry.canEdit)
)
}, [groupPermissions, entry])
const onRemoveEntry = useCallback(() => { const onRemoveEntry = useCallback(() => {
if (!noteId) { if (!noteId) {
@ -79,6 +90,8 @@ export const PermissionEntryUser: React.FC<PermissionEntryUserProps & Permission
<ShowIf condition={!loading && !error}> <ShowIf condition={!loading && !error}>
<li className={'list-group-item d-flex flex-row justify-content-between align-items-center'}> <li className={'list-group-item d-flex flex-row justify-content-between align-items-center'}>
<UserAvatarForUser user={value} /> <UserAvatarForUser user={value} />
<div className={'d-flex flex-row align-items-center'}>
<PermissionInconsistentAlert show={permissionInconsistent ?? false} />
<PermissionEntryButtons <PermissionEntryButtons
type={PermissionType.USER} type={PermissionType.USER}
currentSetting={entry.canEdit ? AccessLevel.WRITEABLE : AccessLevel.READ_ONLY} currentSetting={entry.canEdit ? AccessLevel.WRITEABLE : AccessLevel.READ_ONLY}
@ -88,6 +101,7 @@ export const PermissionEntryUser: React.FC<PermissionEntryUserProps & Permission
onRemove={onRemoveEntry} onRemove={onRemoveEntry}
disabled={disabled} disabled={disabled}
/> />
</div>
</li> </li>
</ShowIf> </ShowIf>
) )

View file

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react'
import { UiIcon } from '../../../../../common/icons/ui-icon'
import { ExclamationTriangleFill as IconExclamationTriangleFill } from 'react-bootstrap-icons'
import type { SimpleAlertProps } from '../../../../../common/simple-alert/simple-alert-props'
import { useTranslatedText } from '../../../../../../hooks/common/use-translated-text'
/**
* Alert that is shown when the permissions are inconsistent.
*
* @param show true to show the alert, false otherwise.
*/
export const PermissionInconsistentAlert: React.FC<SimpleAlertProps> = ({ show }) => {
const message = useTranslatedText('editor.modal.permissions.inconsistent')
if (!show) {
return null
}
return <UiIcon icon={IconExclamationTriangleFill} className={'text-warning me-2'} title={message} />
}

View file

@ -29,16 +29,17 @@ export const PermissionSectionSpecialGroups: React.FC<PermissionDisabledProps> =
const groupLoggedIn = groupPermissions.find((entry) => entry.groupName === (SpecialGroup.LOGGED_IN as string)) const groupLoggedIn = groupPermissions.find((entry) => entry.groupName === (SpecialGroup.LOGGED_IN as string))
return { return {
everyone: groupEveryone everyoneLevel: groupEveryone
? groupEveryone.canEdit ? groupEveryone.canEdit
? AccessLevel.WRITEABLE ? AccessLevel.WRITEABLE
: AccessLevel.READ_ONLY : AccessLevel.READ_ONLY
: AccessLevel.NONE, : AccessLevel.NONE,
loggedIn: groupLoggedIn loggedInLevel: groupLoggedIn
? groupLoggedIn.canEdit ? groupLoggedIn.canEdit
? AccessLevel.WRITEABLE ? AccessLevel.WRITEABLE
: AccessLevel.READ_ONLY : AccessLevel.READ_ONLY
: AccessLevel.NONE : AccessLevel.NONE,
loggedInInconsistentAlert: groupEveryone && (!groupLoggedIn || (groupEveryone.canEdit && !groupLoggedIn.canEdit))
} }
}, [groupPermissions]) }, [groupPermissions])
@ -53,12 +54,13 @@ export const PermissionSectionSpecialGroups: React.FC<PermissionDisabledProps> =
</h5> </h5>
<ul className={'list-group'}> <ul className={'list-group'}>
<PermissionEntrySpecialGroup <PermissionEntrySpecialGroup
level={specialGroupEntries.loggedIn} level={specialGroupEntries.loggedInLevel}
type={SpecialGroup.LOGGED_IN} type={SpecialGroup.LOGGED_IN}
disabled={!isOwner} disabled={!isOwner}
inconsistent={specialGroupEntries.loggedInInconsistentAlert}
/> />
<PermissionEntrySpecialGroup <PermissionEntrySpecialGroup
level={specialGroupEntries.everyone} level={specialGroupEntries.everyoneLevel}
type={SpecialGroup.EVERYONE} type={SpecialGroup.EVERYONE}
disabled={disabled} disabled={disabled}
/> />