mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-23 03:49:49 +00:00
a6fb3713f6
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
584 lines
17 KiB
TypeScript
584 lines
17 KiB
TypeScript
//
|
|
// Copyright © 2022, 2023, 2024 Hardcore Engineering Inc.
|
|
//
|
|
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License. You may
|
|
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
//
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
import activity from '@hcengineering/activity'
|
|
import type { Class, CollectionSize, MarkupBlobRef, Domain, Rank, Role, RolesAssignment } from '@hcengineering/core'
|
|
import { Account, AccountRole, IndexKind, Ref } from '@hcengineering/core'
|
|
import {
|
|
type Document,
|
|
type DocumentEmbedding,
|
|
type DocumentSnapshot,
|
|
type SavedDocument,
|
|
type Teamspace,
|
|
documentId
|
|
} from '@hcengineering/document'
|
|
import {
|
|
type Builder,
|
|
Collection,
|
|
Hidden,
|
|
Index,
|
|
Mixin,
|
|
Model,
|
|
Prop,
|
|
ReadOnly,
|
|
TypeCollaborativeDoc,
|
|
TypeNumber,
|
|
TypeRef,
|
|
TypeString,
|
|
UX
|
|
} from '@hcengineering/model'
|
|
import attachment, { TAttachment } from '@hcengineering/model-attachment'
|
|
import chunter from '@hcengineering/model-chunter'
|
|
import core, { TDoc, TTypedSpace } from '@hcengineering/model-core'
|
|
import { createPublicLinkAction } from '@hcengineering/model-guest'
|
|
import { generateClassNotificationTypes } from '@hcengineering/model-notification'
|
|
import preference, { TPreference } from '@hcengineering/model-preference'
|
|
import presentation from '@hcengineering/model-presentation'
|
|
import tracker from '@hcengineering/model-tracker'
|
|
import view, { actionTemplates, createAction } from '@hcengineering/model-view'
|
|
import workbench from '@hcengineering/model-workbench'
|
|
import notification from '@hcengineering/notification'
|
|
import { type Asset, getEmbeddedLabel } from '@hcengineering/platform'
|
|
import tags from '@hcengineering/tags'
|
|
import time, { type ToDo, type Todoable } from '@hcengineering/time'
|
|
import document from './plugin'
|
|
|
|
export { documentId } from '@hcengineering/document'
|
|
|
|
export { documentOperation } from './migration'
|
|
export { document as default }
|
|
|
|
export const DOMAIN_DOCUMENT = 'document' as Domain
|
|
|
|
@Model(document.class.DocumentEmbedding, attachment.class.Attachment)
|
|
@UX(document.string.Embedding)
|
|
export class TDocumentEmbedding extends TAttachment implements DocumentEmbedding {
|
|
declare attachedTo: Ref<Document>
|
|
declare attachedToClass: Ref<Class<Document>>
|
|
}
|
|
|
|
@Model(document.class.Document, core.class.Doc, DOMAIN_DOCUMENT)
|
|
@UX(document.string.Document, document.icon.Document, undefined, 'name', undefined, document.string.Documents)
|
|
export class TDocument extends TDoc implements Document, Todoable {
|
|
@Prop(TypeString(), document.string.Name)
|
|
@Index(IndexKind.FullText)
|
|
title!: string
|
|
|
|
@Prop(TypeCollaborativeDoc(), document.string.Document)
|
|
content!: MarkupBlobRef | null
|
|
|
|
@Prop(TypeRef(document.class.Document), document.string.ParentDocument)
|
|
parent!: Ref<Document>
|
|
|
|
@Prop(TypeRef(core.class.Space), core.string.Space)
|
|
@Index(IndexKind.Indexed)
|
|
@Hidden()
|
|
declare space: Ref<Teamspace>
|
|
|
|
@Prop(TypeRef(core.class.Account), document.string.LockedBy)
|
|
@Hidden()
|
|
lockedBy?: Ref<Account>
|
|
|
|
@Prop(Collection(document.class.DocumentEmbedding), document.string.Embeddings)
|
|
embeddings?: number
|
|
|
|
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, { shortLabel: attachment.string.Files })
|
|
attachments?: number
|
|
|
|
@Prop(Collection(chunter.class.ChatMessage), chunter.string.Comments)
|
|
comments?: number
|
|
|
|
@Prop(Collection(tags.class.TagReference), document.string.Labels)
|
|
labels?: number
|
|
|
|
@Prop(Collection(activity.class.ActivityReference), document.string.Backlinks)
|
|
@Hidden()
|
|
references!: number
|
|
|
|
@Prop(TypeString(), document.string.Icon)
|
|
@Hidden()
|
|
@Index(IndexKind.FullText)
|
|
icon?: Asset
|
|
|
|
@Prop(TypeNumber(), document.string.Color)
|
|
@Hidden()
|
|
@Index(IndexKind.FullText)
|
|
color?: number
|
|
|
|
@Prop(Collection(time.class.ToDo), getEmbeddedLabel('Action Items'))
|
|
todos?: CollectionSize<ToDo>
|
|
|
|
@Index(IndexKind.Indexed)
|
|
@Hidden()
|
|
rank!: Rank
|
|
}
|
|
|
|
@Model(document.class.DocumentSnapshot, core.class.Doc, DOMAIN_DOCUMENT)
|
|
@UX(document.string.Version)
|
|
export class TDocumentSnapshot extends TDoc implements DocumentSnapshot {
|
|
@Prop(TypeRef(core.class.Space), core.string.Space)
|
|
@Index(IndexKind.Indexed)
|
|
@Hidden()
|
|
declare space: Ref<Teamspace>
|
|
|
|
@Prop(TypeString(), document.string.Name)
|
|
@Index(IndexKind.FullText)
|
|
title!: string
|
|
|
|
@Prop(TypeCollaborativeDoc(), document.string.Document)
|
|
@ReadOnly()
|
|
content!: MarkupBlobRef
|
|
|
|
@Prop(TypeRef(document.class.Document), document.string.ParentDocument)
|
|
parent!: Ref<Document>
|
|
}
|
|
|
|
@Model(document.class.SavedDocument, preference.class.Preference)
|
|
export class TSavedDocument extends TPreference implements SavedDocument {
|
|
@Prop(TypeRef(document.class.Document), document.string.SavedDocuments)
|
|
declare attachedTo: Ref<Document>
|
|
}
|
|
|
|
@Model(document.class.Teamspace, core.class.TypedSpace)
|
|
@UX(document.string.Teamspace, document.icon.Teamspace, 'Teamspace', 'name')
|
|
export class TTeamspace extends TTypedSpace implements Teamspace {}
|
|
|
|
@Mixin(document.mixin.DefaultTeamspaceTypeData, document.class.Teamspace)
|
|
@UX(getEmbeddedLabel('Default teamspace type'), document.icon.Document)
|
|
export class TDefaultTeamspaceTypeData extends TTeamspace implements RolesAssignment {
|
|
[key: Ref<Role>]: Ref<Account>[]
|
|
}
|
|
|
|
function defineTeamspace (builder: Builder): void {
|
|
builder.createModel(TTeamspace)
|
|
|
|
builder.createDoc(
|
|
core.class.SpaceTypeDescriptor,
|
|
core.space.Model,
|
|
{
|
|
name: document.string.DocumentApplication,
|
|
description: document.string.Description,
|
|
icon: document.icon.Document,
|
|
baseClass: document.class.Teamspace,
|
|
availablePermissions: [
|
|
core.permission.UpdateSpace,
|
|
core.permission.ArchiveSpace,
|
|
core.permission.ForbidDeleteObject
|
|
]
|
|
},
|
|
document.descriptor.TeamspaceType
|
|
)
|
|
|
|
builder.createDoc(
|
|
core.class.SpaceType,
|
|
core.space.Model,
|
|
{
|
|
name: 'Default teamspace type',
|
|
descriptor: document.descriptor.TeamspaceType,
|
|
roles: 0,
|
|
targetClass: document.mixin.DefaultTeamspaceTypeData
|
|
},
|
|
document.spaceType.DefaultTeamspaceType
|
|
)
|
|
|
|
// Navigator
|
|
|
|
builder.mixin(document.class.Teamspace, core.class.Class, view.mixin.SpacePresenter, {
|
|
presenter: document.component.TeamspaceSpacePresenter
|
|
})
|
|
|
|
builder.createDoc(
|
|
view.class.Viewlet,
|
|
core.space.Model,
|
|
{
|
|
attachTo: document.class.Teamspace,
|
|
descriptor: view.viewlet.Table,
|
|
configOptions: {
|
|
hiddenKeys: ['name', 'description']
|
|
},
|
|
config: ['', 'members', 'private', 'archived'],
|
|
viewOptions: {
|
|
groupBy: [],
|
|
orderBy: [],
|
|
other: [
|
|
{
|
|
key: 'hideArchived',
|
|
type: 'toggle',
|
|
defaultValue: true,
|
|
actionTarget: 'query',
|
|
action: document.function.HideArchivedTeamspaces,
|
|
label: view.string.HideArchived
|
|
}
|
|
]
|
|
}
|
|
},
|
|
document.viewlet.TeamspaceTable
|
|
)
|
|
|
|
// Actions
|
|
|
|
builder.mixin(document.class.Teamspace, core.class.Class, view.mixin.IgnoreActions, {
|
|
actions: [tracker.action.EditRelatedTargets, tracker.action.NewRelatedIssue]
|
|
})
|
|
|
|
createAction(
|
|
builder,
|
|
{
|
|
action: document.actionImpl.EditTeamspace,
|
|
label: document.string.EditTeamspace,
|
|
icon: view.icon.Edit,
|
|
input: 'focus',
|
|
category: document.category.Document,
|
|
target: document.class.Teamspace,
|
|
visibilityTester: view.function.CanEditSpace,
|
|
query: {},
|
|
context: {
|
|
mode: ['context', 'browser'],
|
|
group: 'edit'
|
|
}
|
|
},
|
|
document.action.EditTeamspace
|
|
)
|
|
|
|
createAction(
|
|
builder,
|
|
{
|
|
action: document.actionImpl.CreateDocument,
|
|
label: document.string.CreateDocument,
|
|
icon: document.icon.Add,
|
|
input: 'focus',
|
|
category: document.category.Document,
|
|
target: document.class.Teamspace,
|
|
inline: true,
|
|
query: {
|
|
archived: false
|
|
},
|
|
context: {
|
|
mode: ['context', 'browser'],
|
|
application: document.app.Documents,
|
|
group: 'create'
|
|
}
|
|
},
|
|
document.action.CreateDocument
|
|
)
|
|
|
|
createPublicLinkAction(builder, document.class.Document, document.action.PublicLink)
|
|
}
|
|
|
|
function defineDocument (builder: Builder): void {
|
|
builder.createModel(TDocument, TDocumentSnapshot, TDocumentEmbedding, TSavedDocument, TDefaultTeamspaceTypeData)
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, time.mixin.ItemPresenter, {
|
|
presenter: document.component.DocumentToDoPresenter
|
|
})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.ObjectFactory, {
|
|
component: document.component.CreateDocument
|
|
})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.ObjectEditor, {
|
|
editor: document.component.EditDoc
|
|
})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.ObjectPanel, {
|
|
component: document.component.EditDoc
|
|
})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.ObjectPresenter, {
|
|
presenter: document.component.DocumentPresenter
|
|
})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.LinkProvider, {
|
|
encode: document.function.GetObjectLinkFragment
|
|
})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.LinkIdProvider, {
|
|
encode: document.function.GetDocumentLinkId,
|
|
decode: document.function.ParseDocumentId
|
|
})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.ObjectIcon, {
|
|
component: document.component.DocumentIcon
|
|
})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.AttributeEditor, {
|
|
inlineEditor: document.component.DocumentInlineEditor
|
|
})
|
|
|
|
// Actions
|
|
|
|
createAction(builder, {
|
|
...actionTemplates.open,
|
|
actionProps: {
|
|
component: document.component.EditDoc
|
|
},
|
|
target: document.class.Document,
|
|
context: {
|
|
mode: ['browser', 'context'],
|
|
group: 'create'
|
|
},
|
|
override: [view.action.Open]
|
|
})
|
|
|
|
createAction(builder, {
|
|
...actionTemplates.move,
|
|
action: view.actionImpl.ShowPopup,
|
|
actionProps: {
|
|
component: document.component.Move,
|
|
element: 'top',
|
|
fillProps: {
|
|
_object: 'value'
|
|
}
|
|
},
|
|
target: document.class.Document,
|
|
context: {
|
|
mode: ['browser', 'context'],
|
|
group: 'tools'
|
|
}
|
|
})
|
|
|
|
createAction(
|
|
builder,
|
|
{
|
|
action: document.actionImpl.CreateChildDocument,
|
|
label: document.string.CreateDocument,
|
|
icon: document.icon.Add,
|
|
input: 'focus',
|
|
category: document.category.Document,
|
|
target: document.class.Document,
|
|
context: {
|
|
mode: ['context', 'browser'],
|
|
application: document.app.Documents,
|
|
group: 'create'
|
|
}
|
|
},
|
|
document.action.CreateChildDocument
|
|
)
|
|
|
|
createAction(
|
|
builder,
|
|
{
|
|
action: view.actionImpl.CopyTextToClipboard,
|
|
actionProps: {
|
|
textProvider: document.function.GetDocumentLink
|
|
},
|
|
label: document.string.CopyDocumentUrl,
|
|
icon: view.icon.CopyLink,
|
|
input: 'focus',
|
|
category: document.category.Document,
|
|
target: document.class.Document,
|
|
context: {
|
|
mode: ['context', 'browser'],
|
|
application: document.app.Documents,
|
|
group: 'copy'
|
|
}
|
|
},
|
|
document.action.CopyDocumentLink
|
|
)
|
|
|
|
createAction(
|
|
builder,
|
|
{
|
|
action: document.actionImpl.LockContent,
|
|
label: document.string.Lock,
|
|
icon: document.icon.Lock,
|
|
input: 'focus',
|
|
category: document.category.Document,
|
|
target: document.class.Document,
|
|
context: {
|
|
mode: ['context', 'browser'],
|
|
application: document.app.Documents,
|
|
group: 'copy'
|
|
},
|
|
visibilityTester: document.function.CanLockDocument
|
|
},
|
|
document.action.LockContent
|
|
)
|
|
|
|
createAction(
|
|
builder,
|
|
{
|
|
action: document.actionImpl.UnlockContent,
|
|
label: document.string.Unlock,
|
|
icon: document.icon.Unlock,
|
|
input: 'focus',
|
|
category: document.category.Document,
|
|
target: document.class.Document,
|
|
context: {
|
|
mode: ['context', 'browser'],
|
|
application: document.app.Documents,
|
|
group: 'copy'
|
|
},
|
|
visibilityTester: document.function.CanUnlockDocument
|
|
},
|
|
document.action.UnlockContent
|
|
)
|
|
|
|
// Notifications
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, activity.mixin.ActivityDoc, {})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, notification.mixin.ClassCollaborators, {
|
|
fields: ['createdBy', 'modifiedBy']
|
|
})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, notification.mixin.NotificationObjectPresenter, {
|
|
presenter: document.component.NotificationDocumentPresenter
|
|
})
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.IgnoreActions, {
|
|
actions: [view.action.Open, view.action.OpenInNewTab, tracker.action.NewRelatedIssue]
|
|
})
|
|
|
|
builder.createDoc(
|
|
notification.class.NotificationGroup,
|
|
core.space.Model,
|
|
{
|
|
label: document.string.DocumentApplication,
|
|
icon: document.icon.DocumentApplication,
|
|
objectClass: document.class.Document
|
|
},
|
|
document.ids.DocumentNotificationGroup
|
|
)
|
|
|
|
builder.createDoc(
|
|
notification.class.NotificationType,
|
|
core.space.Model,
|
|
{
|
|
hidden: false,
|
|
generated: false,
|
|
allowedForAuthor: false,
|
|
label: document.string.Document,
|
|
group: document.ids.DocumentNotificationGroup,
|
|
field: 'content',
|
|
txClasses: [core.class.TxUpdateDoc],
|
|
objectClass: document.class.Document,
|
|
defaultEnabled: false,
|
|
templates: {
|
|
textTemplate: '{body}',
|
|
htmlTemplate: '<p>{body}</p>',
|
|
subjectTemplate: '{title}'
|
|
}
|
|
},
|
|
document.ids.ContentNotification
|
|
)
|
|
|
|
builder.createDoc(notification.class.NotificationProviderDefaults, core.space.Model, {
|
|
provider: notification.providers.InboxNotificationProvider,
|
|
ignoredTypes: [],
|
|
enabledTypes: [document.ids.ContentNotification]
|
|
})
|
|
|
|
generateClassNotificationTypes(
|
|
builder,
|
|
document.class.Document,
|
|
document.ids.DocumentNotificationGroup,
|
|
[],
|
|
['attachments', 'comments']
|
|
)
|
|
|
|
// Activity & Inbox
|
|
|
|
builder.mixin(document.class.Document, core.class.Class, view.mixin.ObjectTitle, {
|
|
titleProvider: document.function.DocumentTitleProvider
|
|
})
|
|
|
|
builder.createDoc(activity.class.ActivityExtension, core.space.Model, {
|
|
ofClass: document.class.Document,
|
|
components: { input: { component: chunter.component.ChatMessageInput } }
|
|
})
|
|
|
|
// Search
|
|
|
|
builder.createDoc(
|
|
presentation.class.ObjectSearchCategory,
|
|
core.space.Model,
|
|
{
|
|
title: document.string.Documents,
|
|
icon: document.icon.Document,
|
|
label: document.string.SearchDocument,
|
|
query: document.completion.DocumentQuery,
|
|
context: ['search', 'mention', 'spotlight'],
|
|
classToSearch: document.class.Document,
|
|
priority: 800
|
|
},
|
|
document.completion.DocumentQueryCategory
|
|
)
|
|
}
|
|
|
|
function defineApplication (builder: Builder): void {
|
|
builder.createDoc(
|
|
workbench.class.Application,
|
|
core.space.Model,
|
|
{
|
|
label: document.string.DocumentApplication,
|
|
icon: document.icon.DocumentApplication,
|
|
alias: documentId,
|
|
hidden: false,
|
|
locationResolver: document.resolver.Location,
|
|
navigatorModel: {
|
|
specials: [
|
|
{
|
|
id: 'browser',
|
|
accessLevel: AccountRole.User,
|
|
label: document.string.Teamspaces,
|
|
icon: view.icon.List,
|
|
component: workbench.component.SpecialView,
|
|
componentProps: {
|
|
_class: document.class.Teamspace,
|
|
icon: view.icon.List,
|
|
label: document.string.Teamspaces
|
|
},
|
|
position: 'top'
|
|
}
|
|
],
|
|
spaces: [
|
|
{
|
|
id: 'teamspaces',
|
|
label: document.string.Teamspaces,
|
|
spaceClass: document.class.Teamspace,
|
|
addSpaceLabel: document.string.CreateTeamspace,
|
|
createComponent: document.component.CreateTeamspace,
|
|
icon: document.icon.Teamspace,
|
|
// intentionally left empty in order to make space presenter working
|
|
specials: []
|
|
}
|
|
]
|
|
},
|
|
navHeaderComponent: document.component.NewDocumentHeader
|
|
},
|
|
document.app.Documents
|
|
)
|
|
}
|
|
|
|
export function createModel (builder: Builder): void {
|
|
defineTeamspace(builder)
|
|
defineDocument(builder)
|
|
|
|
defineApplication(builder)
|
|
|
|
builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, {
|
|
domain: DOMAIN_DOCUMENT,
|
|
disabled: [
|
|
{ modifiedOn: 1 },
|
|
{ modifiedBy: 1 },
|
|
{ createdBy: 1 },
|
|
{ attachedToClass: 1 },
|
|
{ createdOn: -1 },
|
|
{ attachedTo: 1 }
|
|
]
|
|
})
|
|
}
|