diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 42dca937c6..d5382bb645 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -243,6 +243,7 @@ jobs: docker logs $(docker ps | grep transactor | cut -f 1 -d ' ') > logs/transactor.log docker logs $(docker ps | grep account | cut -f 1 -d ' ') > logs/account.log docker logs $(docker ps | grep front | cut -f 1 -d ' ') > logs/front.log + docker logs $(docker ps | grep collaborator | cut -f 1 -d ' ') > logs/collaborator.log - name: Upload test results if: always() uses: actions/upload-artifact@v4 @@ -438,6 +439,7 @@ jobs: docker logs $(docker ps | grep transactor | cut -f 1 -d ' ') > logs/uweb-transactor.log docker logs $(docker ps | grep account | cut -f 1 -d ' ') > logs/uweb-account.log docker logs $(docker ps | grep front | cut -f 1 -d ' ') > logs/uweb-front.log + docker logs $(docker ps | grep collaborator | cut -f 1 -d ' ') > logs/uweb-collaborator.log - name: Upload test results if: always() uses: actions/upload-artifact@v4 diff --git a/.vscode/launch.json b/.vscode/launch.json index 6a14f43c43..5ee24a15d5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -43,7 +43,6 @@ "SERVER_SECRET": "secret", "ENABLE_CONSOLE": "true", "COLLABORATOR_URL": "ws://localhost:3078", - "COLLABORATOR_API_URL": "http://localhost:3078", "REKONI_URL": "http://localhost:4004", "FRONT_URL": "http://localhost:8080", "ACCOUNTS_URL": "http://localhost:3000", @@ -104,7 +103,6 @@ "UPLOAD_URL": "/files", "SERVER_PORT": "8087", "COLLABORATOR_URL": "ws://localhost:3078", - "COLLABORATOR_API_URL": "http://localhost:3078", "CALENDAR_URL": "http://localhost:8095", "GMAIL_URL": "http://localhost:8088", "TELEGRAM_URL": "http://localhost:8086", @@ -239,7 +237,7 @@ "CLIENT_ID": "${env:POD_GITHUB_CLIENTID}", "CLIENT_SECRET": "${env:POD_GITHUB_CLIENT_SECRET}", "PRIVATE_KEY": "${env:POD_GITHUB_PRIVATE_KEY}", - "COLLABORATOR_API_URL": "http://localhost:3078", + "COLLABORATOR_URL": "ws://localhost:3078", "SYSTEM_EMAIL": "anticrm@hc.engineering", "MINIO_ENDPOINT": "localhost", "MINIO_ACCESS_KEY": "minioadmin", @@ -280,7 +278,7 @@ "env": { "SERVER_SECRET": "secret", "ACCOUNTS_URL": "http://localhost:3000", - "COLLABORATOR_API_URL": "http://localhost:3078", + "COLLABORATOR_URL": "ws://localhost:3078", "STORAGE_CONFIG": "minio|localhost?accessKey=minioadmin&secretKey=minioadmin", "MONGO_URL": "mongodb://localhost:27017" }, diff --git a/desktop/src/ui/platform.ts b/desktop/src/ui/platform.ts index 82b14aa378..d151e0f028 100644 --- a/desktop/src/ui/platform.ts +++ b/desktop/src/ui/platform.ts @@ -203,7 +203,6 @@ export async function configurePlatform (): Promise { setMetadata(presentation.metadata.UploadURL, config.UPLOAD_URL) setMetadata(presentation.metadata.FilesURL, config.FILES_URL) setMetadata(presentation.metadata.CollaboratorUrl, config.COLLABORATOR_URL) - setMetadata(presentation.metadata.CollaboratorApiUrl, config.COLLABORATOR_API_URL) setMetadata(presentation.metadata.PreviewConfig, parsePreviewConfig(config.PREVIEW_CONFIG)) setMetadata(presentation.metadata.FrontUrl, config.FRONT_URL) diff --git a/desktop/src/ui/types.ts b/desktop/src/ui/types.ts index 786fd14acd..e6e896ffc4 100644 --- a/desktop/src/ui/types.ts +++ b/desktop/src/ui/types.ts @@ -6,7 +6,6 @@ import { ScreenSource } from '@hcengineering/love' export interface Config { ACCOUNTS_URL: string COLLABORATOR_URL: string - COLLABORATOR_API_URL: string FRONT_URL: string FILES_URL: string UPLOAD_URL: string diff --git a/dev/doc-import-tool/package.json b/dev/doc-import-tool/package.json index 393c7d4b1e..aa81d7c8b6 100644 --- a/dev/doc-import-tool/package.json +++ b/dev/doc-import-tool/package.json @@ -15,7 +15,7 @@ "build:watch": "compile", "_phase:bundle": "rushx bundle", "bundle": "mkdir -p bundle && node esbuild.js", - "run-local": "cross-env SERVER_SECRET=secret MONGO_URL=mongodb://localhost:27017 COLLABORATOR_URL=ws://localhost:3078 COLLABORATOR_API_URL=http://localhost:3078 STORAGE_CONFIG=minio|minio?accessKey=minioadmin&secretKey=minioadmin PRODUCT_ID=ezqms node --nolazy -r ts-node/register ./src/__start.ts", + "run-local": "cross-env SERVER_SECRET=secret MONGO_URL=mongodb://localhost:27017 COLLABORATOR_URL=ws://localhost:3078 STORAGE_CONFIG=minio|minio?accessKey=minioadmin&secretKey=minioadmin PRODUCT_ID=ezqms node --nolazy -r ts-node/register ./src/__start.ts", "run": "cross-env node -r ts-node/register --max-old-space-size=8000 ./src/__start.ts", "format": "format src", "test": "jest --passWithNoTests --silent", diff --git a/dev/doc-import-tool/src/config.ts b/dev/doc-import-tool/src/config.ts index 3e826ff1fc..1503c73eb5 100644 --- a/dev/doc-import-tool/src/config.ts +++ b/dev/doc-import-tool/src/config.ts @@ -8,7 +8,7 @@ import { HtmlConversionBackend } from './convert/convert' export interface Config { doc: string token: string - collaboratorApiURL: string + collaboratorURL: string uploadURL: string workspaceId: WorkspaceId owner: Ref diff --git a/dev/doc-import-tool/src/import.ts b/dev/doc-import-tool/src/import.ts index 0d2569cd7f..1db7697f53 100644 --- a/dev/doc-import-tool/src/import.ts +++ b/dev/doc-import-tool/src/import.ts @@ -21,7 +21,7 @@ import core, { Ref, TxOperations, generateId, - getCollaborativeDoc, + makeCollaborativeDoc, systemAccountEmail, type Blob } from '@hcengineering/core' @@ -100,7 +100,7 @@ async function createDocument ( abstract: '', effectiveDate: 0, reviewInterval: DEFAULT_PERIODIC_REVIEW_INTERVAL, - content: getCollaborativeDoc(generateId()), + content: makeCollaborativeDoc(generateId()), snapshots: 0, plannedEffectiveDate: 0 } @@ -168,7 +168,7 @@ async function createTemplateIfNotExist ( approvers: [], coAuthors: [], changeControl: ccRecordId, - content: getCollaborativeDoc(generateId()), + content: makeCollaborativeDoc(generateId()), snapshots: 0, plannedEffectiveDate: 0 } @@ -208,8 +208,8 @@ async function createSections ( throw new Error(`Invalid document: ${JSON.stringify(doc)}`) } - const { collaboratorApiURL, token, workspaceId } = config - const collaborator = getCollaboratorClient(txops.getHierarchy(), workspaceId, token, collaboratorApiURL) + const { collaboratorURL, token, workspaceId } = config + const collaborator = getCollaboratorClient(workspaceId, token, collaboratorURL) console.log('Creating document content') @@ -229,7 +229,7 @@ async function createSections ( content += `

${section.title}

${section.content}` } - await collaborator.updateContent(collabId, 'content', content) + await collaborator.updateContent(collabId, { content }) } finally { // do nothing } diff --git a/dev/doc-import-tool/src/index.ts b/dev/doc-import-tool/src/index.ts index 27526f93f6..e90c941049 100644 --- a/dev/doc-import-tool/src/index.ts +++ b/dev/doc-import-tool/src/index.ts @@ -44,8 +44,8 @@ export function docImportTool (): void { process.exit(1) } - const collaboratorApiUrl = process.env.COLLABORATOR_API_URL - if (collaboratorApiUrl === undefined) { + const collaboratorUrl = process.env.COLLABORATOR_URL + if (collaboratorUrl === undefined) { console.error('please provide collaborator url') process.exit(1) } @@ -104,7 +104,7 @@ export function docImportTool (): void { space: cmd.space, uploadURL: uploadUrl, storageAdapter, - collaboratorApiURL: collaboratorApiUrl, + collaboratorURL: collaboratorUrl, token: generateToken(systemAccountEmail, workspaceId) } diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index 4e1b6a1f9c..33fac4a2a1 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -106,7 +106,6 @@ services: - TELEGRAM_URL=http://localhost:8086 - REKONI_URL=http://localhost:4004 - COLLABORATOR_URL=ws://localhost:3078 - - COLLABORATOR_API_URL=http://localhost:3078 - STORAGE_CONFIG=${STORAGE_CONFIG} - GITHUB_URL=http://localhost:3500 - PRINT_URL=http://localhost:4005 diff --git a/dev/local-mongo/docker-compose.yaml b/dev/local-mongo/docker-compose.yaml index 67a475b083..eb2ee0d2c0 100644 --- a/dev/local-mongo/docker-compose.yaml +++ b/dev/local-mongo/docker-compose.yaml @@ -93,7 +93,6 @@ services: - TELEGRAM_URL=http://localhost:8086 - REKONI_URL=http://localhost:4004 - COLLABORATOR_URL=ws://localhost:3078 - - COLLABORATOR_API_URL=http://localhost:3078 - STORAGE_CONFIG=${STORAGE_CONFIG} - GITHUB_URL=http://localhost:3500 - PRINT_URL=http://localhost:4005 diff --git a/dev/prod/.env b/dev/prod/.env index ce9bafd5d0..cbfc478199 100644 --- a/dev/prod/.env +++ b/dev/prod/.env @@ -5,9 +5,8 @@ FRONT_URL=http://localhost:8080 REKONI_URL=http://localhost:4004 COLLABORATOR_URL=ws://locahost:3078 -COLLABORATOR_API_URL=http://locahost:3078 PRINT_URL=http://localhost:4005 SIGN_URL=http://localhost:4006 -ANALYTICS_COLLECTOR_URL=http://localhost:4007 \ No newline at end of file +ANALYTICS_COLLECTOR_URL=http://localhost:4007 diff --git a/dev/prod/config.json b/dev/prod/config.json index f292e076a8..a29f38cbea 100644 --- a/dev/prod/config.json +++ b/dev/prod/config.json @@ -1,7 +1,6 @@ { "ACCOUNTS_URL":"http://localhost:3000", "COLLABORATOR_URL": "ws://localhost:3078", - "COLLABORATOR_API_URL": "http://localhost:3078", "UPLOAD_URL":"/files", "REKONI_URL": "http://localhost:4004", "PRINT_URL": "http://localhost:4005", diff --git a/dev/prod/public/config-dev.json b/dev/prod/public/config-dev.json index a1af7ca5fb..842ed6d3b8 100644 --- a/dev/prod/public/config-dev.json +++ b/dev/prod/public/config-dev.json @@ -6,6 +6,5 @@ "GMAIL_URL": "https://gmail.hc.engineering", "CALENDAR_URL": "https://calendar.hc.engineering", "REKONI_URL": "https://rekoni.hc.engineering", - "COLLABORATOR_URL": "wss://collaborator.hc.engineering", - "COLLABORATOR_API_URL": "https://collaborator.hc.engineering" + "COLLABORATOR_URL": "wss://collaborator.hc.engineering" } \ No newline at end of file diff --git a/dev/prod/public/config.json b/dev/prod/public/config.json index 46d9f904a3..b448809f77 100644 --- a/dev/prod/public/config.json +++ b/dev/prod/public/config.json @@ -1,7 +1,6 @@ { "ACCOUNTS_URL":"/account", "COLLABORATOR_URL": "ws://localhost:3078", - "COLLABORATOR_API_URL": "http://localhost:3078", "UPLOAD_URL":"/files", "TELEGRAM_URL": "http://localhost:8086", "GMAIL_URL": "http://localhost:8088", diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 83a37b9967..3d238cd38d 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -124,7 +124,6 @@ export interface Config { MODEL_VERSION: string VERSION: string COLLABORATOR_URL: string - COLLABORATOR_API_URL: string REKONI_URL: string TELEGRAM_URL: string GMAIL_URL: string @@ -288,7 +287,6 @@ export async function configurePlatform() { setMetadata(presentation.metadata.FilesURL, config.FILES_URL) setMetadata(presentation.metadata.UploadURL, config.UPLOAD_URL) setMetadata(presentation.metadata.CollaboratorUrl, config.COLLABORATOR_URL) - setMetadata(presentation.metadata.CollaboratorApiUrl, config.COLLABORATOR_API_URL) setMetadata(presentation.metadata.FrontUrl, config.FRONT_URL) setMetadata(presentation.metadata.PreviewConfig, parsePreviewConfig(config.PREVIEW_CONFIG)) diff --git a/dev/tool/src/clean.ts b/dev/tool/src/clean.ts index 20cca7c2b5..afd0d8d72f 100644 --- a/dev/tool/src/clean.ts +++ b/dev/tool/src/clean.ts @@ -1214,10 +1214,6 @@ async function updateId ( const markup = (contentDoc as any)[attrName] as Markup const newMarkup = markup.replaceAll(doc._id, newId) await update(h, db, contentDoc, { [attrName]: newMarkup }) - } else if (attr.type._class === core.class.TypeCollaborativeMarkup) { - const markup = (contentDoc as any)[attrName] - const newMarkup = markup.replaceAll(doc._id, newId) - await update(h, db, contentDoc, { [attrName]: newMarkup }) } else if (attr.type._class === core.class.TypeCollaborativeDoc) { const collaborativeDoc = (contentDoc as any)[attr.name] as CollaborativeDoc await updateYDoc(ctx, collaborativeDoc, storage, workspaceId, contentDoc, newId, doc) diff --git a/dev/tool/src/markup.ts b/dev/tool/src/markup.ts index f5b494cc85..da3cdb9d75 100644 --- a/dev/tool/src/markup.ts +++ b/dev/tool/src/markup.ts @@ -7,7 +7,7 @@ import core, { type MeasureContext, type Ref, type WorkspaceId, - getCollaborativeDocId + makeCollaborativeDoc } from '@hcengineering/core' import { getMongoClient, getWorkspaceDB } from '@hcengineering/mongo' import { type StorageAdapter } from '@hcengineering/server-core' @@ -39,10 +39,7 @@ export async function fixJsonMarkup ( const attributes = hierarchy.getAllAttributes(_class) const filtered = Array.from(attributes.values()).filter((attribute) => { - return ( - hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) || - hierarchy.isDerived(attribute.type._class, core.class.TypeCollaborativeMarkup) - ) + return hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) }) if (filtered.length === 0) continue @@ -88,7 +85,7 @@ async function processFixJsonMarkupFor ( } if (res !== value) { update[attribute.name] = res - remove.push(getCollaborativeDocId(doc._id, attribute.name)) + remove.push(makeCollaborativeDoc(doc._id, attribute.name)) } } } catch {} diff --git a/models/activity/src/migration.ts b/models/activity/src/migration.ts index 7d4b040834..374ea40215 100644 --- a/models/activity/src/migration.ts +++ b/models/activity/src/migration.ts @@ -46,9 +46,7 @@ async function migrateMarkup (client: MigrationClient): Promise { DOMAIN_ACTIVITY, { _class: activity.class.DocUpdateMessage, - 'attributeUpdates.attrClass': { - $in: [core.class.TypeMarkup, core.class.TypeCollaborativeMarkup] - } + 'attributeUpdates.attrClass': core.class.TypeMarkup }, { projection: { diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts index d3dcacdc10..b85a8cd7b0 100644 --- a/models/contact/src/index.ts +++ b/models/contact/src/index.ts @@ -39,8 +39,8 @@ import { IndexKind, type Blob, type Class, + type CollaborativeDoc, type Domain, - type Markup, type Ref, type Timestamp } from '@hcengineering/core' @@ -54,7 +54,7 @@ import { ReadOnly, TypeBlob, TypeBoolean, - TypeCollaborativeMarkup, + TypeCollaborativeDoc, TypeDate, TypeRecord, TypeRef, @@ -173,9 +173,9 @@ export class TMember extends TAttachedDoc implements Member { @Model(contact.class.Organization, contact.class.Contact) @UX(contact.string.Organization, contact.icon.Company, 'ORG', 'name', undefined, contact.string.Organizations) export class TOrganization extends TContact implements Organization { - @Prop(TypeCollaborativeMarkup(), core.string.Description) + @Prop(TypeCollaborativeDoc(), core.string.Description) @Index(IndexKind.FullText) - description?: Markup + description!: CollaborativeDoc @Prop(Collection(contact.class.Member), contact.string.Members) members!: number diff --git a/models/controlled-documents/src/migration.ts b/models/controlled-documents/src/migration.ts index 861ad75aaf..0672f170cb 100644 --- a/models/controlled-documents/src/migration.ts +++ b/models/controlled-documents/src/migration.ts @@ -8,7 +8,7 @@ import { TxOperations, generateId, DOMAIN_TX, - getCollaborativeDoc, + makeCollaborativeDoc, MeasureMetricsContext, type Class, type Doc, @@ -143,7 +143,7 @@ async function createProductChangeControlTemplate (tx: TxOperations): Promise { diff --git a/models/core/src/index.ts b/models/core/src/index.ts index adf3af9eaf..cad0f0b54b 100644 --- a/models/core/src/index.ts +++ b/models/core/src/index.ts @@ -61,7 +61,6 @@ import { TTypeBoolean, TTypeCollaborativeDoc, TTypeCollaborativeDocVersion, - TTypeCollaborativeMarkup, TTypeDate, TTypeFileSize, TTypeHyperlink, @@ -141,7 +140,6 @@ export function createModel (builder: Builder): void { TTypeMarkup, TTypeCollaborativeDoc, TTypeCollaborativeDocVersion, - TTypeCollaborativeMarkup, TArrOf, TRefTo, TTypeDate, diff --git a/models/core/src/migration.ts b/models/core/src/migration.ts index 0844c55c33..769dab9a69 100644 --- a/models/core/src/migration.ts +++ b/models/core/src/migration.ts @@ -13,16 +13,23 @@ // limitations under the License. // +import { saveCollaborativeDoc, takeCollaborativeDocSnapshot } from '@hcengineering/collaboration' import core, { DOMAIN_BLOB, DOMAIN_DOC_INDEX_STATE, DOMAIN_STATUS, DOMAIN_TX, MeasureMetricsContext, + collaborativeDocParse, coreId, generateId, isClassIndexable, + makeCollaborativeDoc, + type AnyAttribute, type Blob, + type Doc, + type Domain, + type MeasureContext, type Ref, type Space, type Status, @@ -33,10 +40,14 @@ import { tryMigrate, tryUpgrade, type MigrateOperation, + type MigrateUpdate, type MigrationClient, + type MigrationDocumentQuery, + type MigrationIterator, type MigrationUpgradeClient } from '@hcengineering/model' -import { type StorageAdapterEx } from '@hcengineering/storage' +import { type StorageAdapter, type StorageAdapterEx } from '@hcengineering/storage' +import { markupToYDoc } from '@hcengineering/text' import { DOMAIN_SPACE } from './security' async function migrateStatusesToModel (client: MigrationClient): Promise { @@ -143,6 +154,101 @@ async function migrateStatusTransactions (client: MigrationClient): Promise { + const ctx = new MeasureMetricsContext('migrate_content', {}) + const storageAdapter = client.storageAdapter + + const hierarchy = client.hierarchy + const classes = hierarchy.getDescendants(core.class.Doc) + for (const _class of classes) { + const domain = hierarchy.findDomain(_class) + if (domain === undefined) continue + + const attributes = hierarchy.getAllAttributes(_class) + const filtered = Array.from(attributes.values()).filter((attribute) => { + return hierarchy.isDerived(attribute.type._class, core.class.TypeCollaborativeDoc) + }) + if (filtered.length === 0) continue + + const iterator = await client.traverse(domain, { _class }) + try { + console.log('processing', _class) + await processMigrateContentFor(ctx, domain, filtered, client, storageAdapter, iterator) + } finally { + await iterator.close() + } + } +} + +async function processMigrateContentFor ( + ctx: MeasureContext, + domain: Domain, + attributes: AnyAttribute[], + client: MigrationClient, + storageAdapter: StorageAdapter, + iterator: MigrationIterator +): Promise { + let processed = 0 + while (true) { + const docs = await iterator.next(1000) + if (docs === null || docs.length === 0) { + break + } + + const timestamp = Date.now() + const revisionId = `${timestamp}` + + const operations: { filter: MigrationDocumentQuery, update: MigrateUpdate }[] = [] + + for (const doc of docs) { + const update: MigrateUpdate = {} + + for (const attribute of attributes) { + const collaborativeDoc = makeCollaborativeDoc(doc._id, attribute.name, revisionId) + + const value = (doc as any)[attribute.name] as string + if (value != null && value.startsWith('{')) { + const { documentId } = collaborativeDocParse(collaborativeDoc) + const blob = await storageAdapter.stat(ctx, client.workspaceId, documentId) + // only for documents not in storage + if (blob === undefined) { + const ydoc = markupToYDoc(value, attribute.name) + await saveCollaborativeDoc(storageAdapter, client.workspaceId, collaborativeDoc, ydoc, ctx) + await takeCollaborativeDocSnapshot( + storageAdapter, + client.workspaceId, + collaborativeDoc, + ydoc, + { + versionId: revisionId, + name: 'Migration to storage', + createdBy: core.account.System, + createdOn: Date.now() + }, + ctx + ) + } + + update[attribute.name] = collaborativeDoc + } else if (value == null) { + update[attribute.name] = makeCollaborativeDoc(doc._id, attribute.name, revisionId) + } + } + + if (Object.keys(update).length > 0) { + operations.push({ filter: { _id: doc._id }, update }) + } + } + + if (operations.length > 0) { + await client.bulk(domain, operations) + } + + processed += docs.length + console.log('...processed', processed) + } +} + export const coreOperation: MigrateOperation = { async migrate (client: MigrationClient): Promise { // We need to delete all documents in doc index state for missing classes @@ -189,6 +295,10 @@ export const coreOperation: MigrateOperation = { func: async (client: MigrationClient) => { await client.update(DOMAIN_DOC_INDEX_STATE, {}, { $set: { needIndex: true } }) } + }, + { + state: 'collaborative-content-to-storage', + func: migrateCollaborativeContentToStorage } ]) }, diff --git a/models/document/src/migration.ts b/models/document/src/migration.ts index b0d9a91980..89eeb311b8 100644 --- a/models/document/src/migration.ts +++ b/models/document/src/migration.ts @@ -13,15 +13,7 @@ // limitations under the License. // -import { type Attachment } from '@hcengineering/attachment' -import { - DOMAIN_TX, - getCollaborativeDoc, - MeasureMetricsContext, - type Class, - type Doc, - type Ref -} from '@hcengineering/core' +import { DOMAIN_TX, MeasureMetricsContext } from '@hcengineering/core' import { type Document, type Teamspace } from '@hcengineering/document' import { tryMigrate, @@ -29,126 +21,12 @@ import { type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model' -import { DOMAIN_ATTACHMENT } from '@hcengineering/model-attachment' import core, { DOMAIN_SPACE } from '@hcengineering/model-core' import { type Asset } from '@hcengineering/platform' import document, { documentId, DOMAIN_DOCUMENT } from './index' import { loadCollaborativeDoc, saveCollaborativeDoc, yDocCopyXmlField } from '@hcengineering/collaboration' -async function migrateCollaborativeContent (client: MigrationClient): Promise { - const attachedFiles = await client.find(DOMAIN_ATTACHMENT, { - _class: 'document:class:CollaboratorDocument' as Ref>, - attachedToClass: document.class.Document - }) - - for (const attachment of attachedFiles) { - const collaborativeDoc = getCollaborativeDoc(attachment._id) - - await client.update( - DOMAIN_DOCUMENT, - { - _id: attachment.attachedTo, - _class: attachment.attachedToClass, - content: { - $exists: false - } - }, - { - $set: { - content: collaborativeDoc - } - } - ) - } - - // delete snapshots in old format - await client.deleteMany(DOMAIN_DOCUMENT, { - _class: 'document:class:DocumentSnapshot' as Ref>, - contentId: { $exists: true } - }) - - await client.update( - DOMAIN_DOCUMENT, - { - _class: document.class.Document, - snapshots: { $gt: 0 } - }, - { - $set: { - snapshots: 0 - } - } - ) - - // delete old snapshot transactions - await client.deleteMany(DOMAIN_TX, { - _class: core.class.TxCollectionCUD, - objectClass: document.class.Document, - collection: 'snapshots' - }) -} - -async function fixCollaborativeContentId (client: MigrationClient): Promise { - const documents = await client.find(DOMAIN_DOCUMENT, { - content: { $exists: true } - }) - - // there was a wrong migration that assigned incorrect collaborative doc id - for (const document of documents) { - if (!document.content.includes(':')) { - await client.update(DOMAIN_DOCUMENT, { _id: document._id }, { content: getCollaborativeDoc(document.content) }) - } - } -} - -async function migrateWrongDomainContent (client: MigrationClient): Promise { - // migrate content saved into wrong domain - const attachedFiles = await client.find(DOMAIN_DOCUMENT, { - _class: 'document:class:CollaboratorDocument' as Ref>, - attachedToClass: document.class.Document - }) - - for (const attachment of attachedFiles) { - const collaborativeDoc = getCollaborativeDoc(attachment._id) - - await client.update( - DOMAIN_DOCUMENT, - { - _id: attachment.attachedTo, - _class: attachment.attachedToClass, - content: { - $exists: false - } - }, - { - $set: { - content: collaborativeDoc - } - } - ) - } - - await client.move( - DOMAIN_DOCUMENT, - { - _class: 'document:class:CollaboratorDocument' as Ref>, - attachedToClass: document.class.Document - }, - DOMAIN_ATTACHMENT - ) -} - -async function migrateDeleteCollaboratorDocument (client: MigrationClient): Promise { - await client.deleteMany(DOMAIN_ATTACHMENT, { _class: 'document:class:CollaboratorDocument' as Ref> }) - await client.deleteMany(DOMAIN_DOCUMENT, { _class: 'document:class:CollaboratorDocument' as Ref> }) - await client.deleteMany(DOMAIN_TX, { - _class: core.class.TxCollectionCUD, - collection: 'attachments', - 'tx.objectClass': 'document:class:CollaboratorDocument' as Ref> - }) -} - async function migrateDocumentIcons (client: MigrationClient): Promise { await client.update( DOMAIN_SPACE, @@ -173,60 +51,6 @@ async function migrateDocumentIcons (client: MigrationClient): Promise { ) } -async function setNoParent (client: MigrationClient): Promise { - await client.update( - DOMAIN_DOCUMENT, - { - _class: document.class.Document, - attachedTo: { $exists: false } - }, - { - $set: { - attachedTo: document.ids.NoParent, - attachedToClass: document.class.Document - } - } - ) - const docsWithParent = (await client.find(DOMAIN_DOCUMENT, { - _class: document.class.Document, - attachedTo: { $exists: true, $ne: document.ids.NoParent } - })) as Document[] - for (const doc of docsWithParent) { - const parent = await client.find(DOMAIN_DOCUMENT, { - _class: document.class.Document, - _id: doc.attachedTo - }) - if (parent.length === 0) continue - if (parent[0].space !== doc.space) { - await client.update( - DOMAIN_DOCUMENT, - { - _class: document.class.Document, - _id: doc._id - }, - { - $set: { - attachedTo: document.ids.NoParent - } - } - ) - } - } - - await client.update( - DOMAIN_DOCUMENT, - { - _class: document.class.Document, - attachedTo: '' - }, - { - $set: { - attachedTo: document.ids.NoParent - } - } - ) -} - async function migrateTeamspaces (client: MigrationClient): Promise { await client.update( DOMAIN_SPACE, @@ -306,28 +130,6 @@ async function migrateContentField (client: MigrationClient): Promise { export const documentOperation: MigrateOperation = { async migrate (client: MigrationClient): Promise { await tryMigrate(client, documentId, [ - { - state: 'migrate-no-parent', - func: async (client) => { - await setNoParent(client) - } - }, - { - state: 'collaborativeContent', - func: migrateCollaborativeContent - }, - { - state: 'fixCollaborativeContentId', - func: fixCollaborativeContentId - }, - { - state: 'wrongDomainContent', - func: migrateWrongDomainContent - }, - { - state: 'deleteCollaboratorDocument', - func: migrateDeleteCollaboratorDocument - }, { state: 'updateDocumentIcons', func: migrateDocumentIcons diff --git a/models/lead/src/types.ts b/models/lead/src/types.ts index 53fce71c18..9869970326 100644 --- a/models/lead/src/types.ts +++ b/models/lead/src/types.ts @@ -17,6 +17,7 @@ import type { Employee } from '@hcengineering/contact' import { Account, IndexKind, + type CollaborativeDoc, type Role, type RolesAssignment, type Ref, @@ -31,7 +32,7 @@ import { Model, Prop, ReadOnly, - TypeCollaborativeMarkup, + TypeCollaborativeDoc, TypeDate, TypeMarkup, TypeRef, @@ -94,9 +95,9 @@ export class TCustomer extends TContact implements Customer { @Prop(Collection(lead.class.Lead), lead.string.Leads) leads?: number - @Prop(TypeCollaborativeMarkup(), core.string.Description) + @Prop(TypeCollaborativeDoc(), core.string.Description) @Index(IndexKind.FullText) - description!: string + description!: CollaborativeDoc } @Mixin(lead.mixin.DefaultFunnelTypeData, lead.class.Funnel) diff --git a/models/recruit/src/types.ts b/models/recruit/src/types.ts index 3ed58cb304..cf94bbb76b 100644 --- a/models/recruit/src/types.ts +++ b/models/recruit/src/types.ts @@ -17,6 +17,7 @@ import type { Employee, Organization } from '@hcengineering/contact' import { Account, IndexKind, + type CollaborativeDoc, type Domain, type Markup, type Ref, @@ -34,7 +35,7 @@ import { Prop, ReadOnly, TypeBoolean, - TypeCollaborativeMarkup, + TypeCollaborativeDoc, TypeDate, TypeMarkup, TypeRef, @@ -63,9 +64,9 @@ import recruit from './plugin' @Model(recruit.class.Vacancy, task.class.Project) @UX(recruit.string.Vacancy, recruit.icon.Vacancy, 'VCN', 'name', undefined, recruit.string.Vacancies) export class TVacancy extends TProject implements Vacancy { - @Prop(TypeCollaborativeMarkup(), recruit.string.FullDescription) + @Prop(TypeCollaborativeDoc(), recruit.string.FullDescription) @Index(IndexKind.FullText) - fullDescription?: string + fullDescription!: CollaborativeDoc @Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files }) attachments?: number diff --git a/models/text-editor/src/migration.ts b/models/text-editor/src/migration.ts index f999d0910a..f4505ac6c4 100644 --- a/models/text-editor/src/migration.ts +++ b/models/text-editor/src/migration.ts @@ -34,10 +34,7 @@ async function migrateMarkup (client: MigrationClient): Promise { const attributes = hierarchy.getAllAttributes(_class) const filtered = Array.from(attributes.values()).filter((attribute) => { - return ( - hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) || - hierarchy.isDerived(attribute.type._class, core.class.TypeCollaborativeMarkup) - ) + return hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) }) if (filtered.length === 0) continue @@ -106,10 +103,7 @@ async function fixMigrateMarkup (client: MigrationClient): Promise { const attributes = hierarchy.getAllAttributes(_class) const filtered = Array.from(attributes.values()).filter((attribute) => { - return ( - hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) || - hierarchy.isDerived(attribute.type._class, core.class.TypeCollaborativeMarkup) - ) + return hierarchy.isDerived(attribute.type._class, core.class.TypeMarkup) }) if (filtered.length === 0) continue diff --git a/models/tracker/src/types.ts b/models/tracker/src/types.ts index 4cbeb7dbdd..70cadb279e 100644 --- a/models/tracker/src/types.ts +++ b/models/tracker/src/types.ts @@ -18,8 +18,9 @@ import contact, { type Employee, type Person } from '@hcengineering/contact' import { DOMAIN_MODEL, DateRangeMode, - type Domain, IndexKind, + type CollaborativeDoc, + type Domain, type Markup, type Ref, type RelatedDocument, @@ -39,7 +40,7 @@ import { Model, Prop, ReadOnly, - TypeCollaborativeMarkup, + TypeCollaborativeDoc, TypeDate, TypeMarkup, TypeNumber, @@ -182,9 +183,9 @@ export class TIssue extends TTask implements Issue { @Index(IndexKind.FullText) title!: string - @Prop(TypeCollaborativeMarkup(), tracker.string.Description) + @Prop(TypeCollaborativeDoc(), tracker.string.Description) @Index(IndexKind.FullText) - description!: Markup + description!: CollaborativeDoc @Prop(TypeRef(tracker.class.IssueStatus), tracker.string.Status, { _id: tracker.attribute.IssueStatus, @@ -275,7 +276,7 @@ export class TIssueTemplate extends TDoc implements IssueTemplate { @Index(IndexKind.FullText) title!: string - @Prop(TypeCollaborativeMarkup(), tracker.string.Description) + @Prop(TypeMarkup(), tracker.string.Description) @Index(IndexKind.FullText) description!: Markup diff --git a/models/view/src/index.ts b/models/view/src/index.ts index f9aee0018b..e033ee20bd 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -495,17 +495,8 @@ export function createModel (builder: Builder): void { editor: view.component.HTMLEditor }) - classPresenter( - builder, - core.class.TypeCollaborativeMarkup, - view.component.MarkupPresenter, - undefined, - undefined, - view.component.MarkupDiffPresenter - ) - - builder.mixin(core.class.TypeCollaborativeMarkup, core.class.Class, view.mixin.InlineAttributEditor, { - editor: view.component.CollaborativeHTMLEditor + builder.mixin(core.class.TypeCollaborativeDoc, core.class.Class, view.mixin.ActivityAttributePresenter, { + presenter: view.component.MarkupDiffPresenter }) builder.mixin(core.class.TypeCollaborativeDoc, core.class.Class, view.mixin.InlineAttributEditor, { diff --git a/packages/collaborator-client/src/client.ts b/packages/collaborator-client/src/client.ts index 76c845ceac..2ae2f30307 100644 --- a/packages/collaborator-client/src/client.ts +++ b/packages/collaborator-client/src/client.ts @@ -16,33 +16,39 @@ import { Account, CollaborativeDoc, - Hierarchy, Markup, Ref, Timestamp, WorkspaceId, + collaborativeDocWithLastVersion, collaborativeDocWithVersion, concatLink } from '@hcengineering/core' import { DocumentId } from './types' import { formatMinioDocumentId } from './utils' +/** @public */ +export interface DocumentSnapshotParams { + createdBy: Ref + versionId: string + versionName?: string +} + /** @public */ export interface GetContentRequest { documentId: DocumentId - field: string } /** @public */ export interface GetContentResponse { - html: string + content: Record } /** @public */ export interface UpdateContentRequest { documentId: DocumentId - field: string - html: string + content: Record + snapshot?: DocumentSnapshotParams } /** @public */ @@ -54,6 +60,7 @@ export interface CopyContentRequest { documentId: DocumentId sourceField: string targetField: string + snapshot?: DocumentSnapshotParams } /** @public */ @@ -82,8 +89,7 @@ export interface RemoveDocumentResponse {} /** @public */ export interface TakeSnapshotRequest { documentId: DocumentId - createdBy: Ref - snapshotName: string + snapshot: DocumentSnapshotParams } /** @public */ @@ -95,38 +101,36 @@ export interface TakeSnapshotResponse { createdOn: Timestamp } -/** @public */ -export interface CollaborativeDocSnapshotParams { - snapshotName: string - createdBy: Ref -} - /** @public */ export interface CollaboratorClient { // field operations - getContent: (collaborativeDoc: CollaborativeDoc, field: string) => Promise - updateContent: (collaborativeDoc: CollaborativeDoc, field: string, value: Markup) => Promise - copyContent: (collaborativeDoc: CollaborativeDoc, sourceField: string, targetField: string) => Promise + getContent: (collaborativeDoc: CollaborativeDoc) => Promise> + updateContent: ( + document: CollaborativeDoc, + content: Record, + snapshot?: DocumentSnapshotParams + ) => Promise + copyContent: ( + document: CollaborativeDoc, + sourceField: string, + targetField: string, + snapshot?: DocumentSnapshotParams + ) => Promise // document operations branch: (source: CollaborativeDoc, target: CollaborativeDoc) => Promise remove: (collaborativeDoc: CollaborativeDoc) => Promise - snapshot: (collaborativeDoc: CollaborativeDoc, params: CollaborativeDocSnapshotParams) => Promise + snapshot: (collaborativeDoc: CollaborativeDoc, params: DocumentSnapshotParams) => Promise } /** @public */ -export function getClient ( - hierarchy: Hierarchy, - workspaceId: WorkspaceId, - token: string, - collaboratorUrl: string -): CollaboratorClient { - return new CollaboratorClientImpl(hierarchy, workspaceId, token, collaboratorUrl) +export function getClient (workspaceId: WorkspaceId, token: string, collaboratorUrl: string): CollaboratorClient { + const url = collaboratorUrl.replaceAll('wss://', 'https://').replace('ws://', 'http://') + return new CollaboratorClientImpl(workspaceId, token, url) } class CollaboratorClientImpl implements CollaboratorClient { constructor ( - private readonly hierarchy: Hierarchy, private readonly workspace: WorkspaceId, private readonly token: string, private readonly collaboratorUrl: string @@ -153,30 +157,43 @@ class CollaboratorClientImpl implements CollaboratorClient { return result } - async getContent (document: CollaborativeDoc, field: string): Promise { + async getContent (document: CollaborativeDoc): Promise> { const workspace = this.workspace.name const documentId = formatMinioDocumentId(workspace, document) - const payload: GetContentRequest = { documentId, field } + const payload: GetContentRequest = { documentId } const res = (await this.rpc('getContent', payload)) as GetContentResponse - return res.html ?? '' + return res.content ?? {} } - async updateContent (document: CollaborativeDoc, field: string, value: Markup): Promise { + async updateContent ( + document: CollaborativeDoc, + content: Record, + snapshot?: DocumentSnapshotParams + ): Promise { const workspace = this.workspace.name const documentId = formatMinioDocumentId(workspace, document) - const payload: UpdateContentRequest = { documentId, field, html: value } + const payload: UpdateContentRequest = { documentId, content, snapshot } await this.rpc('updateContent', payload) + + return snapshot !== undefined ? collaborativeDocWithLastVersion(document, snapshot.versionId) : document } - async copyContent (document: CollaborativeDoc, sourceField: string, targetField: string): Promise { + async copyContent ( + document: CollaborativeDoc, + sourceField: string, + targetField: string, + snapshot?: DocumentSnapshotParams + ): Promise { const workspace = this.workspace.name const documentId = formatMinioDocumentId(workspace, document) - const payload: CopyContentRequest = { documentId, sourceField, targetField } + const payload: CopyContentRequest = { documentId, sourceField, targetField, snapshot } await this.rpc('copyContent', payload) + + return snapshot !== undefined ? collaborativeDocWithLastVersion(document, snapshot.versionId) : document } async branch (source: CollaborativeDoc, target: CollaborativeDoc): Promise { @@ -198,11 +215,11 @@ class CollaboratorClientImpl implements CollaboratorClient { await this.rpc('removeDocument', payload) } - async snapshot (document: CollaborativeDoc, params: CollaborativeDocSnapshotParams): Promise { + async snapshot (document: CollaborativeDoc, snapshot: DocumentSnapshotParams): Promise { const workspace = this.workspace.name const documentId = formatMinioDocumentId(workspace, document) - const payload: TakeSnapshotRequest = { documentId, ...params } + const payload: TakeSnapshotRequest = { documentId, snapshot } const res = (await this.rpc('takeSnapshot', payload)) as TakeSnapshotResponse return collaborativeDocWithVersion(document, res.versionId) diff --git a/packages/collaborator-client/src/utils.ts b/packages/collaborator-client/src/utils.ts index 18886de3ea..a8b4b27649 100644 --- a/packages/collaborator-client/src/utils.ts +++ b/packages/collaborator-client/src/utils.ts @@ -17,7 +17,6 @@ import { Class, CollaborativeDoc, Doc, - Domain, Ref, collaborativeDocChain, collaborativeDocFormat, @@ -80,24 +79,21 @@ export function parseDocumentId (documentId: DocumentId): { /** @public */ export function formatPlatformDocumentId ( - objectDomain: Domain, objectClass: Ref>, objectId: Ref, objectAttr: string ): PlatformDocumentId { - return `${objectDomain}/${objectClass}/${objectId}/${objectAttr}` as PlatformDocumentId + return `${objectClass}/${objectId}/${objectAttr}` as PlatformDocumentId } /** @public */ export function parsePlatformDocumentId (platformDocumentId: PlatformDocumentId): { - objectDomain: Domain objectClass: Ref> objectId: Ref objectAttr: string } { - const [objectDomain, objectClass, objectId, objectAttr] = platformDocumentId.split('/') + const [objectClass, objectId, objectAttr] = platformDocumentId.split('/') return { - objectDomain: objectDomain as Domain, objectClass: objectClass as Ref>, objectId: objectId as Ref, objectAttr diff --git a/packages/core/src/collaboration.ts b/packages/core/src/collaboration.ts index de6d1c6b61..cda6bd2751 100644 --- a/packages/core/src/collaboration.ts +++ b/packages/core/src/collaboration.ts @@ -44,16 +44,16 @@ export type CollaborativeDocVersion = string | typeof CollaborativeDocVersionHea export const CollaborativeDocVersionHead = 'HEAD' /** @public */ -export function getCollaborativeDocId (objectId: Ref, objectAttr?: string | undefined): string { - return objectAttr !== undefined && objectAttr !== '' ? `${objectId}%${objectAttr}` : `${objectId}` -} - -/** @public */ -export function getCollaborativeDoc (documentId: string): CollaborativeDoc { +export function makeCollaborativeDoc ( + objectId: Ref, + objectAttr?: string | undefined, + versionId?: string | undefined +): CollaborativeDoc { + const storageDocumentId = objectAttr !== undefined && objectAttr !== '' ? `${objectId}%${objectAttr}` : `${objectId}` return collaborativeDocFormat({ - documentId, + documentId: storageDocumentId, versionId: CollaborativeDocVersionHead, - lastVersionId: CollaborativeDocVersionHead + lastVersionId: versionId ?? '0' }) } diff --git a/packages/core/src/component.ts b/packages/core/src/component.ts index 436d77f8c6..d751649ae9 100644 --- a/packages/core/src/component.ts +++ b/packages/core/src/component.ts @@ -34,7 +34,6 @@ import type { Hyperlink, IndexingConfiguration, Interface, - Markup, MigrationState, Obj, Permission, @@ -122,7 +121,6 @@ export default plugin(coreId, { TypeDate: '' as Ref>>, TypeCollaborativeDoc: '' as Ref>>, TypeCollaborativeDocVersion: '' as Ref>>, - TypeCollaborativeMarkup: '' as Ref>>, RefTo: '' as Ref>>, ArrOf: '' as Ref>>, Enum: '' as Ref>, diff --git a/packages/model/src/dsl.ts b/packages/model/src/dsl.ts index cec2247938..430aeaea72 100644 --- a/packages/model/src/dsl.ts +++ b/packages/model/src/dsl.ts @@ -265,21 +265,25 @@ function _generateTx (tx: ClassTxes): Tx[] { [ClassifierKind.INTERFACE]: core.class.Interface, [ClassifierKind.MIXIN]: core.class.Mixin } - const createTx = txFactory.createTxCreateDoc( + const createTx = txFactory.createTxCreateDoc( _cl[tx.kind], core.space.Model, { ...(tx.domain !== undefined ? { domain: tx.domain } : {}), kind: tx.kind, + label: tx.label, + icon: tx.icon, ...(tx.kind === ClassifierKind.INTERFACE ? { extends: tx.implements } : { extends: tx.extends, implements: tx.implements }), - label: tx.label, - icon: tx.icon, - shortLabel: tx.shortLabel, - sortingKey: tx.sortingKey, - filteringKey: tx.filteringKey, - pluralLabel: tx.pluralLabel + ...(tx.kind === ClassifierKind.INTERFACE + ? { extends: tx.implements } + : { + shortLabel: tx.shortLabel, + sortingKey: tx.sortingKey, + filteringKey: tx.filteringKey, + pluralLabel: tx.pluralLabel + }) }, objectId ) @@ -412,13 +416,6 @@ export function TypeMarkup (): Type { return { _class: core.class.TypeMarkup, label: core.string.Markup } } -/** - * @public - */ -export function TypeCollaborativeMarkup (): Type { - return { _class: core.class.TypeCollaborativeMarkup, label: core.string.Collaborative } -} - /** * @public */ diff --git a/packages/presentation/src/collaborator.ts b/packages/presentation/src/collaborator.ts index 8da19a5176..bde927c5a0 100644 --- a/packages/presentation/src/collaborator.ts +++ b/packages/presentation/src/collaborator.ts @@ -13,34 +13,36 @@ // limitations under the License. // -import { type CollaboratorClient, getClient as getCollaborator } from '@hcengineering/collaborator-client' +import { + type CollaboratorClient, + getClient as getCollaborator, + type DocumentSnapshotParams +} from '@hcengineering/collaborator-client' import { type CollaborativeDoc, type Markup, getCurrentAccount, getWorkspaceId } from '@hcengineering/core' import { getMetadata } from '@hcengineering/platform' import { getCurrentLocation } from '@hcengineering/ui' -import { getClient } from '.' import presentation from './plugin' /** @public */ export function getCollaboratorClient (): CollaboratorClient { const workspaceId = getWorkspaceId(getCurrentLocation().path[1] ?? '') - const hierarchy = getClient().getHierarchy() const token = getMetadata(presentation.metadata.Token) ?? '' - const collaboratorURL = getMetadata(presentation.metadata.CollaboratorApiUrl) ?? '' + const collaboratorURL = getMetadata(presentation.metadata.CollaboratorUrl) ?? '' - return getCollaborator(hierarchy, workspaceId, token, collaboratorURL) + return getCollaborator(workspaceId, token, collaboratorURL) } /** @public */ -export async function getMarkup (collaborativeDoc: CollaborativeDoc, field: string): Promise { +export async function getMarkup (collaborativeDoc: CollaborativeDoc): Promise> { const client = getCollaboratorClient() - return await client.getContent(collaborativeDoc, field) + return await client.getContent(collaborativeDoc) } /** @public */ -export async function updateMarkup (collaborativeDoc: CollaborativeDoc, field: string, value: Markup): Promise { +export async function updateMarkup (collaborativeDoc: CollaborativeDoc, content: Record): Promise { const client = getCollaboratorClient() - await client.updateContent(collaborativeDoc, field, value) + await client.updateContent(collaborativeDoc, content) } /** @public */ @@ -60,12 +62,10 @@ export async function copyDocument (source: CollaborativeDoc, target: Collaborat } /** @public */ -export async function takeSnapshot ( - collaborativeDoc: CollaborativeDoc, - snapshotName: string -): Promise { +export async function takeSnapshot (collaborativeDoc: CollaborativeDoc, versionName: string): Promise { const client = getCollaboratorClient() const createdBy = getCurrentAccount()._id - return await client.snapshot(collaborativeDoc, { createdBy, snapshotName }) + const snapshot: DocumentSnapshotParams = { createdBy, versionId: `${Date.now()}`, versionName } + return await client.snapshot(collaborativeDoc, snapshot) } diff --git a/packages/presentation/src/plugin.ts b/packages/presentation/src/plugin.ts index adb7a026d3..1f35e9f890 100644 --- a/packages/presentation/src/plugin.ts +++ b/packages/presentation/src/plugin.ts @@ -132,7 +132,6 @@ export default plugin(presentationId, { UploadURL: '' as Metadata, FilesURL: '' as Metadata, CollaboratorUrl: '' as Metadata, - CollaboratorApiUrl: '' as Metadata, Token: '' as Metadata, Endpoint: '' as Metadata, Workspace: '' as Metadata, diff --git a/packages/presentation/src/utils.ts b/packages/presentation/src/utils.ts index 75c0e03b2a..5224918cb9 100644 --- a/packages/presentation/src/utils.ts +++ b/packages/presentation/src/utils.ts @@ -533,9 +533,6 @@ export function getAttributePresenterClass ( if (hierarchy.isDerived(attrClass, core.class.TypeMarkup)) { category = 'inplace' } - if (hierarchy.isDerived(attrClass, core.class.TypeCollaborativeMarkup)) { - category = 'inplace' - } if (hierarchy.isDerived(attrClass, core.class.TypeCollaborativeDoc)) { category = 'inplace' } diff --git a/packages/text/src/ydoc.ts b/packages/text/src/ydoc.ts index 12e80f2773..f00e6c0b53 100644 --- a/packages/text/src/ydoc.ts +++ b/packages/text/src/ydoc.ts @@ -64,7 +64,7 @@ const defaultSchema = getSchema(defaultExtensions) * @public */ export function yDocToNode (ydoc: YDoc, field?: string, schema?: Schema, extensions?: Extensions): Node { - schema ??= extensions === undefined ? defaultSchema : getSchema(extensions ?? defaultExtensions) + schema ??= getSchema(extensions ?? defaultExtensions) try { const body = yDocToProsemirrorJSON(ydoc, field) diff --git a/plugins/activity-resources/src/activityMessagesUtils.ts b/plugins/activity-resources/src/activityMessagesUtils.ts index 4d503fd869..a13c1caec6 100644 --- a/plugins/activity-resources/src/activityMessagesUtils.ts +++ b/plugins/activity-resources/src/activityMessagesUtils.ts @@ -59,7 +59,6 @@ const valueTypes: ReadonlyArray>> = [ core.class.TypeDate, core.class.TypeFileSize, core.class.TypeMarkup, - core.class.TypeCollaborativeMarkup, core.class.TypeHyperlink ] diff --git a/plugins/activity-resources/src/utils.ts b/plugins/activity-resources/src/utils.ts index cde846e3d5..7a91bb20bb 100644 --- a/plugins/activity-resources/src/utils.ts +++ b/plugins/activity-resources/src/utils.ts @@ -144,7 +144,6 @@ export function getIsTextType (attributeModel?: AttributeModel): boolean { return ( attributeModel.attribute?.type?._class === core.class.TypeMarkup || - attributeModel.attribute?.type?._class === core.class.TypeCollaborativeMarkup || attributeModel.attribute?.type?._class === core.class.TypeCollaborativeDoc ) } diff --git a/plugins/bitrix/src/hr.ts b/plugins/bitrix/src/hr.ts index e1753ffcc2..1ed9d9faa7 100644 --- a/plugins/bitrix/src/hr.ts +++ b/plugins/bitrix/src/hr.ts @@ -1,5 +1,16 @@ import { Organization } from '@hcengineering/contact' -import core, { Account, Client, Data, Doc, Ref, SortingOrder, Status, TxOperations } from '@hcengineering/core' +import core, { + Account, + Client, + Data, + Doc, + Ref, + SortingOrder, + Status, + TxOperations, + generateId, + makeCollaborativeDoc +} from '@hcengineering/core' import recruit, { Applicant, Vacancy } from '@hcengineering/recruit' import task, { ProjectType, makeRank } from '@hcengineering/task' @@ -23,17 +34,25 @@ export async function createVacancy ( const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true) - const id = await client.createDoc(recruit.class.Vacancy, core.space.Space, { - name, - description: type.shortDescription ?? '', - fullDescription: type.description, - private: false, - archived: false, - company, - number: (incResult as any).object.sequence, - members: [], - type: typeId - }) + const id: Ref = generateId() + await client.createDoc( + recruit.class.Vacancy, + core.space.Space, + { + name, + description: type.shortDescription ?? '', + fullDescription: makeCollaborativeDoc(id, 'fullDescription'), + private: false, + archived: false, + company, + number: (incResult as any).object.sequence, + members: [], + type: typeId + }, + id + ) + + // TODO type.description return id } diff --git a/plugins/contact-resources/package.json b/plugins/contact-resources/package.json index 9c5a71487e..23b73e1ff9 100644 --- a/plugins/contact-resources/package.json +++ b/plugins/contact-resources/package.json @@ -52,6 +52,7 @@ "@hcengineering/presentation": "^0.6.3", "@hcengineering/setting": "^0.6.17", "@hcengineering/templates": "^0.6.11", + "@hcengineering/text": "^0.6.5", "@hcengineering/text-editor": "^0.6.0", "@hcengineering/text-editor-resources": "^0.6.0", "@hcengineering/theme": "^0.6.5", diff --git a/plugins/contact-resources/src/components/CreateOrganization.svelte b/plugins/contact-resources/src/components/CreateOrganization.svelte index f53c5b0e22..655635fb39 100644 --- a/plugins/contact-resources/src/components/CreateOrganization.svelte +++ b/plugins/contact-resources/src/components/CreateOrganization.svelte @@ -13,13 +13,22 @@ // limitations under the License. --> @@ -185,12 +198,16 @@ focusIndex={3} /> - +
+ +
, - fullDescription: '', + fullDescription: makeCollaborativeDoc(objectId, 'fullDescription'), location: '', type: typeId as Ref } @@ -103,7 +112,7 @@ $: typeId && templateQ.query(task.class.ProjectType, { _id: typeId }, (result) => { const { _class, _id, description, targetClass, ...templateData } = result[0] - vacancyData = { ...(templateData as unknown as Data), fullDescription: description } + vacancyData = { ...(templateData as unknown as Data) } if (appliedTemplateId !== typeId) { fullDescription = description ?? '' appliedTemplateId = typeId @@ -172,10 +181,11 @@ } const number = (incResult as any).object.sequence + const resId: Ref = generateId() const identifier = `${project?.identifier}-${number}` - const resId = await client.addCollection(tracker.class.Issue, space, parent, tracker.class.Issue, 'subIssues', { + const data: AttachedData = { title: template.title + ` (${name})`, - description: template.description, + description: makeCollaborativeDoc(resId, 'description'), assignee: template.assignee, component: template.component, milestone: template.milestone, @@ -195,7 +205,10 @@ childInfo: [], kind: taskType._id, identifier - }) + } + + await updateMarkup(data.description, { description: template.description }) + await client.addCollection(tracker.class.Issue, space, parent, tracker.class.Issue, 'subIssues', data, resId) if ((template.labels?.length ?? 0) > 0) { const tagElements = await client.findAll(tags.class.TagElement, { _id: { $in: template.labels } }) for (const label of tagElements) { @@ -224,7 +237,7 @@ ...vacancyData, name, description: template?.shortDescription ?? '', - fullDescription, + fullDescription: makeCollaborativeDoc(objectId, 'fullDescription'), private: false, archived: false, number: (incResult as any).object.sequence, @@ -235,6 +248,8 @@ type: typeId } + await updateMarkup(data.fullDescription, { fullDescription }) + const id = await client.createDoc(recruit.class.Vacancy, core.space.Space, data, objectId) Analytics.handleEvent(RecruitEvents.VacancyCreated, { diff --git a/plugins/recruit/src/types.ts b/plugins/recruit/src/types.ts index 471d2d872f..5e8c0a3e78 100644 --- a/plugins/recruit/src/types.ts +++ b/plugins/recruit/src/types.ts @@ -15,13 +15,13 @@ import { Event } from '@hcengineering/calendar' import type { Channel, Organization, Person } from '@hcengineering/contact' -import type { AttachedData, AttachedDoc, Markup, Ref, Status, Timestamp } from '@hcengineering/core' +import type { AttachedData, AttachedDoc, CollaborativeDoc, Markup, Ref, Status, Timestamp } from '@hcengineering/core' import { TagReference } from '@hcengineering/tags' import type { Project, Task } from '@hcengineering/task' /** @public */ export interface Vacancy extends Project { - fullDescription?: string + fullDescription: CollaborativeDoc attachments?: number dueTo?: Timestamp location?: string diff --git a/plugins/text-editor-resources/src/components/CollaborativeAttributeBox.svelte b/plugins/text-editor-resources/src/components/CollaborativeAttributeBox.svelte index adb24249d3..530d504d4f 100644 --- a/plugins/text-editor-resources/src/components/CollaborativeAttributeBox.svelte +++ b/plugins/text-editor-resources/src/components/CollaborativeAttributeBox.svelte @@ -13,7 +13,7 @@ // limitations under the License. -->