diff --git a/apps/sensenet/src/application-paths.ts b/apps/sensenet/src/application-paths.ts index caae61c9e..0d640e2ba 100644 --- a/apps/sensenet/src/application-paths.ts +++ b/apps/sensenet/src/application-paths.ts @@ -13,11 +13,11 @@ export const PATHS = { content: { appPath: '/content/:browseType/:action?', snPath: '/Root/Content' }, contentTemplates: { appPath: '/content-templates/:browseType/:action?', snPath: '/Root/ContentTemplates' }, custom: { appPath: '/custom/:browseType/:path/:action?' }, - configuration: { appPath: '/settings/configuration/:action?', snPath: '/Root/System/Settings' }, - localization: { appPath: '/settings/localization/:action?', snPath: '/Root/Localization' }, - webhooks: { appPath: '/settings/webhooks/:action?', snPath: '/Root/System/WebHooks' }, - settings: { appPath: '/settings/:submenu?' }, - apiKeys: { appPath: '/settings/apikeys' }, + configuration: { appPath: '/system/settings/:action?', snPath: '/Root/System/Settings' }, + localization: { appPath: '/system/localization/:action?', snPath: '/Root/Localization' }, + webhooks: { appPath: '/system/webhooks/:action?', snPath: '/Root/System/WebHooks' }, + settings: { appPath: '/system/:submenu?' }, + apiKeys: { appPath: '/system/apikeys' }, } as const type SettingsItemType = 'stats' | 'apikeys' | 'webhooks' | 'adminui' @@ -30,28 +30,28 @@ type RoutesWithContentBrowser = keyof Pick< type RoutesWithActionParam = keyof Pick type Options = - | { path: (typeof PATHS)['events']['appPath']; params?: { eventGuid: string; [index: string]: string } } + | { path: (typeof PATHS)['events']['appPath']; params?: { eventGuid: string;[index: string]: string } } | { - path: (typeof PATHS)[RoutesWithContentBrowser]['appPath'] - params: { browseType: (typeof BrowseType)[number]; action?: string; [index: string]: string | undefined } - } + path: (typeof PATHS)[RoutesWithContentBrowser]['appPath'] + params: { browseType: (typeof BrowseType)[number]; action?: string;[index: string]: string | undefined } + } | { - path: (typeof PATHS)['custom']['appPath'] - params: { - browseType: (typeof BrowseType)[number] - path: string - action?: string - [index: string]: string | undefined - } + path: (typeof PATHS)['custom']['appPath'] + params: { + browseType: (typeof BrowseType)[number] + path: string + action?: string + [index: string]: string | undefined } + } | { - path: (typeof PATHS)[RoutesWithActionParam]['appPath'] - params?: { action: string; [index: string]: string } - } + path: (typeof PATHS)[RoutesWithActionParam]['appPath'] + params?: { action: string;[index: string]: string } + } | { - path: (typeof PATHS)['settings']['appPath'] - params?: { submenu: SettingsItemType; [index: string]: string | SettingsItemType } - } + path: (typeof PATHS)['settings']['appPath'] + params?: { submenu: SettingsItemType;[index: string]: string | SettingsItemType } + } export const resolvePathParams = ({ path, params }: Options) => { let currentPath: string = path diff --git a/apps/sensenet/src/components/appbar/desktop-nav-menu.tsx b/apps/sensenet/src/components/appbar/desktop-nav-menu.tsx index 969232779..7e3831348 100644 --- a/apps/sensenet/src/components/appbar/desktop-nav-menu.tsx +++ b/apps/sensenet/src/components/appbar/desktop-nav-menu.tsx @@ -1,6 +1,7 @@ import { Grid, IconButton, ListItemIcon, ListItemText, MenuItem, MenuList, Paper, Typography } from '@material-ui/core' import ClickAwayListener from '@material-ui/core/ClickAwayListener' import { createStyles, makeStyles, Theme, useTheme } from '@material-ui/core/styles' +import { TuneOutlined } from '@material-ui/icons' import KeyboardArrowDown from '@material-ui/icons/KeyboardArrowDown' import { Switch } from '@sensenet/controls-react' import { useInjector, useRepository } from '@sensenet/hooks-react' @@ -17,6 +18,10 @@ import { UserAvatar } from '../UserAvatar' const useStyles = makeStyles((theme: Theme) => createStyles({ + viewOptions: { + cursor: 'pointer', + marginRight: '16px', + }, navMenu: { height: '100%', width: '140px', @@ -38,6 +43,13 @@ const useStyles = makeStyles((theme: Theme) => height: 'fit-content', width: '216px', }, + popperViewWrapper: { + position: 'absolute', + top: globals.common.headerHeight, + right: '1px', + height: 'fit-content', + width: '256px', + }, popper: { backgroundColor: theme.palette.type === 'light' ? globals.light.navMenuColor : globals.dark.navMenuColor, border: theme.palette.type === 'light' ? clsx(globals.light.borderColor, '1px') : 'none', @@ -52,7 +64,7 @@ const useStyles = makeStyles((theme: Theme) => color: theme.palette.primary.main, fontSize: '14px', }, - themeSwitcher: { + checkboxMenuItem: { color: theme.palette.primary.main, fontSize: '14px', '& .MuiButtonBase-root': { @@ -75,6 +87,7 @@ export const DesktopNavMenu: FunctionComponent = () => { const localization = useLocalization() const { openDialog } = useDialog() const [openUserMenu, setOpenUserMenu] = useState(false) + const [openViewOptions, setOpenViewOptions] = useState(false) const handleToggle = (setter: Dispatch>) => { setter((prevState) => !prevState) @@ -99,9 +112,21 @@ export const DesktopNavMenu: FunctionComponent = () => { service.setPersonalSettingsValue({ ...settings, theme: event.target.checked ? 'dark' : 'light' }) } + const toggleHideSettingsFolder = () => (event: ChangeEvent) => { + const settings = service.userValue.getValue() + service.setPersonalSettingsValue({ ...settings, showHiddenItems: event.target.checked }) + } + return (
<> + handleToggle(setOpenViewOptions)}> + + { {localization.topMenu.logout} + + +
+ + ) : null} + {openViewOptions ? ( + +
+ handleClose(setOpenViewOptions)}> + + handleClose(setOpenViewOptions)}> + + {localization.topMenu.viewOptions} + + - - - + + + {personalSettings.theme === 'dark' ? 'Light theme' : 'Dark theme'} @@ -201,6 +241,22 @@ export const DesktopNavMenu: FunctionComponent = () => { + + + + + {localization.topMenu.showHiddenItems} + + + + + + +
diff --git a/apps/sensenet/src/components/content-list/content-list.tsx b/apps/sensenet/src/components/content-list/content-list.tsx index 8e9a9c353..bbcab9528 100644 --- a/apps/sensenet/src/components/content-list/content-list.tsx +++ b/apps/sensenet/src/components/content-list/content-list.tsx @@ -27,12 +27,13 @@ import React, { import { TableCellProps } from 'react-virtualized' import { ResponsiveContext, ResponsivePersonalSettings } from '../../context' import { globals, useGlobalStyles } from '../../globalStyles' -import { useLocalization, useSelectionService } from '../../hooks' +import { useLocalization, usePersonalSettings, useSelectionService } from '../../hooks' import { ContentBreadcrumbs } from '../ContentBreadcrumbs' import { ContentContextMenu } from '../context-menu/content-context-menu' import { useDialog } from '../dialogs' import { DropFileArea } from '../DropFileArea' import { SelectionControl } from '../SelectionControl' +import { SETTINGS_FOLDER_FILTER } from '../tree/tree-with-data' import { ContextMenuWrapper } from './context-menu-wrapper' import { ActionsField, @@ -124,6 +125,7 @@ export const ContentList = (props: Co const ancestors = useContext(CurrentAncestorsContext) as T[] const device = useContext(ResponsiveContext) const personalSettings = useContext(ResponsivePersonalSettings) + const userPersonalSettings = usePersonalSettings() const loadSettings = useContext(LoadSettingsContext) const repo = useRepository() const classes = useStyles() @@ -266,9 +268,18 @@ export const ContentList = (props: Co }, []), ], orderby: [[currentOrder as any, currentDirection as any]], + filter: !userPersonalSettings.showHiddenItems ? `(${SETTINGS_FOLDER_FILTER})` : '', }) // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentDirection, currentOrder, personalSettings.content.fields, props.fieldsToDisplay, repo, columnSettings]) + }, [ + currentDirection, + currentOrder, + personalSettings.content.fields, + userPersonalSettings.showHiddenItems, + props.fieldsToDisplay, + repo, + columnSettings, + ]) useEffect(() => { setSelected([]) diff --git a/apps/sensenet/src/components/settings/index.tsx b/apps/sensenet/src/components/settings/index.tsx index 22d42ea8f..ca0cb12b9 100644 --- a/apps/sensenet/src/components/settings/index.tsx +++ b/apps/sensenet/src/components/settings/index.tsx @@ -53,8 +53,8 @@ export const Settings: React.FunctionComponent = () => { const settingsItems = [ { - name: 'configuration', - displayName: localizationDrawer.titles.Configuration, + name: 'settings', + displayName: localizationDrawer.titles.Settings, url: resolvePathParams({ path: PATHS.configuration.appPath }), }, { @@ -83,7 +83,7 @@ export const Settings: React.FunctionComponent = () => { switch (routeMatch.params.submenu) { case 'localization': return - case 'configuration': + case 'settings': return case 'adminui': return @@ -117,7 +117,7 @@ export const Settings: React.FunctionComponent = () => { return (
- {localizationDrawer.titles.Settings} + {localizationDrawer.titles.System}
diff --git a/apps/sensenet/src/components/settings/setup.tsx b/apps/sensenet/src/components/settings/setup.tsx index c3a88df3b..495e78e93 100644 --- a/apps/sensenet/src/components/settings/setup.tsx +++ b/apps/sensenet/src/components/settings/setup.tsx @@ -128,7 +128,7 @@ const Setup = () => { return (
- {localizationDrawerTitles.Configuration} + {localizationDrawerTitles.Settings}
{renderContent()}
diff --git a/apps/sensenet/src/components/tree/tree-with-data.tsx b/apps/sensenet/src/components/tree/tree-with-data.tsx index e5f7ef1f4..26c8d6f96 100644 --- a/apps/sensenet/src/components/tree/tree-with-data.tsx +++ b/apps/sensenet/src/components/tree/tree-with-data.tsx @@ -4,7 +4,7 @@ import { GenericContent } from '@sensenet/default-content-types' import { useLogger, useRepository, useRepositoryEvents } from '@sensenet/hooks-react' import React, { useCallback, useEffect, useState } from 'react' import Semaphore from 'semaphore-async-await' -import { usePreviousValue, useSelectionService } from '../../hooks' +import { usePersonalSettings, usePreviousValue, useSelectionService } from '../../hooks' import { ItemType, Tree } from './tree' type TreeWithDataProps = { @@ -18,6 +18,7 @@ type TreeWithDataProps = { let lastRequest: { path: string; lastIndex: number } | undefined const ITEM_THRESHOLD = 50 +export const SETTINGS_FOLDER_FILTER = `not ((Name eq 'Settings') and (isOf('SystemFolder')))` const walkTree = (node: ItemType, callBack: (node: ItemType) => void) => { if (node?.children?.length) { @@ -31,6 +32,7 @@ const lock = new Semaphore(1) export default function TreeWithData(props: TreeWithDataProps) { const repo = useRepository() + const personalSettings = usePersonalSettings() const [itemCount, setItemCount] = useState(0) const [treeData, setTreeData] = useState() const [isLoading, setIsLoading] = useState(false) @@ -39,6 +41,7 @@ export default function TreeWithData(props: TreeWithDataProps) { const logger = useLogger('tree-with-data') const prevActiveItemPath = usePreviousValue(props.activeItemPath) + const prevShowHiddenItems = usePreviousValue(personalSettings.showHiddenItems) const { onTreeLoadingChange } = props const loadCollection = useCallback( @@ -55,7 +58,7 @@ export default function TreeWithData(props: TreeWithDataProps) { oDataOptions: { top, skip, - filter: 'IsFolder eq true', + filter: `IsFolder eq true ${!personalSettings.showHiddenItems ? `and (${SETTINGS_FOLDER_FILTER})` : ''}`, orderby: [ ['DisplayName', 'asc'], ['Name', 'asc'], @@ -72,7 +75,7 @@ export default function TreeWithData(props: TreeWithDataProps) { setIsLoading(false) } }, - [logger, repo, onTreeLoadingChange], + [onTreeLoadingChange, repo, personalSettings.showHiddenItems, logger], ) useEffect(() => { @@ -97,7 +100,6 @@ export default function TreeWithData(props: TreeWithDataProps) { const openTree = useCallback( async (forced = false) => { if (!forced && treeData && treeData.Path === props.parentPath) return - const buildTree = (items: GenericContent[], id?: number): any => { if (!id) { return { ...items[0], children: buildTree(items, items[0].Id), hasNextPage: false, expanded: true } @@ -118,7 +120,10 @@ export default function TreeWithData(props: TreeWithDataProps) { idOrPath: props.activeItemPath, name: 'OpenTree', method: 'GET', - oDataOptions: props.loadSettings, + oDataOptions: { + ...props.loadSettings, + filter: !personalSettings.showHiddenItems ? SETTINGS_FOLDER_FILTER : '', + }, body: { rootPath: props.parentPath, withSystem: true, @@ -132,7 +137,7 @@ export default function TreeWithData(props: TreeWithDataProps) { setTreeData(undefined) } }, - [props.activeItemPath, props.parentPath, repo, treeData, props.loadSettings], + [treeData, props.parentPath, props.activeItemPath, props.loadSettings, repo, personalSettings.showHiddenItems], ) const loadMoreItems = useCallback( @@ -279,8 +284,8 @@ export default function TreeWithData(props: TreeWithDataProps) { ]) useEffect(() => { - openTree() - }, [openTree]) + openTree(personalSettings.showHiddenItems !== prevShowHiddenItems) + }, [openTree, personalSettings.showHiddenItems, prevShowHiddenItems]) const onItemClick = async (item: ItemType) => { if (!treeData) { diff --git a/apps/sensenet/src/components/view-controls/common/view-title.tsx b/apps/sensenet/src/components/view-controls/common/view-title.tsx index 84cf4f87b..69cc1b183 100644 --- a/apps/sensenet/src/components/view-controls/common/view-title.tsx +++ b/apps/sensenet/src/components/view-controls/common/view-title.tsx @@ -37,7 +37,9 @@ const useStyles = makeStyles(() => { color: 'grey', marginLeft: '5px', }, - + actionText: { + marginRight: '5px', + }, viewTitle: { '& span': { display: 'inline-block', @@ -76,7 +78,8 @@ export const ViewTitle: React.FunctionComponent = (props) => { return (
- {props.title} {props.titleBold} + {props.title} + {props.titleBold} ({props.content!.Type})
diff --git a/apps/sensenet/src/components/view-controls/permission-view.tsx b/apps/sensenet/src/components/view-controls/permission-view.tsx index c04fac43f..a451abb8f 100644 --- a/apps/sensenet/src/components/view-controls/permission-view.tsx +++ b/apps/sensenet/src/components/view-controls/permission-view.tsx @@ -292,7 +292,7 @@ export const PermissionView: React.FC = (props) => { - {inheritedEntry.identity.displayName} + {inheritedEntry.identity.displayName} ({inheritedEntry.identity.path}) = (props) => { - {setOnThisEntry.identity.displayName} + {setOnThisEntry.identity.displayName} ({setOnThisEntry.identity.path}) {!setOnThisEntry.propagates && ( diff --git a/apps/sensenet/src/hooks/use-drawer-items.tsx b/apps/sensenet/src/hooks/use-drawer-items.tsx index 558ca0978..b53e80ab9 100644 --- a/apps/sensenet/src/hooks/use-drawer-items.tsx +++ b/apps/sensenet/src/hooks/use-drawer-items.tsx @@ -89,7 +89,7 @@ export const useDrawerItems = () => { systemItem: true, }, { - itemType: 'Settings', + itemType: 'System', systemItem: true, }, ], @@ -131,7 +131,7 @@ export const useDrawerItems = () => { return case 'CustomContent': return item.settings?.icon ? : - case 'Settings': + case 'System': return // no default } @@ -174,7 +174,7 @@ export const useDrawerItems = () => { path: (item as CustomContentDrawerItem).settings?.appPath || '', }, }) - case 'Settings': + case 'System': return resolvePathParams({ path: PATHS.settings.appPath, params: { submenu: 'stats' } }) default: return '/' @@ -194,32 +194,32 @@ export const useDrawerItems = () => { return drawerItem } - ;[...settings.drawer.items, ...builtInDrawerItems] - .filterAsync(async (item) => { - if (!item.permissions?.length) { - return true - } + ;[...settings.drawer.items, ...builtInDrawerItems] + .filterAsync(async (item) => { + if (!item.permissions?.length) { + return true + } - try { - for (const permission of item.permissions) { - const actions = await repo.getActions({ idOrPath: permission.path }) - const actionIndex = actions.d.results.findIndex((action) => action.Name === permission.action) - if (actionIndex === -1 || actions.d.results[actionIndex].Forbidden) { - return false + try { + for (const permission of item.permissions) { + const actions = await repo.getActions({ idOrPath: permission.path }) + const actionIndex = actions.d.results.findIndex((action) => action.Name === permission.action) + if (actionIndex === -1 || actions.d.results[actionIndex].Forbidden) { + return false + } } + } catch (error) { + logger.debug({ + message: error.message, + data: { + error, + }, + }) + return false } - } catch (error) { - logger.debug({ - message: error.message, - data: { - error, - }, - }) - return false - } - return true - }) - .then((items) => setDrawerItems(items.map(getItemFromSettings))) + return true + }) + .then((items) => setDrawerItems(items.map(getItemFromSettings))) }, [ localization.descriptions, localization.titles, diff --git a/apps/sensenet/src/localization/default.ts b/apps/sensenet/src/localization/default.ts index e169886d2..5495f8021 100644 --- a/apps/sensenet/src/localization/default.ts +++ b/apps/sensenet/src/localization/default.ts @@ -128,6 +128,7 @@ const values = { Settings: 'Settings', Configuration: 'Configuration', Stats: 'Stats', + System: 'System', ApiAndSecurity: 'Api and Security', Webhooks: 'Webhooks', AdminUiCustomization: 'Admin-ui customization', @@ -143,6 +144,7 @@ const values = { UsersAndGroups: 'Manage users and groups, roles and identities', CustomContent: 'Explore and manage your content from the configured path', Settings: 'Configure the sensenet system', + System: 'Configure the sensenet system', ContentTemplates: 'Manage content templates', }, personalSettingsTitle: 'Edit personal settings', @@ -308,6 +310,9 @@ const values = { openUserMenu: 'Open user menu', openNewMenu: `What's new`, accountSettings: 'Account settings', + openViewOptions: 'Open view options', + showHiddenItems: 'Show hidden items', + viewOptions: 'View options', }, navigationCommandProvider: { personalSettingsPrimary: 'Personal Settings', diff --git a/apps/sensenet/src/localization/hungarian.ts b/apps/sensenet/src/localization/hungarian.ts index ee088f125..5d8424041 100644 --- a/apps/sensenet/src/localization/hungarian.ts +++ b/apps/sensenet/src/localization/hungarian.ts @@ -148,6 +148,9 @@ const values: Localization = { openUserMenu: 'Felhasználói menü kinyitása', openNewMenu: 'Újdonságok', accountSettings: 'Felhasználói beállítások', + openViewOptions: 'Felületi beállítások kinyitása', + showHiddenItems: 'Rejtett elemek megj.', + viewOptions: 'Felület beállítások', }, settings: { edit: 'Módosítás', diff --git a/apps/sensenet/src/services/PersonalSettings.ts b/apps/sensenet/src/services/PersonalSettings.ts index 315ffe892..eeb5712d3 100644 --- a/apps/sensenet/src/services/PersonalSettings.ts +++ b/apps/sensenet/src/services/PersonalSettings.ts @@ -29,7 +29,7 @@ export const BuiltInDrawerItemType = tuple( 'SavedQueries', 'Trash', 'UsersAndGroups', - 'Settings', + 'System', 'ContentTemplates', ) @@ -88,6 +88,7 @@ export interface CustomContentDrawerItem export type PersonalSettingsType = PlatformDependent & { eventLogSize: number sendLogWithCrashReports: boolean + showHiddenItems: boolean logLevel: Array language: 'default' | 'hungarian' theme: 'light' | 'dark' @@ -138,6 +139,7 @@ export const defaultSettings: PersonalSettingsType = { sendLogWithCrashReports: true, logLevel: ['Information', 'Warning', 'Error', 'Fatal'], theme: prefersDark ? 'dark' : 'light', + showHiddenItems: true, uploadHandlers: [ 'SenseNet.ContentRepository.File', 'SenseNet.ContentRepository.Image', @@ -149,19 +151,19 @@ export const defaultSettings: PersonalSettingsType = { export class PersonalSettings { private checkDrawerItems(settings: Partial): Partial { if (settings.default?.drawer?.items?.find((i) => typeof i === 'string')) { - ;(settings.default.drawer.items as any) = undefined + ; (settings.default.drawer.items as any) = undefined } if (settings.desktop?.drawer?.items?.find((i) => typeof i === 'string')) { - ;(settings.desktop.drawer.items as any) = undefined + ; (settings.desktop.drawer.items as any) = undefined } if (settings.tablet?.drawer?.items?.find((i) => typeof i === 'string')) { - ;(settings.tablet.drawer.items as any) = undefined + ; (settings.tablet.drawer.items as any) = undefined } if (settings.mobile?.drawer?.items?.find((i) => typeof i === 'string')) { - ;(settings.mobile.drawer.items as any) = undefined + ; (settings.mobile.drawer.items as any) = undefined } return settings diff --git a/packages/sn-hooks-react/src/hooks/use-download.ts b/packages/sn-hooks-react/src/hooks/use-download.ts index a5a05019d..7bd80b564 100644 --- a/packages/sn-hooks-react/src/hooks/use-download.ts +++ b/packages/sn-hooks-react/src/hooks/use-download.ts @@ -10,6 +10,7 @@ export const fakeClick = (obj: EventTarget) => { export const downloadFile = (name: string, repositoryUrl: string) => { const saveLink = document.createElement('a') saveLink.href = `${repositoryUrl}${name}?download` + saveLink.target = '_blank'; fakeClick(saveLink) }