feat: Add ability to configure guest permissions (#10708)

* Configure guest permissions

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix permission domain

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix permission declaration

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix lock file

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Guest permissions

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Allow guests to update their own documents

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Add modules order

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix translations, icons

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Fix disabled apps and update translations

Signed-off-by: Artem Savchenko <armisav@gmail.com>

---------

Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
Artyom Savchenko
2026-04-05 14:36:59 +07:00
committed by GitHub
parent 8b5b57563e
commit ea254970c0
81 changed files with 1390 additions and 33 deletions
@@ -14,7 +14,7 @@
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import core, { AccountRole, getCurrentAccount, type Ref } from '@hcengineering/core'
import core, { AccountRole, getCurrentAccount, type ModulePermissionGroup, type Ref } from '@hcengineering/core'
import { createNotificationsQuery, createQuery } from '@hcengineering/presentation'
import { Scroller, deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
import { NavLink } from '@hcengineering/view-resources'
@@ -36,7 +36,7 @@
const dispatch = createEventDispatcher()
function getClickHandler (app: Application, customProps: any) {
function getClickHandler (app: Application, customProps: any): () => void {
return (
customProps.onClick ??
(() => {
@@ -46,10 +46,34 @@
}
let loaded: boolean = false
let permissionsLoaded: boolean = false
let hiddenAppsIds: Array<Ref<Application>> = []
let excludedApps: string[] = []
let disabledApplications: Set<Ref<Application>> = new Set<Ref<Application>>()
const hiddenAppsIdsQuery = createQuery()
const modulePermissionGroupsQuery = createQuery()
modulePermissionGroupsQuery.query(core.class.ModulePermissionGroup, {}, (res) => {
try {
const modulePermissionGroups = res as ModulePermissionGroup[]
disabledApplications = new Set<Ref<Application>>(
modulePermissionGroups
.filter((g) => {
if (g.enabled ?? true) return false
const role = getCurrentAccount().role
if (role === g.role) return true
// DocGuest / ReadOnlyGuest should also respect Guest module disables.
return (role === AccountRole.DocGuest || role === AccountRole.ReadOnlyGuest) && g.role === AccountRole.Guest
})
.map((g) => g.application as Ref<Application>)
)
} catch (error) {
console.error('Error loading module permission groups:', error)
} finally {
permissionsLoaded = true
}
})
hiddenAppsIdsQuery.query(
workbench.class.HiddenApplication,
{
@@ -86,22 +110,20 @@
updateExcludedApps()
function isAppVisibleInSwitcher (app: Application, disabledModules: Set<Ref<Application>>): boolean {
return !hiddenAppsIds.includes(app._id) && !excludedApps.includes(app.alias) && !disabledModules.has(app._id)
}
$: topApps = apps
.filter((it) => it.position === 'top' && !hiddenAppsIds.includes(it._id) && !excludedApps.includes(it.alias))
.filter((it) => it.position === 'top' && isAppVisibleInSwitcher(it, disabledApplications))
.sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity))
$: midApps = apps
.filter(
(it) =>
!hiddenAppsIds.includes(it._id) &&
!excludedApps.includes(it.alias) &&
it.position !== 'top' &&
it.position !== 'bottom'
(it) => it.position !== 'top' && it.position !== 'bottom' && isAppVisibleInSwitcher(it, disabledApplications)
)
.sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity))
$: bottomApps = apps.filter(
(it) => it.position === 'bottom' && !hiddenAppsIds.includes(it._id) && !excludedApps.includes(it.alias)
)
$: bottomApps = apps.filter((it) => it.position === 'bottom' && isAppVisibleInSwitcher(it, disabledApplications))
const inboxClient = InboxNotificationsClientImpl.getClient()
const inboxNotificationsByContextStore = inboxClient.inboxNotificationsByContext
@@ -135,7 +157,7 @@
</script>
<div class="flex-{direction === 'horizontal' ? 'row-center' : 'col-center'} clear-mins apps-{direction} relative">
{#if loaded}
{#if loaded && permissionsLoaded}
<Scroller
invertScroll
padding={direction === 'horizontal' ? '.75rem .5rem' : '.5rem .75rem'}