Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-08-24 21:51:30 +06:00 committed by GitHub
parent 9396f7ef20
commit 821ed6cd7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 233 additions and 230 deletions

View File

@ -114,13 +114,11 @@ export function devTool (
.command('create-account <email>')
.description('create user and corresponding account in master database')
.requiredOption('-p, --password <password>', 'user password')
.requiredOption('-f, --first <first>', 'first name')
.requiredOption('-l, --last <last>', 'last name')
.action(async (email: string, cmd) => {
const { mongodbUri } = prepareTools()
return await withDatabase(mongodbUri, async (db) => {
console.log(`creating account ${cmd.first as string} ${cmd.last as string} (${email})...`)
await createAcc(db, productId, email, cmd.password, cmd.first, cmd.last, true)
await createAcc(db, productId, email, cmd.password, true)
})
})
@ -139,11 +137,13 @@ export function devTool (
program
.command('assign-workspace <email> <workspace>')
.description('assign workspace')
.requiredOption('-f, --first <first>', 'first name')
.requiredOption('-l, --last <last>', 'last name')
.action(async (email: string, workspace: string, cmd) => {
const { mongodbUri } = prepareTools()
return await withDatabase(mongodbUri, async (db, client) => {
console.log(`assigning user ${email} to ${workspace}...`)
await assignWorkspace(db, productId, email, workspace)
await assignWorkspace(db, productId, email, workspace, cmd.first, cmd.last)
})
})

View File

@ -90,7 +90,6 @@ export class TChannelProvider extends TDoc implements ChannelProvider {
export class TContact extends TDoc implements Contact {
@Prop(TypeString(), contact.string.Name)
@Index(IndexKind.FullText)
@Hidden()
name!: string
avatar?: string | null
@ -168,10 +167,6 @@ export class TEmployee extends TPerson implements Employee {
@Hidden()
statuses?: number
@Prop(TypeString(), contact.string.DisplayName)
@Hidden()
displayName?: string | null
@Prop(TypeString(), contact.string.Position)
@Hidden()
position?: string | null
@ -293,6 +288,15 @@ export function createModel (builder: Builder): void {
index: 100
})
builder.createDoc(activity.class.TxViewlet, core.space.Model, {
objectClass: contact.class.Person,
icon: contact.icon.Person,
txClass: core.class.TxUpdateDoc,
labelComponent: contact.activity.TxNameChange,
display: 'inline',
match: { 'operations.name': { $exists: true } }
})
builder.createDoc<Viewlet>(
view.class.Viewlet,
core.space.Model,

View File

@ -26,6 +26,9 @@ import type { AnyComponent } from '@hcengineering/ui'
import { Action, ActionCategory, ViewAction } from '@hcengineering/view'
export default mergeIds(contactId, contact, {
activity: {
TxNameChange: '' as AnyComponent
},
component: {
PersonPresenter: '' as AnyComponent,
ContactRefPresenter: '' as AnyComponent,

View File

@ -139,7 +139,6 @@ export function createModel (builder: Builder): void {
core.space.Model,
{
email: systemAccountEmail,
name: systemAccountEmail,
role: AccountRole.Owner
},
core.account.System

View File

@ -48,6 +48,5 @@ export class TSpace extends TDoc implements Space {
@UX(core.string.Account, undefined, undefined, 'name')
export class TAccount extends TDoc implements Account {
email!: string
name!: string
role!: AccountRole
}

View File

@ -238,7 +238,6 @@ describe('memdb', () => {
})
const account = await model.createDoc(core.class.Account, core.space.Model, {
email: 'email',
name: 'test',
role: 0
})
await model.updateDoc(core.class.Space, core.space.Model, space, { $push: { members: account } })

View File

@ -329,7 +329,6 @@ export interface Space extends Doc {
* @public
*/
export interface Account extends Doc {
name: string
email: string
role: AccountRole
}

View File

@ -194,8 +194,8 @@ export function genMinModel (): TxCUD<Doc>[] {
const u1 = 'User1' as Ref<Account>
const u2 = 'User2' as Ref<Account>
txes.push(
createDoc(core.class.Account, { email: 'user1@site.com', name: 'user1', role: 0 }, u1),
createDoc(core.class.Account, { email: 'user2@site.com', name: 'user2', role: 0 }, u2),
createDoc(core.class.Account, { email: 'user1@site.com', role: 0 }, u1),
createDoc(core.class.Account, { email: 'user2@site.com', role: 0 }, u2),
createDoc(core.class.Space, {
name: 'Sp1',
description: '',

View File

@ -101,7 +101,6 @@ describe('query', () => {
await factory.createDoc(core.class.Account, core.space.Model, {
email: 'user1@site.com',
name: 'user1',
role: 0
})
await factory.createDoc<Channel>(core.class.Space, core.space.Model, {

View File

@ -46,7 +46,7 @@
const activityQuery = newActivity(client, attrs)
let viewlets: Map<ActivityKey, TxViewlet>
let viewlets: Map<ActivityKey, TxViewlet[]> = new Map()
let allViewlets: TxViewlet[] = []
let editableMap: Map<Ref<Class<Doc>>, boolean> | undefined = undefined
@ -61,7 +61,18 @@
)
})
$: viewlets = new Map(allViewlets.map((r) => [activityKey(r.objectClass, r.txClass), r]))
$: viewlets = buildViewletsMap(allViewlets)
function buildViewletsMap (allViewlets: TxViewlet[]): Map<ActivityKey, TxViewlet[]> {
const viewlets = new Map()
for (const res of allViewlets) {
const key = activityKey(res.objectClass, res.txClass)
const arr = viewlets.get(key) ?? []
arr.push(res)
viewlets.set(key, arr)
}
return viewlets
}
let loading = false

View File

@ -43,7 +43,7 @@
import TxViewTx from './TxViewTx.svelte'
export let tx: DisplayTx
export let viewlets: Map<ActivityKey, TxViewlet>
export let viewlets: Map<ActivityKey, TxViewlet[]>
export let showIcon: boolean = true
export let isNew: boolean = false
export let isNextNew: boolean = false

View File

@ -5,6 +5,7 @@ import core, {
Client,
Collection,
Doc,
Hierarchy,
Obj,
Ref,
TxCUD,
@ -14,7 +15,8 @@ import core, {
TxOperations,
TxProcessor,
TxUpdateDoc,
getObjectValue
getObjectValue,
matchQuery
} from '@hcengineering/core'
import { Asset, IntlString, getResource, translate } from '@hcengineering/platform'
import { getAttributePresenterClass } from '@hcengineering/presentation'
@ -82,7 +84,11 @@ export function getDTxProps (dtx: DisplayTx): any {
return { tx: dtx.tx, value: dtx.doc, isOwnTx: dtx.isOwnTx, prevValue: dtx.prevDoc }
}
function getViewlet (viewlets: Map<ActivityKey, TxViewlet>, dtx: DisplayTx): TxDisplayViewlet | undefined {
function getViewlet (
viewlets: Map<ActivityKey, TxViewlet[]>,
dtx: DisplayTx,
hierarchy: Hierarchy
): TxDisplayViewlet | undefined {
let key: string
if (dtx.mixinTx?.mixin !== undefined && dtx.tx._id === dtx.mixinTx._id) {
key = activityKey(dtx.mixinTx.mixin, dtx.tx._class)
@ -91,13 +97,21 @@ function getViewlet (viewlets: Map<ActivityKey, TxViewlet>, dtx: DisplayTx): TxD
}
const vl = viewlets.get(key)
if (vl !== undefined) {
return { ...vl, pseudo: false }
for (const viewlet of vl) {
if (viewlet.match === undefined) {
return { ...viewlet, pseudo: false }
}
const res = matchQuery([dtx.tx], viewlet.match, dtx.tx._class, hierarchy)
if (res.length > 0) {
return { ...viewlet, pseudo: false }
}
}
}
}
export async function updateViewlet (
client: TxOperations,
viewlets: Map<ActivityKey, TxViewlet>,
viewlets: Map<ActivityKey, TxViewlet[]>,
dtx: DisplayTx
): Promise<{
viewlet: TxDisplayViewlet
@ -107,7 +121,7 @@ export async function updateViewlet (
modelIcon: Asset | undefined
iconComponent: AnyComponent | undefined
}> {
let viewlet = getViewlet(viewlets, dtx)
let viewlet = getViewlet(viewlets, dtx, client.getHierarchy())
const props = getDTxProps(dtx)
let model: AttributeModel[] = []

View File

@ -946,7 +946,6 @@ async function synchronizeUsers (
})
accountId = await ops.client.createDoc(contact.class.PersonAccount, core.space.Model, {
email: u.EMAIL,
name: combineName(u.NAME, u.LAST_NAME),
person: employeeId,
role: AccountRole.User
})

View File

@ -83,7 +83,6 @@
"MergePersons": "Merge contacts",
"MergePersonsFrom": "Source contact",
"MergePersonsTo": "Final contact",
"DisplayName": "Display name",
"SelectAvatar": "Select avatar",
"AvatarProvider": "Avatar provider",
"GravatarsManaged": "Gravatars are managed",

View File

@ -84,7 +84,6 @@
"MergePersons": "Объеденить контакта",
"MergePersonsFrom": "Исходный контакт",
"MergePersonsTo": "Финальный контакт",
"DisplayName": "Отображаемое имя",
"SelectAvatar": "Выбрать аватар",
"GravatarsManaged": "Граватары управляются",
"Through": "через",

View File

@ -62,7 +62,6 @@
await client.createDoc(contact.class.PersonAccount, core.space.Model, {
email: email.trim(),
name,
person: id,
role: AccountRole.User
})

View File

@ -14,13 +14,19 @@
// limitations under the License.
-->
<script lang="ts">
import { Channel, Employee, PersonAccount, getFirstName, getLastName, Person } from '@hcengineering/contact'
import { AccountRole, getCurrentAccount, Ref } from '@hcengineering/core'
import login from '@hcengineering/login'
import { getResource } from '@hcengineering/platform'
import {
Channel,
Employee,
Person,
PersonAccount,
combineName,
getFirstName,
getLastName
} from '@hcengineering/contact'
import { AccountRole, Ref, getCurrentAccount } from '@hcengineering/core'
import { AttributeEditor, createQuery, getClient } from '@hcengineering/presentation'
import setting, { IntegrationType } from '@hcengineering/setting'
import { createFocusManager, EditBox, FocusHandler, Scroller } from '@hcengineering/ui'
import { EditBox, FocusHandler, Scroller, createFocusManager } from '@hcengineering/ui'
import { createEventDispatcher, onMount } from 'svelte'
import { ChannelsDropdown } from '..'
import contact from '../plugin'
@ -61,13 +67,15 @@
const dispatch = createEventDispatcher()
async function firstNameChange () {
const changeName = await getResource(login.function.ChangeName)
await changeName(firstName, getLastName(object.name))
await client.update(object, {
name: combineName(firstName, getLastName(object.name))
})
}
async function lastNameChange () {
const changeName = await getResource(login.function.ChangeName)
await changeName(getFirstName(object.name), lastName)
await client.update(object, {
name: combineName(getFirstName(object.name), lastName)
})
}
let integrations: Set<Ref<IntegrationType>> = new Set<Ref<IntegrationType>>()
@ -76,7 +84,7 @@
integrations = new Set(res.map((p) => p.type))
})
const sendOpen = () => dispatch('open', { ignoreKeys: ['comments', 'name', 'channels', 'city', 'displayName'] })
const sendOpen = () => dispatch('open', { ignoreKeys: ['comments', 'name', 'channels', 'city'] })
onMount(sendOpen)
async function onAvatarDone () {

View File

@ -0,0 +1,34 @@
<!--
// Copyright © 2023 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.
-->
<script lang="ts">
import { Person, formatName } from '@hcengineering/contact'
import { TxUpdateDoc } from '@hcengineering/core'
import contact from '../../plugin'
import { Label } from '@hcengineering/ui'
import activity from '@hcengineering/activity'
export let tx: TxUpdateDoc<Person>
const val = tx.operations.name
</script>
{#if val}
<span class="lower"><Label label={activity.string.Changed} /></span>
<span class="lower"><Label label={contact.string.Name} /></span>
<span class="lower"><Label label={activity.string.To} /></span>
<span class="strong overflow-label">
{formatName(val)}
</span>
{/if}

View File

@ -88,6 +88,7 @@ import ActivityChannelMessage from './components/activity/ActivityChannelMessage
import ActivityChannelPresenter from './components/activity/ActivityChannelPresenter.svelte'
import ExpandRightDouble from './components/icons/ExpandRightDouble.svelte'
import IconMembers from './components/icons/Members.svelte'
import TxNameChange from './components/activity/TxNameChange.svelte'
import contact from './plugin'
import {
@ -274,6 +275,9 @@ export default async (): Promise<Resources> => ({
KickEmployee: kickEmployee,
OpenChannel: openChannelURL
},
activity: {
TxNameChange
},
component: {
ContactArrayEditor,
PersonEditor,

View File

@ -140,7 +140,9 @@ export async function getRefs (
export async function getCurrentEmployeeName (): Promise<string> {
const me = getCurrentAccount() as PersonAccount
return formatName(me.name)
const client = getClient()
const employee = await client.findOne(contact.class.Person, { _id: me.person })
return employee !== undefined ? formatName(employee.name) : ''
}
export async function getCurrentEmployeeEmail (): Promise<string> {

View File

@ -139,7 +139,6 @@ export interface Status extends AttachedDoc {
export interface Employee extends Person {
active: boolean
statuses?: number
displayName?: string | null
position?: string | null
}
@ -263,7 +262,6 @@ export const contactPlugin = plugin(contactId, {
UseColor: '' as IntlString,
PersonFirstNamePlaceholder: '' as IntlString,
PersonLastNamePlaceholder: '' as IntlString,
DisplayName: '' as IntlString,
NumberMembers: '' as IntlString,
Position: '' as IntlString
},

View File

@ -16,7 +16,7 @@
import { AttachedData, Class, Client, Doc, FindResult, Ref, Hierarchy } from '@hcengineering/core'
import { IconSize } from '@hcengineering/ui'
import { MD5 } from 'crypto-js'
import { Channel, Contact, contactPlugin, Employee, Person } from '.'
import { Channel, Contact, contactPlugin, Person } from '.'
import { AVATAR_COLORS, GravatarPlaceholderType } from './types'
/**
@ -212,19 +212,12 @@ export function formatName (name: string): string {
* @public
*/
export function getName (hierarchy: Hierarchy, value: Contact): string {
if (isEmployee(hierarchy, value)) {
return hierarchy.as(value, contactPlugin.mixin.Employee).displayName ?? formatName(value.name)
}
if (isPerson(value)) {
if (isPerson(hierarchy, value)) {
return formatName(value.name)
}
return value.name
}
function isEmployee (hierarchy: Hierarchy, value: Contact): value is Employee {
return hierarchy.hasMixin(value, contactPlugin.mixin.Employee)
}
function isPerson (value: Contact): value is Person {
return value._class === contactPlugin.class.Person
function isPerson (hierarchy: Hierarchy, value: Contact): value is Person {
return hierarchy.isDerived(value._class, contactPlugin.class.Person)
}

View File

@ -25,6 +25,8 @@
import { onMount } from 'svelte'
const fields = [
{ id: 'given-name', name: 'first', i18n: login.string.FirstName, short: true },
{ id: 'family-name', name: 'last', i18n: login.string.LastName, short: true },
{
name: 'workspace',
i18n: login.string.Workspace,
@ -34,7 +36,9 @@
]
const object = {
workspace: ''
workspace: '',
first: '',
last: ''
}
let status: Status<any> = OK
@ -54,7 +58,7 @@
func: async () => {
status = new Status(Severity.INFO, login.status.ConnectingToServer, {})
const [loginStatus, result] = await createWorkspace(object.workspace)
const [loginStatus, result] = await createWorkspace(object.workspace, object.first, object.last)
status = loginStatus
if (result !== undefined) {

View File

@ -30,6 +30,8 @@
$: fields =
page === 'login'
? [
{ id: 'given-name', name: 'first', i18n: login.string.FirstName, short: true },
{ id: 'family-name', name: 'last', i18n: login.string.LastName, short: true },
{ id: 'email', name: 'username', i18n: login.string.Email },
{
id: 'current-password',
@ -63,7 +65,7 @@
const [loginStatus, result] =
page === 'login'
? await join(object.username, object.password, location.query?.inviteId ?? '')
? await join(object.username, object.password, object.first, object.last, location.query?.inviteId ?? '')
: await signUpJoin(
object.username,
object.password,

View File

@ -22,8 +22,6 @@
import Form from './Form.svelte'
const fields = [
{ id: 'given-name', name: 'first', i18n: login.string.FirstName, short: true },
{ id: 'family-name', name: 'last', i18n: login.string.LastName, short: true },
{ id: 'email', name: 'username', i18n: login.string.Email },
{ id: 'new-password', name: 'password', i18n: login.string.Password, password: true },
{ id: 'new-password', name: 'password2', i18n: login.string.PasswordRepeat, password: true }
@ -44,7 +42,7 @@
func: async () => {
status = new Status(Severity.INFO, login.status.ConnectingToServer, {})
const [loginStatus, result] = await signUp(object.username, object.password, object.first, object.last)
const [loginStatus, result] = await signUp(object.username, object.password)
status = loginStatus

View File

@ -16,7 +16,7 @@
import InviteLink from './components/InviteLink.svelte'
import LoginApp from './components/LoginApp.svelte'
import { changeName, changePassword, getWorkspaces, leaveWorkspace, selectWorkspace, sendInvite } from './utils'
import { changePassword, getWorkspaces, leaveWorkspace, selectWorkspace, sendInvite } from './utils'
/*!
* Anticrm Platform Login Plugin
* © 2020, 2021 Anticrm Platform Contributors.
@ -30,7 +30,6 @@ export default async () => ({
InviteLink
},
function: {
ChangeName: changeName,
LeaveWorkspace: leaveWorkspace,
ChangePassword: changePassword,
SelectWorkspace: selectWorkspace,

View File

@ -76,12 +76,7 @@ export async function doLogin (email: string, password: string): Promise<[Status
}
}
export async function signUp (
email: string,
password: string,
first: string,
last: string
): Promise<[Status, LoginInfo | undefined]> {
export async function signUp (email: string, password: string): Promise<[Status, LoginInfo | undefined]> {
const accountsUrl = getMetadata(login.metadata.AccountsUrl)
if (accountsUrl === undefined) {
@ -98,7 +93,7 @@ export async function signUp (
const request = {
method: 'createAccount',
params: [email, password, first, last]
params: [email, password]
}
try {
@ -116,7 +111,11 @@ export async function signUp (
}
}
export async function createWorkspace (workspace: string): Promise<[Status, LoginInfo | undefined]> {
export async function createWorkspace (
workspace: string,
firstName: string,
lastName: string
): Promise<[Status, LoginInfo | undefined]> {
const accountsUrl = getMetadata(login.metadata.AccountsUrl)
if (accountsUrl === undefined) {
@ -143,7 +142,7 @@ export async function createWorkspace (workspace: string): Promise<[Status, Logi
const request = {
method: 'createWorkspace',
params: [workspace]
params: [workspace, firstName, lastName]
}
try {
@ -426,6 +425,8 @@ export async function getInviteLink (expHours: number = 1, emailMask: string = '
export async function join (
email: string,
password: string,
first: string,
last: string,
inviteId: string
): Promise<[Status, WorkspaceLoginInfo | undefined]> {
const accountsUrl = getMetadata(login.metadata.AccountsUrl)
@ -444,7 +445,7 @@ export async function join (
const request = {
method: 'join',
params: [email, password, inviteId]
params: [email, password, first, last, inviteId]
}
try {
@ -503,37 +504,6 @@ export async function signUpJoin (
}
}
export async function changeName (first: string, last: string): Promise<void> {
const accountsUrl = getMetadata(login.metadata.AccountsUrl)
if (accountsUrl === undefined) {
throw new Error('accounts url not specified')
}
const overrideToken = getMetadata(login.metadata.OverrideLoginToken)
if (overrideToken !== undefined) {
const endpoint = getMetadata(login.metadata.OverrideEndpoint)
if (endpoint !== undefined) {
return
}
}
const token = getMetadata(presentation.metadata.Token) as string
const request = {
method: 'changeName',
params: [first, last]
}
await fetch(accountsUrl, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify(request)
})
}
export async function changePassword (oldPassword: string, password: string): Promise<void> {
const accountsUrl = getMetadata(login.metadata.AccountsUrl)

View File

@ -69,7 +69,6 @@ export default plugin(loginId, {
InviteLimit: '' as IntlString
},
function: {
ChangeName: '' as Resource<(first: string, last: string) => Promise<void>>,
SendInvite: '' as Resource<(email: string) => Promise<void>>,
LeaveWorkspace: '' as Resource<(email: string) => Promise<void>>,
ChangePassword: '' as Resource<(oldPassword: string, password: string) => Promise<void>>,

View File

@ -106,7 +106,7 @@
}
}
let viewlets: Map<ActivityKey, TxViewlet>
let viewlets: Map<ActivityKey, TxViewlet[]>
const listProvider = new ListSelectionProvider((offset: 1 | -1 | 0, of?: Doc, dir?: SelectDirection) => {
if (dir === 'vertical') {
@ -121,7 +121,14 @@
const descriptors = createQuery()
descriptors.query(activity.class.TxViewlet, {}, (result) => {
viewlets = new Map(result.map((r) => [activityKey(r.objectClass, r.txClass), r]))
viewlets = new Map()
for (const res of result) {
const key = activityKey(res.objectClass, res.txClass)
const arr = viewlets.get(key) ?? []
arr.push(res)
viewlets.set(key, arr)
}
viewlets = viewlets
})
let selected = 0

View File

@ -105,11 +105,18 @@
}
}
let viewlets: Map<ActivityKey, TxViewlet>
let viewlets: Map<ActivityKey, TxViewlet[]>
const descriptors = createQuery()
descriptors.query(activity.class.TxViewlet, {}, (result) => {
viewlets = new Map(result.map((r) => [activityKey(r.objectClass, r.txClass), r]))
viewlets = new Map()
for (const res of result) {
const key = activityKey(res.objectClass, res.txClass)
const arr = viewlets.get(key) ?? []
arr.push(res)
viewlets.set(key, arr)
}
viewlets = viewlets
})
let selected = 0

View File

@ -26,7 +26,7 @@
import TxView from './TxView.svelte'
export let value: DocUpdates
export let viewlets: Map<ActivityKey, TxViewlet>
export let viewlets: Map<ActivityKey, TxViewlet[]>
export let selected: boolean
export let preview: boolean = false

View File

@ -125,11 +125,18 @@
}
}
let viewlets: Map<ActivityKey, TxViewlet>
let viewlets: Map<ActivityKey, TxViewlet[]>
const descriptors = createQuery()
descriptors.query(activity.class.TxViewlet, {}, (result) => {
viewlets = new Map(result.map((r) => [activityKey(r.objectClass, r.txClass), r]))
viewlets = new Map()
for (const res of result) {
const key = activityKey(res.objectClass, res.txClass)
const arr = viewlets.get(key) ?? []
arr.push(res)
viewlets.set(key, arr)
}
viewlets = viewlets
})
let selected = 0

View File

@ -30,7 +30,7 @@
export let value: PersonAccount
export let items: DocUpdates[]
export let viewlets: Map<ActivityKey, TxViewlet>
export let viewlets: Map<ActivityKey, TxViewlet[]>
export let selected: boolean
$: firstItem = items[0]

View File

@ -35,7 +35,7 @@
export let tx: TxCUD<Doc>
export let objectId: Ref<Doc>
export let viewlets: Map<ActivityKey, TxViewlet>
export let viewlets: Map<ActivityKey, TxViewlet[]>
const client = getClient()
let ptx: DisplayTx | undefined

View File

@ -27,7 +27,7 @@
import request from '../plugin'
export let tx: Tx
const viewlets: Map<ActivityKey, TxViewlet> = new Map<ActivityKey, TxViewlet>()
const viewlets: Map<ActivityKey, TxViewlet[]> = new Map<ActivityKey, TxViewlet[]>()
const client = getClient()
let ptx: DisplayTx | undefined

View File

@ -13,11 +13,11 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { PersonAccount, formatName } from '@hcengineering/contact'
import contact, { PersonAccount } from '@hcengineering/contact'
import { EmployeePresenter, personByIdStore } from '@hcengineering/contact-resources'
import { AccountRole, getCurrentAccount, SortingOrder } from '@hcengineering/core'
import { AccountRole, SortingOrder, getCurrentAccount } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { DropdownIntlItem, DropdownLabelsIntl, Icon, Label, EditBox } from '@hcengineering/ui'
import { DropdownIntlItem, DropdownLabelsIntl, EditBox, Icon, Label } from '@hcengineering/ui'
import setting from '../plugin'
const client = getClient()
@ -64,13 +64,13 @@
<div class="ac-column max">
{#each accounts as account (account._id)}
{@const employee = $personByIdStore.get(account.person)}
{#if account.name.includes(search)}
{#if employee?.name?.includes(search)}
<div class="flex-row-center p-2">
<div class="p-1 min-w-80">
{#if employee}
<EmployeePresenter value={employee} disabled={false} />
{:else}
{formatName(account.name)}
{account.email}
{/if}
</div>
<DropdownLabelsIntl

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Employee, PersonAccount, getFirstName, getLastName } from '@hcengineering/contact'
import contact, { Employee, PersonAccount, combineName, getFirstName, getLastName } from '@hcengineering/contact'
import { ChannelsEditor, EditableAvatar, employeeByIdStore } from '@hcengineering/contact-resources'
import { Ref, getCurrentAccount } from '@hcengineering/core'
import login from '@hcengineering/login'
@ -30,7 +30,6 @@
const employee = account !== undefined ? $employeeByIdStore.get(account.person as Ref<Employee>) : undefined
let firstName = employee ? getFirstName(employee.name) : ''
let lastName = employee ? getLastName(employee.name) : ''
let displayName = employee?.displayName ?? ''
onDestroy(
employeeByIdStore.subscribe((p) => {
@ -38,7 +37,6 @@
if (emp) {
firstName = getFirstName(emp.name)
lastName = getLastName(emp.name)
displayName = emp?.displayName ?? ''
}
})
)
@ -74,10 +72,10 @@
)
}
function changeDisplayName () {
async function nameChange () {
if (employee) {
client.update(employee, {
displayName: displayName.trim() === '' ? null : displayName
await client.update(employee, {
name: combineName(firstName, lastName)
})
}
}
@ -110,27 +108,14 @@
kind={'large-style'}
autoFocus
focusIndex={1}
on:change={async () => {
const changeName = await getResource(login.function.ChangeName)
changeName(firstName, lastName)
}}
on:change={nameChange}
/>
<EditBox
placeholder={contact.string.PersonLastNamePlaceholder}
bind:value={lastName}
kind={'large-style'}
focusIndex={2}
on:change={async () => {
const changeName = await getResource(login.function.ChangeName)
changeName(firstName, lastName)
}}
/>
<EditBox
placeholder={contact.string.DisplayName}
bind:value={displayName}
kind={'large-style'}
focusIndex={2}
on:change={changeDisplayName}
on:change={nameChange}
/>
<div class="location">
<AttributeEditor

View File

@ -184,7 +184,9 @@
{/if}
<div class="ml-2 flex-col">
{#if account}
<div class="overflow-label fs-bold caption-color">{formatName(account.name)}</div>
<div class="overflow-label fs-bold caption-color">
{employee !== undefined ? formatName(employee.name) : ''}
</div>
<div class="overflow-label text-sm content-dark-color">{account.email}</div>
{/if}
</div>

View File

@ -253,7 +253,9 @@ async function getEmailNotification (
attachedTo: { $in: Array.from(contacts) }
})
const senderName = formatName(sender.name)
const senderPerson = (await control.findAll(contact.class.Person, { _id: sender.person }))[0]
const senderName = senderPerson !== undefined ? formatName(senderPerson.name) : ''
const content = await getContent(doc, senderName, type, control, '')
if (content === undefined) return []

View File

@ -221,8 +221,12 @@ async function createEmailNotificationTxes (
const receiver = (await control.modelDb.findAll(contact.class.PersonAccount, { _id: receiverId }))[0]
if (receiver === undefined) return
let senderName = ''
const senderName = sender !== undefined ? formatName(sender.name) : ''
if (sender !== undefined) {
const senderPerson = (await control.modelDb.findAll(contact.class.Person, { _id: sender.person }))[0]
senderName = senderPerson !== undefined ? formatName(senderPerson.name) : ''
}
const content = await getContent(doc, senderName, type, control, data)

View File

@ -87,7 +87,7 @@ describe('server', () => {
})
await methods.assignWorkspace(db, '', {
method: 'assignWorkspace',
params: ['andrey', workspace]
params: ['andrey', workspace, 'firstName', 'lastName']
})
const request: any = {
@ -140,7 +140,7 @@ describe('server', () => {
})
await methods.assignWorkspace(db, '', {
method: 'assignWorkspace',
params: ['andrey', workspace]
params: ['andrey', workspace, 'first', 'last']
})
// Check we had one

View File

@ -97,8 +97,6 @@ const getTransactor = (): string => {
export interface Account {
_id: ObjectId
email: string
first: string
last: string
hash: Binary
salt: Binary
workspaces: ObjectId[]
@ -349,12 +347,14 @@ export async function join (
productId: string,
email: string,
password: string,
firstName: string,
lastName: string,
inviteId: ObjectId
): Promise<WorkspaceLoginInfo> {
const invite = await getInvite(db, inviteId)
const workspace = await checkInvite(invite, email)
console.log(`join attempt:${email}, ${workspace.name}`)
await assignWorkspace(db, productId, email, workspace.name)
await assignWorkspace(db, productId, email, workspace.name, firstName, lastName)
const token = (await login(db, productId, email, password)).token
const result = await selectWorkspace(db, productId, token, workspace.name)
@ -470,8 +470,8 @@ export async function signUpJoin (
console.log(`signup join:${email} ${first} ${last}`)
const invite = await getInvite(db, inviteId)
const workspace = await checkInvite(invite, email)
await createAcc(db, productId, email, password, first, last, invite?.emailMask === email)
await assignWorkspace(db, productId, email, workspace.name)
await createAcc(db, productId, email, password, invite?.emailMask === email)
await assignWorkspace(db, productId, email, workspace.name, first, last)
const token = (await login(db, productId, email, password)).token
const result = await selectWorkspace(db, productId, token, workspace.name)
@ -487,8 +487,6 @@ export async function createAcc (
productId: string,
email: string,
password: string,
first: string,
last: string,
confirmed: boolean = false
): Promise<Account> {
const salt = randomBytes(32)
@ -508,8 +506,6 @@ export async function createAcc (
email,
hash,
salt,
first,
last,
confirmed,
workspaces: []
})
@ -527,15 +523,8 @@ export async function createAcc (
/**
* @public
*/
export async function createAccount (
db: Db,
productId: string,
email: string,
password: string,
first: string,
last: string
): Promise<LoginInfo> {
const account = await createAcc(db, productId, email, password, first, last, false)
export async function createAccount (db: Db, productId: string, email: string, password: string): Promise<LoginInfo> {
const account = await createAcc(db, productId, email, password, false)
const result = {
endpoint: getEndpoint(),
@ -634,7 +623,14 @@ export async function upgradeWorkspace (
*/
export const createUserWorkspace =
(version: Data<Version>, txes: Tx[], migrationOperation: [string, MigrateOperation][]) =>
async (db: Db, productId: string, token: string, workspace: string): Promise<LoginInfo> => {
async (
db: Db,
productId: string,
token: string,
workspace: string,
firstName: string,
lastName: string
): Promise<LoginInfo> => {
if (!/^[0-9a-z][0-9a-z-]{2,62}[0-9a-z]$/.test(workspace)) {
throw new PlatformError(new Status(Severity.ERROR, platform.status.InvalidId, { id: workspace }))
}
@ -682,7 +678,7 @@ export const createUserWorkspace =
// Update last workspace time.
await db.collection(ACCOUNT_COLLECTION).updateOne({ _id: info._id }, { $set: { lastWorkspace: Date.now() } })
await assignWorkspace(db, productId, email, workspace)
await assignWorkspace(db, productId, email, workspace, firstName, lastName)
await setRole(email, workspace, productId, AccountRole.Owner)
const result = {
endpoint: getEndpoint(),
@ -791,7 +787,14 @@ export async function setRole (email: string, workspace: string, productId: stri
/**
* @public
*/
export async function assignWorkspace (db: Db, productId: string, email: string, workspace: string): Promise<void> {
export async function assignWorkspace (
db: Db,
productId: string,
email: string,
workspace: string,
firstName: string,
lastName: string
): Promise<void> {
const initWS = getMetadata(toolPlugin.metadata.InitWorkspace)
if (initWS !== undefined && initWS === workspace) {
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
@ -799,7 +802,7 @@ export async function assignWorkspace (db: Db, productId: string, email: string,
const { workspaceId, accountId } = await getWorkspaceAndAccount(db, productId, email, workspace)
const account = await db.collection<Account>(ACCOUNT_COLLECTION).findOne({ _id: accountId })
if (account !== null) await createPersonAccount(account, productId, workspace)
if (account !== null) await createPersonAccount(account, productId, workspace, firstName, lastName)
// Add account into workspace.
await db.collection(WORKSPACE_COLLECTION).updateOne({ _id: workspaceId }, { $addToSet: { accounts: accountId } })
@ -833,12 +836,18 @@ async function createEmployee (ops: TxOperations, name: string, email: string):
return id
}
async function createPersonAccount (account: Account, productId: string, workspace: string): Promise<void> {
async function createPersonAccount (
account: Account,
productId: string,
workspace: string,
firstName: string,
lastName: string
): Promise<void> {
const connection = await connect(getTransactor(), getWorkspaceId(workspace, productId))
try {
const ops = new TxOperations(connection, core.account.System)
const name = combineName(account.first, account.last)
const name = combineName(firstName, lastName)
// Check if EmployeeAccoun is not exists
const existingAccount = await ops.findOne(contact.class.PersonAccount, { email: account.email })
if (existingAccount === undefined) {
@ -847,7 +856,6 @@ async function createPersonAccount (account: Account, productId: string, workspa
await ops.createDoc(contact.class.PersonAccount, core.space.Model, {
email: account.email,
person: employee,
name,
role: 0
})
} else {
@ -975,57 +983,6 @@ export async function restorePassword (db: Db, productId: string, token: string,
return await login(db, productId, email, password)
}
/**
* @public
*/
export async function changeName (db: Db, productId: string, token: string, first: string, last: string): Promise<void> {
const { email } = decodeToken(token)
const account = await getAccount(db, email)
if (account === null) {
throw new PlatformError(new Status(Severity.ERROR, platform.status.AccountNotFound, { account: email }))
}
await db.collection<Account>(ACCOUNT_COLLECTION).updateOne({ _id: account._id }, { $set: { first, last } })
account.first = first
account.last = last
const workspaces = await db
.collection<Workspace>(WORKSPACE_COLLECTION)
.find(withProductId(productId, { _id: { $in: account.workspaces } }))
.toArray()
const promises: Promise<void>[] = []
for (const ws of workspaces) {
promises.push(updatePersonAccount(account, ws.workspace, ws.productId))
}
await Promise.all(promises)
}
async function updatePersonAccount (account: Account, workspace: string, productId: string): Promise<void> {
const connection = await connect(getTransactor(), getWorkspaceId(workspace, productId), account.email)
try {
const ops = new TxOperations(connection, core.account.System)
const name = combineName(account.first, account.last)
const employeeAccount = await ops.findOne(contact.class.PersonAccount, { email: account.email })
if (employeeAccount === undefined) return
await ops.update(employeeAccount, {
name
})
const employee = await ops.findOne(contact.mixin.Employee, { _id: employeeAccount.person as Ref<Employee> })
if (employee === undefined) return
await ops.update(employee, {
name
})
} finally {
await connection.close()
}
}
/**
* @public
*/
@ -1246,7 +1203,6 @@ export function getMethods (
removeWorkspace: wrap(removeWorkspace),
leaveWorkspace: wrap(leaveWorkspace),
listWorkspaces: wrap(listWorkspaces),
changeName: wrap(changeName),
changePassword: wrap(changePassword),
requestPassword: wrap(requestPassword),
restorePassword: wrap(restorePassword),

View File

@ -42,7 +42,6 @@ export async function getUser (storage: ServerStorage, ctx: SessionContext): Pro
role: AccountRole.Owner,
email: systemAccountEmail,
space: core.space.Model,
name: core.string.System,
modifiedBy: core.account.System,
modifiedOn: 0
}

View File

@ -199,8 +199,8 @@ export function genMinModel (): TxCUD<Doc>[] {
const u1 = 'User1' as Ref<Account>
const u2 = 'User2' as Ref<Account>
txes.push(
createDoc(core.class.Account, { email: 'user1@site.com', name: '1', role: 0 }, u1),
createDoc(core.class.Account, { email: 'user2@site.com', name: '2', role: 0 }, u2),
createDoc(core.class.Account, { email: 'user1@site.com', role: 0 }, u1),
createDoc(core.class.Account, { email: 'user2@site.com', role: 0 }, u2),
createDoc(core.class.Space, {
name: 'Sp1',
description: '',

View File

@ -174,8 +174,8 @@ export function genMinModel (): TxCUD<Doc>[] {
const u1 = 'User1' as Ref<Account>
const u2 = 'User2' as Ref<Account>
txes.push(
createDoc(core.class.Account, { email: 'user1@site.com', name: 'test1', role: 0 }, u1),
createDoc(core.class.Account, { email: 'user2@site.com', name: 'test2', role: 0 }, u2),
createDoc(core.class.Account, { email: 'user1@site.com', role: 0 }, u1),
createDoc(core.class.Account, { email: 'user2@site.com', role: 0 }, u2),
createDoc(core.class.Space, {
name: 'Sp1',
description: '',

View File

@ -10,7 +10,7 @@ export SERVER_SECRET=secret
# Create workspace record in accounts
node ../dev/tool/bundle.js create-workspace sanity-ws -o SanityTest
# Create user record in accounts
node ../dev/tool/bundle.js create-account user1 -f John -l Appleseed -p 1234
node ../dev/tool/bundle.js create-account user1 -p 1234
node ../dev/tool/bundle.js confirm-email user1
@ -20,7 +20,7 @@ node ../dev/tool/bundle.js backup-restore ./sanity-ws sanity-ws
node ../dev/tool/bundle.js upgrade-workspace sanity-ws
# Re-assign user to workspace.
node ../dev/tool/bundle.js assign-workspace user1 sanity-ws
node ../dev/tool/bundle.js assign-workspace user1 sanity-ws -f John -l Appleseed
node ../dev/tool/bundle.js configure sanity-ws --enable=*
node ../dev/tool/bundle.js configure sanity-ws --list

View File

@ -9,7 +9,7 @@ docker-compose -p sanity up -d --force-recreate --renew-anon-volumes
# Create workspace record in accounts
./tool.sh create-workspace sanity-ws -o SanityTest
# Create user record in accounts
./tool.sh create-account user1 -f John -l Appleseed -p 1234
./tool.sh create-account user1 -p 1234
# Make user the workspace maintainer
./tool.sh set-user-role user1 sanity-ws 1
./tool.sh confirm-email user1

View File

@ -13,7 +13,7 @@ node ../dev/tool/bundle.js backup-restore ./sanity-ws sanity-ws
node ../dev/tool/bundle.js upgrade-workspace sanity-ws
# Re-assign user to workspace.
node ../dev/tool/bundle.js assign-workspace user1 sanity-ws
node ../dev/tool/bundle.js assign-workspace user1 sanity-ws -f John -l Appleseed
node ../dev/tool/bundle.js configure sanity-ws --enable=*
node ../dev/tool/bundle.js configure sanity-ws --list

View File

@ -6,7 +6,7 @@
./tool.sh upgrade-workspace sanity-ws
# Re-assign user to workspace.
./tool.sh assign-workspace user1 sanity-ws
./tool.sh assign-workspace user1 sanity-ws -f John -l Appleseed
./tool.sh configure sanity-ws --enable=*
./tool.sh configure sanity-ws --list