Fix guest settings (#5597)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2024-05-14 21:06:57 +05:00 committed by GitHub
parent 9c22b1732f
commit 7eb795f9b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 68 additions and 95 deletions

View File

@ -17,6 +17,7 @@
import activity, { type ActivityMessage } from '@hcengineering/activity'
import chunter from '@hcengineering/chunter'
import {
AccountRole,
DOMAIN_MODEL,
IndexKind,
type Account,
@ -390,7 +391,7 @@ export function createModel (builder: Builder): void {
icon: notification.icon.Notifications,
component: notification.component.NotificationSettings,
group: 'settings-account',
secured: false,
role: AccountRole.Guest,
order: 1500
},
notification.ids.NotificationSettings

View File

@ -15,7 +15,7 @@
import activity from '@hcengineering/activity'
import contact from '@hcengineering/contact'
import { DOMAIN_MODEL, type Account, type Domain, type Ref } from '@hcengineering/core'
import { AccountRole, DOMAIN_MODEL, type Account, type Domain, type Ref } from '@hcengineering/core'
import { Mixin, Model, type Builder, UX } from '@hcengineering/model'
import core, { TClass, TConfiguration, TDoc } from '@hcengineering/model-core'
import view, { createAction } from '@hcengineering/model-view'
@ -62,7 +62,7 @@ export class TSettingsCategory extends TDoc implements SettingsCategory {
label!: IntlString
icon!: Asset
component!: AnyComponent
secured!: boolean
role!: AccountRole
adminOnly?: boolean
}
@ -72,7 +72,7 @@ export class TWorkspaceSettingCategory extends TDoc implements SettingsCategory
label!: IntlString
icon!: Asset
component!: AnyComponent
secured!: boolean
role!: AccountRole
}
@Model(setting.class.IntegrationType, core.class.Doc, DOMAIN_MODEL)
@ -150,7 +150,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.AccountSettings,
component: setting.component.Profile,
group: 'settings-account',
secured: false,
role: AccountRole.Guest,
order: 0
},
setting.ids.Profile
@ -165,7 +165,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.Password,
component: setting.component.Password,
group: 'settings-account',
secured: false,
role: AccountRole.Guest,
order: 1000
},
setting.ids.Password
@ -182,7 +182,7 @@ export function createModel (builder: Builder): void {
navigation: setting.component.WorkspaceSettings
},
group: 'settings',
secured: false,
role: AccountRole.User,
order: 2000
},
setting.ids.Setting
@ -196,7 +196,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.Integrations,
component: setting.component.Integrations,
group: 'settings-account',
secured: false,
role: AccountRole.User,
order: 1500
},
setting.ids.Integrations
@ -210,7 +210,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.Owners,
component: setting.component.Owners,
order: 1000,
secured: true
role: AccountRole.Maintainer
},
setting.ids.Owners
)
@ -223,7 +223,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.Views,
component: setting.component.Spaces,
order: 1100,
secured: true
role: AccountRole.Maintainer
},
setting.ids.Spaces
)
@ -236,7 +236,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.Setting,
component: setting.component.Configure,
order: 1200,
secured: true,
role: AccountRole.Owner,
adminOnly: true
},
setting.ids.Configure
@ -250,7 +250,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.AccountSettings,
component: setting.component.WorkspaceSetting,
order: 1300,
secured: true
role: AccountRole.Owner
},
setting.ids.WorkspaceSetting
)
@ -263,7 +263,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.Clazz,
component: setting.component.ClassSetting,
group: 'settings-editor',
secured: false,
role: AccountRole.Maintainer,
order: 4500
},
setting.ids.ClassSetting
@ -277,7 +277,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.Enums,
component: setting.component.EnumSetting,
group: 'settings-editor',
secured: false,
role: AccountRole.User,
order: 4600,
expandable: true
},
@ -292,7 +292,7 @@ export function createModel (builder: Builder): void {
icon: setting.icon.InviteSettings,
component: setting.component.InviteSetting,
group: 'settings-editor',
secured: true,
role: AccountRole.Maintainer,
order: 4700
},
setting.ids.InviteSettings
@ -603,7 +603,7 @@ export function createModel (builder: Builder): void {
tools: setting.component.ManageSpaceTypesTools
},
group: 'settings-editor',
secured: false,
role: AccountRole.User,
order: 6000,
expandable: true
},

View File

@ -14,7 +14,7 @@
// limitations under the License.
//
import { type Domain, DOMAIN_MODEL, IndexKind, type Ref, type Markup } from '@hcengineering/core'
import { type Domain, DOMAIN_MODEL, IndexKind, type Ref, type Markup, AccountRole } from '@hcengineering/core'
import { type Builder, Index, Model, Prop, TypeString, UX, TypeMarkup } from '@hcengineering/model'
import core, { TDoc, TSpace } from '@hcengineering/model-core'
import textEditor from '@hcengineering/model-text-editor'
@ -76,7 +76,7 @@ export function createModel (builder: Builder): void {
icon: templates.icon.Templates,
component: templates.component.Templates,
group: 'settings-editor',
secured: false,
role: AccountRole.User,
order: 3500
},
templates.ids.Templates

View File

@ -635,7 +635,7 @@ export function createModel (builder: Builder): void {
icon: tracker.icon.Relations,
component: tracker.component.SettingsRelatedTargets,
group: 'settings-editor',
secured: false,
role: AccountRole.Maintainer,
order: 4000
})

View File

@ -7,6 +7,7 @@
"scripts": {
"build": "compile ui",
"build:docs": "api-extractor run --local",
"svelte-check": "do-svelte-check",
"format": "format src",
"build:watch": "compile ui",
"_phase:build": "compile ui",

View File

@ -14,7 +14,6 @@
// limitations under the License.
-->
<script lang="ts">
import { deepEqual } from 'fast-equals'
import { AccountArrayEditor } from '@hcengineering/contact-resources'
import core, {
Account,
@ -22,18 +21,17 @@
Ref,
Role,
RolesAssignment,
SortingOrder,
SpaceType,
WithLookup
} from '@hcengineering/core'
import lead, { Funnel } from '@hcengineering/lead'
import presentation, { createQuery, getClient, SpaceCreateCard } from '@hcengineering/presentation'
import presentation, { getClient, SpaceCreateCard } from '@hcengineering/presentation'
import task, { ProjectType } from '@hcengineering/task'
import ui, { Component, EditBox, Grid, Label, ToggleWithLabel } from '@hcengineering/ui'
import { deepEqual } from 'fast-equals'
import { createEventDispatcher } from 'svelte'
import leadRes from '../plugin'
import view from '@hcengineering/view'
export let funnel: Funnel | undefined = undefined
const dispatch = createEventDispatcher()
@ -157,10 +155,7 @@
}
$: canSave =
name.trim().length > 0 &&
!(members.length === 0 && isPrivate) &&
owners.length > 0 &&
(!isPrivate || owners.some((o) => members.includes(o)))
name.trim().length > 0 && members.length > 0 && owners.length > 0 && owners.some((o) => members.includes(o))
</script>
<SpaceCreateCard

View File

@ -15,7 +15,7 @@
<script lang="ts">
import { createEventDispatcher, onDestroy } from 'svelte'
import contact, { Employee, PersonAccount, combineName, getFirstName, getLastName } from '@hcengineering/contact'
import { ChannelsEditor, EditableAvatar, employeeByIdStore } from '@hcengineering/contact-resources'
import { ChannelsEditor, EditableAvatar, employeeByIdStore, personByIdStore } from '@hcengineering/contact-resources'
import { Ref, getCurrentAccount } from '@hcengineering/core'
import login from '@hcengineering/login'
import { getResource } from '@hcengineering/platform'
@ -32,12 +32,12 @@
let avatarEditor: EditableAvatar
const account = getCurrentAccount() as PersonAccount
const employee = account !== undefined ? $employeeByIdStore.get(account.person as Ref<Employee>) : undefined
const employee = account !== undefined ? $personByIdStore.get(account.person) : undefined
let firstName = employee ? getFirstName(employee.name) : ''
let lastName = employee ? getLastName(employee.name) : ''
onDestroy(
employeeByIdStore.subscribe((p) => {
personByIdStore.subscribe((p) => {
const emp = p.get(account.person as Ref<Employee>)
if (emp !== undefined) {
firstName = getFirstName(emp.name)

View File

@ -57,7 +57,7 @@
setting.class.SettingsCategory,
{},
(res) => {
categories = hasAccountRole(account, AccountRole.Maintainer) ? res : res.filter((p) => !p.secured)
categories = res.filter((p) => hasAccountRole(account, p.role))
category = findCategory(categoryId)
},
{ sort: { order: 1 } }
@ -167,11 +167,13 @@
label={setting.string.SelectWorkspace}
on:click={selectWorkspace}
/>
<NavItem
icon={setting.icon.InviteWorkspace}
label={setting.string.InviteWorkspace}
on:click={inviteWorkspace}
/>
{#if hasAccountRole(account, AccountRole.User)}
<NavItem
icon={setting.icon.InviteWorkspace}
label={setting.string.InviteWorkspace}
on:click={inviteWorkspace}
/>
{/if}
<NavItem icon={setting.icon.Signout} label={setting.string.Signout} on:click={signOut} />
</NavFooter>
</div>

View File

@ -45,7 +45,7 @@
setting.class.WorkspaceSettingCategory,
{},
(res) => {
categories = hasAccountRole(account, AccountRole.Maintainer) ? res : res.filter((p) => !p.secured)
categories = res.filter((p) => hasAccountRole(account, p.role))
if (!admin) {
categories = categories.filter((p) => !(p.adminOnly ?? false))
}

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import type { Account, Class, Configuration, Doc, Mixin, Ref, Space } from '@hcengineering/core'
import type { Account, AccountRole, Class, Configuration, Doc, Mixin, Ref, Space } from '@hcengineering/core'
import type { Plugin } from '@hcengineering/platform'
import { Asset, IntlString, plugin, Resource } from '@hcengineering/platform'
import { AnyComponent } from '@hcengineering/ui'
@ -87,7 +87,7 @@ export interface SettingsCategory extends Doc {
// If defined, will sort using order.
order?: number
secured: boolean
role: AccountRole
expandable?: boolean
adminOnly?: boolean

View File

@ -50,6 +50,7 @@
"@hcengineering/request": "^0.6.6",
"@hcengineering/notification": "^0.6.16",
"@hcengineering/notification-resources": "^0.6.0",
"@hcengineering/contact-resources": "^0.6.0",
"@hcengineering/preference": "^0.6.9",
"@hcengineering/contact": "^0.6.20",
"@hcengineering/support": "^0.6.1",

View File

@ -13,8 +13,9 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Employee, PersonAccount, formatName } from '@hcengineering/contact'
import { AccountRole, Ref, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
import contact, { PersonAccount, formatName } from '@hcengineering/contact'
import { personByIdStore } from '@hcengineering/contact-resources'
import { AccountRole, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
import login from '@hcengineering/login'
import { createQuery } from '@hcengineering/presentation'
import setting, { SettingsCategory, settingId } from '@hcengineering/setting'
@ -41,25 +42,13 @@
setting.class.SettingsCategory,
{},
(res) => {
items = hasAccountRole(getCurrentAccount(), AccountRole.Maintainer) ? res : res.filter((p) => !p.secured)
items = res.filter((p) => hasAccountRole(getCurrentAccount(), p.role))
},
{ sort: { order: 1 } }
)
const account = getCurrentAccount() as PersonAccount
let employee: Employee | undefined
const employeeQ = createQuery()
employeeQ.query(
contact.mixin.Employee,
{
_id: account.person as Ref<Employee>
},
(res) => {
employee = res[0]
},
{ limit: 1 }
)
$: person = $personByIdStore.get(account.person)
function selectCategory (sp?: SettingsCategory): void {
closePopup()
@ -130,16 +119,18 @@
selectCategory()
}
})
actions.push(
...getMenu(items, ['main']),
{
actions.push(...getMenu(items, ['main']))
if (hasAccountRole(account, AccountRole.User)) {
actions.push({
icon: setting.icon.InviteWorkspace,
label: setting.string.InviteWorkspace,
action: async () => {
inviteWorkspace()
},
group: 'end'
},
})
}
actions.push(
{
icon: setting.icon.Support,
label: workbench.string.HelpAndSupport,
@ -175,16 +166,13 @@
editProfile(items)
}}
>
{#if employee}
<Component
is={contact.component.Avatar}
props={{ avatar: employee.avatar, size: 'medium', name: employee.name }}
/>
{#if person}
<Component is={contact.component.Avatar} props={{ avatar: person.avatar, size: 'medium', name: person.name }} />
{/if}
<div class="ml-2 flex-col">
{#if account}
<div class="overflow-label fs-bold caption-color">
{employee !== undefined ? formatName(employee.name) : ''}
{person !== undefined ? formatName(person.name) : ''}
</div>
<div class="overflow-label text-sm content-dark-color">{account.email}</div>
{/if}

View File

@ -13,11 +13,11 @@
// limitations under the License.
-->
<script lang="ts">
import type { Ref } from '@hcengineering/core'
import { getCurrentAccount, type Ref } from '@hcengineering/core'
import type { Application } from '@hcengineering/workbench'
import { createQuery } from '@hcengineering/presentation'
import workbench from '@hcengineering/workbench'
import { hideApplication, showApplication } from '../utils'
import { hideApplication, isAppAllowed, showApplication } from '../utils'
import { Loading, IconCheck, Label, Icon } from '@hcengineering/ui'
import preference from '@hcengineering/preference'
// import Drag from './icons/Drag.svelte'
@ -66,6 +66,10 @@
loaded = true
}
)
const me = getCurrentAccount()
const filteredApps = apps.filter((it) => !hiddenAppsIds.includes(it._id) && isAppAllowed(it, me))
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
@ -74,7 +78,7 @@
<div class="ap-scroll">
<div class="ap-box">
{#if loaded}
{#each apps as app, i}
{#each filteredApps as app, i}
<button
bind:this={btns[i]}
class="ap-menuItem withIcon flex-row-center flex-grow"

View File

@ -14,20 +14,14 @@
-->
<script lang="ts">
import { Analytics } from '@hcengineering/analytics'
import contact, { Person, PersonAccount } from '@hcengineering/contact'
import contact, { PersonAccount } from '@hcengineering/contact'
import { personByIdStore } from '@hcengineering/contact-resources'
import core, { AccountRole, Class, Doc, Ref, Space, getCurrentAccount, hasAccountRole } from '@hcengineering/core'
import login from '@hcengineering/login'
import notification, { DocNotifyContext, InboxNotification, notificationId } from '@hcengineering/notification'
import { BrowserNotificatator, InboxNotificationsClientImpl } from '@hcengineering/notification-resources'
import { IntlString, broadcastEvent, getMetadata, getResource } from '@hcengineering/platform'
import {
ActionContext,
ComponentExtensions,
createQuery,
getClient,
isAdminUser,
reduceCalls
} from '@hcengineering/presentation'
import { ActionContext, ComponentExtensions, getClient, isAdminUser, reduceCalls } from '@hcengineering/presentation'
import setting from '@hcengineering/setting'
import support, { SupportStatus, supportLink } from '@hcengineering/support'
import {
@ -148,20 +142,7 @@
const account = getCurrentAccount() as PersonAccount
let person: Person | undefined
const personQ = createQuery()
$: account &&
personQ.query<Person>(
contact.class.Person,
{
_id: account?.person
},
(res) => {
person = res[0]
},
{ limit: 1 }
)
$: person = $personByIdStore.get(account.person)
const workspaceId = $location.path[1]
const inboxClient = InboxNotificationsClientImpl.createClient()

View File

@ -7,7 +7,7 @@
"RecoveryHTML": "<p>We received a request to reset the password for your account. To reset your password, please click the link below: <a href={link}>Reset password</a></p><p>If the Reset password link above does not work, paste the following link in your web browser's address bar: {link}</p><p>If you have not ordered a password recovery just ignore this letter.</p>",
"RecoverySubject": "Password recovery",
"InviteText": "You were invited to {ws}. To join please paste the following link in your web browser's address bar: {link}. Link valid for {expHours} hours.",
"InviteHTML": "<p>You were invited to ${ws}. To join, please click the link below: <a href={link}>Join</a></p><p>If the invite link above does not work, paste the following link in your web browser's address bar: {link}</p><p>Link valid for {expHours} hours.</p>",
"InviteHTML": "<p>You were invited to {ws}. To join, please click the link below: <a href={link}>Join</a></p><p>If the invite link above does not work, paste the following link in your web browser's address bar: {link}</p><p>Link valid for {expHours} hours.</p>",
"InviteSubject": "Invitation to {ws}"
}
}

View File

@ -7,7 +7,7 @@
"RecoveryHTML": "<p>Recibimos una solicitud para restablecer la contraseña de tu cuenta. Para restablecer tu contraseña, haz clic en el enlace a continuación: <a href={link}>Restablecer contraseña</a></p><p>Si el enlace anterior Restablecer contraseña no funciona, pega el siguiente enlace en la barra de direcciones de tu navegador web: {link}</p><p>Si no solicitaste la recuperación de la contraseña, ignora este mensaje.</p>",
"RecoverySubject": "Recuperación de Contraseña",
"InviteText": "Te invitaron a {ws}. Para unirte, pega el siguiente enlace en la barra de URL de tu navegador web: {link}. Enlace válido por {expHours} horas.",
"InviteHTML": "<p>Te invitaron a ${ws}. Para unirte, haz clic en el enlace a continuación: <a href={link}>Unirse</a></p><p>Si el enlace de invitación anterior no funciona, pega el siguiente enlace en la barra de URL de tu navegador web: {link}</p><p>Enlace válido por {expHours} horas.</p>",
"InviteHTML": "<p>Te invitaron a {ws}. Para unirte, haz clic en el enlace a continuación: <a href={link}>Unirse</a></p><p>Si el enlace de invitación anterior no funciona, pega el siguiente enlace en la barra de URL de tu navegador web: {link}</p><p>Enlace válido por {expHours} horas.</p>",
"InviteSubject": "Invitación a {ws}"
}
}

View File

@ -7,7 +7,7 @@
"RecoveryHTML": "<p>Recebemos um pedido para repor a senha da sua conta. Para repor a sua senha, clique no link abaixo: <a href={link}>Repor senha</a></p><p>Se o link Repor senha acima não funcionar, cole o seguinte link na barra de endereço do seu navegador web: {link}</p><p>Se não solicitou a recuperação da senha, ignore esta mensagem.</p>",
"RecoverySubject": "Recuperação de Senha",
"InviteText": "Foi convidado para {ws}. Para se juntar, cole o seguinte link na barra de URL do seu navegador web: {link}. Link válido por {expHours} horas.",
"InviteHTML": "<p>Foi convidado para ${ws}. Para se juntar, clique no link abaixo: <a href={link}>Juntar-se</a></p><p>Se o link de convite acima não funcionar, cole o seguinte link na barra de URL do seu navegador web: {link}</p><p>Link válido por {expHours} horas.</p>",
"InviteHTML": "<p>Foi convidado para {ws}. Para se juntar, clique no link abaixo: <a href={link}>Juntar-se</a></p><p>Se o link de convite acima não funcionar, cole o seguinte link na barra de URL do seu navegador web: {link}</p><p>Link válido por {expHours} horas.</p>",
"InviteSubject": "Convite para {ws}"
}
}

View File

@ -1,7 +1,7 @@
{
"string": {
"ConfirmationText": "Спасибо за ваш интерес к {name}. Для завершения регистрации скопируйте ссылку в адресную строку вашего браузера {link}. С уважением, Команда {name}.",
"ConfirmationHTML": "<p>Здравствуйте,</p><p>Спасибо за ваш интерес к {name}. Для завершения регистрации пройдите по <a href=${link}>этой ссылке</a> или скопируйте ссылку ниже в адресную строку вашего браузера.</p><p>{link}</p><p>С уважением,</p><p>Команда {name}.</p>",
"ConfirmationHTML": "<p>Здравствуйте,</p><p>Спасибо за ваш интерес к {name}. Для завершения регистрации пройдите по <a href={link}>этой ссылке</a> или скопируйте ссылку ниже в адресную строку вашего браузера.</p><p>{link}</p><p>С уважением,</p><p>Команда {name}.</p>",
"ConfirmationSubject": "Подтвердите адрес электронной почты для регистрации на {name}",
"RecoveryText": "Мы получили запрос на сброс пароля для вашей учетной записи. Чтобы сбросить пароль, пожалуйста, перейдите по ссылке ниже: {link}. Если вы не заказывали восстановление пароля, просто проигнорируйте это письмо.",
"RecoveryHTML": "<p>Мы получили запрос на сброс пароля для вашей учетной записи. Чтобы сбросить пароль, пожалуйста, перейдите по ссылке ниже: <a href={link}>Сбросить пароль</a></p><p>Если ссылка выше не работает, скопируйте следующую ссылку в адресную строку вашего браузера: {link}</p><p>Если вы не заказывали восстановление пароля, просто проигнорируйте это письмо.</p>",