From ac1553bd38e098cf16dcd1b3ff1c3c9941e74213 Mon Sep 17 00:00:00 2001 From: Artyom Savchenko Date: Sun, 12 Apr 2026 13:25:31 +0700 Subject: [PATCH] feat: Guest auto-join (#10751) * feat: Guest auto-join Signed-off-by: Artem Savchenko * Fix tests Signed-off-by: Artem Savchenko * Revert "Fix tests" This reverts commit e3b7c7425f0cc38fabb1057af769caea8c80899d. * Update tests Signed-off-by: Artem Savchenko * Clean up Signed-off-by: Artem Savchenko * Clean up Signed-off-by: Artem Savchenko * Minor fixes Signed-off-by: Artem Savchenko * Clean up Signed-off-by: Artem Savchenko * Temp ignore test Signed-off-by: Artem Savchenko --------- Signed-off-by: Artem Savchenko --- common/config/rush/pnpm-lock.yaml | 3 + foundations/core/packages/core/lang/cs.json | 2 + foundations/core/packages/core/lang/de.json | 2 + foundations/core/packages/core/lang/en.json | 2 + foundations/core/packages/core/lang/es.json | 2 + foundations/core/packages/core/lang/fr.json | 2 + foundations/core/packages/core/lang/it.json | 2 + foundations/core/packages/core/lang/ja.json | 2 + .../core/packages/core/lang/pt-br.json | 2 + foundations/core/packages/core/lang/pt.json | 2 + foundations/core/packages/core/lang/ru.json | 2 + foundations/core/packages/core/lang/tr.json | 2 + foundations/core/packages/core/lang/zh.json | 2 + .../core/src/__tests__/autoJoinRoles.test.ts | 27 ++++++++ .../core/packages/core/src/autoJoinRoles.ts | 33 +++++++++ foundations/core/packages/core/src/classes.ts | 2 + .../core/packages/core/src/component.ts | 2 + foundations/core/packages/core/src/index.ts | 1 + models/chunter/src/index.ts | 13 +++- models/core/src/security.ts | 3 + packages/ui/src/components/Toggle.svelte | 2 +- .../components/navigator/CreateSpace.svelte | 35 +++++++++- .../src/components/EditChannel.svelte | 4 +- .../components/EditChannelSettingsTab.svelte | 69 ++++++++++++++++++- .../chat/create/CreateChannel.svelte | 45 +++++++++++- .../components/projects/CreateProject.svelte | 37 +++++++++- server-plugins/contact-resources/package.json | 1 + server-plugins/contact-resources/src/index.ts | 43 ++++++++---- tests/sanity/tests/chat/chat.spec.ts | 3 +- 29 files changed, 324 insertions(+), 23 deletions(-) create mode 100644 foundations/core/packages/core/src/__tests__/autoJoinRoles.test.ts create mode 100644 foundations/core/packages/core/src/autoJoinRoles.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index d672a923bc..ae7ededf41 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -32754,6 +32754,9 @@ importers: '@hcengineering/card': specifier: workspace:^0.7.0 version: link:../../plugins/card + '@hcengineering/chunter': + specifier: workspace:^0.7.0 + version: link:../../plugins/chunter '@hcengineering/contact': specifier: workspace:^0.7.0 version: link:../../plugins/contact diff --git a/foundations/core/packages/core/lang/cs.json b/foundations/core/packages/core/lang/cs.json index f9fa410241..e9f035cd82 100644 --- a/foundations/core/packages/core/lang/cs.json +++ b/foundations/core/packages/core/lang/cs.json @@ -61,6 +61,8 @@ "ArchiveSpaceDescription": "Umožňuje uživatelům archivovat prostor", "AutoJoin": "Automatické připojení", "AutoJoinDescr": "Automaticky připojit nové zaměstnance k tomuto prostoru", + "AutoJoinGuests": "Automaticky připojit hosty", + "AutoJoinGuestsDescr": "Pokud je zapnuto, hosté pracovního prostoru se při aktivaci přidají do tohoto prostoru", "RBAC": "Řízení přístupu na základě rolí", "RBACDescr": "Vyžadovat řízení přístupu na základě rolí pro provádění akcí v tomto prostoru", "BlobSize": "Velikost", diff --git a/foundations/core/packages/core/lang/de.json b/foundations/core/packages/core/lang/de.json index 803b5b8098..57272652b5 100644 --- a/foundations/core/packages/core/lang/de.json +++ b/foundations/core/packages/core/lang/de.json @@ -61,6 +61,8 @@ "ArchiveSpaceDescription": "Gewährt Benutzern die Möglichkeit, den Arbeitsbereich zu archivieren", "AutoJoin": "Automatisch beitreten", "AutoJoinDescr": "Neue Mitarbeiter automatisch diesem Arbeitsbereich hinzufügen", + "AutoJoinGuests": "Gäste automatisch hinzufügen", + "AutoJoinGuestsDescr": "Wenn aktiviert, werden Arbeitsbereichs-Gäste bei der Aktivierung diesem Bereich hinzugefügt", "RBAC": "Rollenbasierte Zugriffskontrolle", "RBACDescr": "Erfordert rollenbasierten Zugriff, um Aktionen in diesem Arbeitsbereich auszuführen", "BlobSize": "Größe", diff --git a/foundations/core/packages/core/lang/en.json b/foundations/core/packages/core/lang/en.json index 6216e771fe..aacdb84a17 100644 --- a/foundations/core/packages/core/lang/en.json +++ b/foundations/core/packages/core/lang/en.json @@ -61,6 +61,8 @@ "ArchiveSpaceDescription": "Grants users ability to archive the space", "AutoJoin": "Auto join", "AutoJoinDescr": "Automatically join new employees to this space", + "AutoJoinGuests": "Auto-join guests", + "AutoJoinGuestsDescr": "When enabled, workspace guests are added to this space on activation", "RBAC": "Role-based access control", "RBACDescr": "Require role-based access to perform actions in this space", "BlobSize": "Size", diff --git a/foundations/core/packages/core/lang/es.json b/foundations/core/packages/core/lang/es.json index 7842f2fa8b..ff1867cea6 100644 --- a/foundations/core/packages/core/lang/es.json +++ b/foundations/core/packages/core/lang/es.json @@ -54,6 +54,8 @@ "ArchiveSpaceDescription": "Concede a los usuarios la capacidad de archivar el espacio", "AutoJoin": "Auto unirse", "AutoJoinDescr": "Unirse automáticamente a los nuevos empleados a este espacio", + "AutoJoinGuests": "Unir invitados automáticamente", + "AutoJoinGuestsDescr": "Si está activado, los invitados del espacio de trabajo se añaden a este espacio al activarse", "RBAC": "Control de acceso basado en roles", "RBACDescr": "Requerir acceso basado en roles para realizar acciones en este espacio", "BlobSize": "Tamaño", diff --git a/foundations/core/packages/core/lang/fr.json b/foundations/core/packages/core/lang/fr.json index d344db529e..9e6e354a0e 100644 --- a/foundations/core/packages/core/lang/fr.json +++ b/foundations/core/packages/core/lang/fr.json @@ -61,6 +61,8 @@ "ArchiveSpaceDescription": "Accorde aux utilisateurs la capacité d'archiver l'espace", "AutoJoin": "Rejoindre automatiquement", "AutoJoinDescr": "Ajouter automatiquement les nouveaux employés à cet espace", + "AutoJoinGuests": "Rejoindre automatiquement les invités", + "AutoJoinGuestsDescr": "Si activé, les invités de l’espace de travail sont ajoutés à cet espace à l’activation", "RBAC": "Contrôle d'accès basé sur les rôles", "RBACDescr": "Exiger un accès basé sur les rôles pour effectuer des actions dans cet espace", "BlobSize": "Taille", diff --git a/foundations/core/packages/core/lang/it.json b/foundations/core/packages/core/lang/it.json index cb491afd14..86794b5ef1 100644 --- a/foundations/core/packages/core/lang/it.json +++ b/foundations/core/packages/core/lang/it.json @@ -61,6 +61,8 @@ "ArchiveSpaceDescription": "Concede agli utenti la possibilità di archiviare lo spazio", "AutoJoin": "Partecipazione automatica", "AutoJoinDescr": "Aggiungi automaticamente i nuovi dipendenti a questo spazio", + "AutoJoinGuests": "Partecipazione automatica ospiti", + "AutoJoinGuestsDescr": "Se abilitato, gli ospiti del workspace vengono aggiunti a questo spazio all’attivazione", "RBAC": "Controllo accessi basato sui ruoli", "RBACDescr": "Richiedi accesso basato sui ruoli per eseguire azioni in questo spazio", "BlobSize": "Dimensione", diff --git a/foundations/core/packages/core/lang/ja.json b/foundations/core/packages/core/lang/ja.json index 4d9b5e848d..d71787fe1d 100644 --- a/foundations/core/packages/core/lang/ja.json +++ b/foundations/core/packages/core/lang/ja.json @@ -61,6 +61,8 @@ "ArchiveSpaceDescription": "ユーザーにスペースをアーカイブする権限を付与します", "AutoJoin": "自動参加", "AutoJoinDescr": "新しいユーザーを自動的にこのスペースに参加させます", + "AutoJoinGuests": "ゲストの自動参加", + "AutoJoinGuestsDescr": "有効にすると、ワークスペースのゲストは有効化時にこのスペースのメンバーに追加されます", "RBAC": "ロールベースアクセス制御", "RBACDescr": "このスペースでの操作を実行するためにロールベースのアクセスを要求します", "BlobSize": "サイズ", diff --git a/foundations/core/packages/core/lang/pt-br.json b/foundations/core/packages/core/lang/pt-br.json index c128d88b0c..6c00b6811c 100644 --- a/foundations/core/packages/core/lang/pt-br.json +++ b/foundations/core/packages/core/lang/pt-br.json @@ -54,6 +54,8 @@ "ArchiveSpaceDescription": "Concede aos usuários a capacidade de arquivar o espaço", "AutoJoin": "Auto adesão", "AutoJoinDescr": "Adesão automática de novos funcionários a este espaço", + "AutoJoinGuests": "Adesão automática de convidados", + "AutoJoinGuestsDescr": "Se ativado, convidados do espaço de trabalho são adicionados a este espaço na ativação", "RBAC": "Controle de acesso baseado em funções", "RBACDescr": "Exigir acesso baseado em funções para executar ações neste espaço", "BlobSize": "Tamanho", diff --git a/foundations/core/packages/core/lang/pt.json b/foundations/core/packages/core/lang/pt.json index 9ae51b2c3e..befce7c131 100644 --- a/foundations/core/packages/core/lang/pt.json +++ b/foundations/core/packages/core/lang/pt.json @@ -54,6 +54,8 @@ "ArchiveSpaceDescription": "Concede aos usuários a capacidade de arquivar o espaço", "AutoJoin": "Auto adesão", "AutoJoinDescr": "Adesão automática de novos funcionários a este espaço", + "AutoJoinGuests": "Adesão automática de convidados", + "AutoJoinGuestsDescr": "Se ativado, convidados do espaço de trabalho são adicionados a este espaço na ativação", "RBAC": "Controle de acesso baseado em funções", "RBACDescr": "Exigir acesso baseado em funções para executar ações neste espaço", "BlobSize": "Tamanho", diff --git a/foundations/core/packages/core/lang/ru.json b/foundations/core/packages/core/lang/ru.json index 8bb9bfa12b..1d72f0e94b 100644 --- a/foundations/core/packages/core/lang/ru.json +++ b/foundations/core/packages/core/lang/ru.json @@ -61,6 +61,8 @@ "ArchiveSpaceDescription": "Дает пользователям разрешение архивировать пространство", "AutoJoin": "Автоприсоединение", "AutoJoinDescr": "Автоматически присоединять новых сотрудников к этому пространству", + "AutoJoinGuests": "Автоприсоединение гостей", + "AutoJoinGuestsDescr": "При активации добавлять гостей воркспейса в участники этого пространства", "RBAC": "Ролевой доступ", "RBACDescr": "Требовать ролевой доступ для выполнения действий в этом пространстве", "BlobSize": "Размер", diff --git a/foundations/core/packages/core/lang/tr.json b/foundations/core/packages/core/lang/tr.json index 2595a1a0f9..d6f6ce068c 100644 --- a/foundations/core/packages/core/lang/tr.json +++ b/foundations/core/packages/core/lang/tr.json @@ -61,6 +61,8 @@ "ArchiveSpaceDescription": "Kullanıcılara alanı arşivleme yetkisi verir", "AutoJoin": "Otomatik katıl", "AutoJoinDescr": "Yeni çalışanları bu alana otomatik olarak ekle", + "AutoJoinGuests": "Misafirleri otomatik katıl", + "AutoJoinGuestsDescr": "Etkinleştirilirse, çalışma alanı misafirleri etkinleştirmede bu alana eklenir", "RBAC": "Rol tabanlı erişim kontrolü", "RBACDescr": "Bu alanda işlemleri gerçekleştirmek için rol tabanlı erişim gerektir", "BlobSize": "Boyut", diff --git a/foundations/core/packages/core/lang/zh.json b/foundations/core/packages/core/lang/zh.json index 3fd492d867..dd55ddf5ef 100644 --- a/foundations/core/packages/core/lang/zh.json +++ b/foundations/core/packages/core/lang/zh.json @@ -61,6 +61,8 @@ "ArchiveSpaceDescription": "授予用户归档空间的权限", "AutoJoin": "自动加入", "AutoJoinDescr": "自动将新员工加入此空间", + "AutoJoinGuests": "访客自动加入", + "AutoJoinGuestsDescr": "启用后,工作区访客在激活时会加入此空间", "RBAC": "基于角色的访问控制", "RBACDescr": "要求基于角色的访问权限以在此空间中执行操作", "BlobSize": "大小", diff --git a/foundations/core/packages/core/src/__tests__/autoJoinRoles.test.ts b/foundations/core/packages/core/src/__tests__/autoJoinRoles.test.ts new file mode 100644 index 0000000000..f2211e46b4 --- /dev/null +++ b/foundations/core/packages/core/src/__tests__/autoJoinRoles.test.ts @@ -0,0 +1,27 @@ +// +// Copyright © 2026 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { AccountRole } from '../classes' +import { setWorkspaceGuestAutoJoinRoles } from '../autoJoinRoles' + +describe('autoJoinRoles', () => { + test('setWorkspaceGuestAutoJoinRoles toggles Guest only', () => { + expect(setWorkspaceGuestAutoJoinRoles(undefined, false)).toEqual([]) + expect(setWorkspaceGuestAutoJoinRoles([AccountRole.User], true).sort()).toEqual( + [AccountRole.User, AccountRole.Guest].sort() + ) + expect(setWorkspaceGuestAutoJoinRoles([AccountRole.Guest, AccountRole.User], false)).toEqual([AccountRole.User]) + }) +}) diff --git a/foundations/core/packages/core/src/autoJoinRoles.ts b/foundations/core/packages/core/src/autoJoinRoles.ts new file mode 100644 index 0000000000..737f9dbb03 --- /dev/null +++ b/foundations/core/packages/core/src/autoJoinRoles.ts @@ -0,0 +1,33 @@ +// +// Copyright © 2026 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { AccountRole } from './classes' + +/** + * Toggle {@link AccountRole.Guest} in {@link Space.autoJoinForRoles}; other roles are preserved. + * @public + */ +export function setWorkspaceGuestAutoJoinRoles ( + existing: AccountRole[] | undefined, + includeGuest: boolean +): AccountRole[] { + const next = new Set(existing ?? []) + if (includeGuest) { + next.add(AccountRole.Guest) + } else { + next.delete(AccountRole.Guest) + } + return Array.from(next) +} diff --git a/foundations/core/packages/core/src/classes.ts b/foundations/core/packages/core/src/classes.ts index 2b8c9954cf..d780f9cc53 100644 --- a/foundations/core/packages/core/src/classes.ts +++ b/foundations/core/packages/core/src/classes.ts @@ -499,6 +499,8 @@ export interface Space extends Doc { archived: boolean owners?: AccountUuid[] autoJoin?: boolean + /** If it includes {@link AccountRole.Guest}, that guest is auto-added to members on activation (see OnEmployeeCreate). */ + autoJoinForRoles?: AccountRole[] } /** diff --git a/foundations/core/packages/core/src/component.ts b/foundations/core/packages/core/src/component.ts index 5d132e5098..0fd395cf6e 100644 --- a/foundations/core/packages/core/src/component.ts +++ b/foundations/core/packages/core/src/component.ts @@ -299,6 +299,8 @@ export default plugin(coreId, { ArchiveSpaceDescription: '' as IntlString, AutoJoin: '' as IntlString, AutoJoinDescr: '' as IntlString, + AutoJoinGuests: '' as IntlString, + AutoJoinGuestsDescr: '' as IntlString, RBAC: '' as IntlString, RBACDescr: '' as IntlString, Version: '' as IntlString diff --git a/foundations/core/packages/core/src/index.ts b/foundations/core/packages/core/src/index.ts index de5ad39f63..ddafa5c49d 100644 --- a/foundations/core/packages/core/src/index.ts +++ b/foundations/core/packages/core/src/index.ts @@ -15,6 +15,7 @@ import core from './component' export * from './classes' +export * from './autoJoinRoles' export * from './client' export * from './collaboration' export { diff --git a/models/chunter/src/index.ts b/models/chunter/src/index.ts index 420d973a85..6d7231b9dc 100644 --- a/models/chunter/src/index.ts +++ b/models/chunter/src/index.ts @@ -241,7 +241,18 @@ export function createModel (builder: Builder): void { }) builder.mixin(chunter.class.DirectMessage, core.class.Class, chunter.mixin.ObjectChatPanel, { - ignoreKeys: ['archived', 'collaborators', 'lastMessage', 'pinned', 'topic', 'description', 'members', 'owners'] + ignoreKeys: [ + 'archived', + 'collaborators', + 'lastMessage', + 'pinned', + 'topic', + 'description', + 'members', + 'owners', + 'autoJoin', + 'autoJoinForRoles' + ] }) builder.createDoc(activity.class.ReplyProvider, core.space.Model, { diff --git a/models/core/src/security.ts b/models/core/src/security.ts index 186d008c47..be3c300db4 100644 --- a/models/core/src/security.ts +++ b/models/core/src/security.ts @@ -84,6 +84,9 @@ export class TSpace extends TDoc implements Space { @Prop(TypeBoolean(), core.string.AutoJoin) autoJoin?: boolean + + @Prop(ArrOf(TypeString()), core.string.AutoJoinGuests) + autoJoinForRoles?: AccountRole[] } @Model(core.class.SystemSpace, core.class.Space) diff --git a/packages/ui/src/components/Toggle.svelte b/packages/ui/src/components/Toggle.svelte index 3998717dcb..bb8f8e129d 100644 --- a/packages/ui/src/components/Toggle.svelte +++ b/packages/ui/src/components/Toggle.svelte @@ -25,7 +25,7 @@ const dispatch = createEventDispatcher() -