Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions models/card/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,22 @@ export function createActions (builder: Builder): void {
group: 'edit'
}
})

createAction(
builder,
{
action: card.actionImpl.DuplicateCard,
label: card.string.Duplicate,
icon: card.icon.Duplicate,
input: 'focus',
category: card.category.Card,
target: card.class.Card,
context: {
mode: ['context', 'browser'],
application: card.app.Card,
group: 'associate'
}
},
card.action.Duplicate
)
}
4 changes: 3 additions & 1 deletion models/card/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ export default mergeIds(cardId, card, {
},
actionImpl: {
DeleteMasterTag: '' as ViewAction,
DuplicateCard: '' as ViewAction,
EditSpace: '' as ViewAction
},
action: {
DeleteMasterTag: '' as Ref<Action>,
SetParent: '' as Ref<Action<Doc, any>>,
UnsetParent: '' as Ref<Action<Doc, any>>,
PublicLink: '' as Ref<Action<Doc, any>>
PublicLink: '' as Ref<Action<Doc, any>>,
Duplicate: '' as Ref<Action>
},
string: {
CreateCardPersmissionDescription: '' as IntlString,
Expand Down
4 changes: 4 additions & 0 deletions plugins/card-assets/assets/icons.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "Zakázat aktualizaci karty",
"ForbidCreateCardPermission": "Zakázat vytváření karet",
"ForbidAddTagPermission": "Zakázat přidávání štítků",
"ForbidRemoveTag": "Zakázat odebírání štítků"
"ForbidRemoveTag": "Zakázat odebírání štítků",
"Duplicate": "Duplikovat"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "Aktualisieren der Karte verbieten",
"ForbidCreateCardPermission": "Erstellen von Karten verbieten",
"ForbidAddTagPermission": "Hinzufügen von Tags verbieten",
"ForbidRemoveTag": "Entfernen von Tags verbieten"
"ForbidRemoveTag": "Entfernen von Tags verbieten",
"Duplicate": "Duplizieren"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "Forbid Update Card",
"ForbidCreateCardPermission": "Forbid Create Card",
"ForbidAddTagPermission": "Forbid Add Tag",
"ForbidRemoveTag": "Forbid Remove Tag"
"ForbidRemoveTag": "Forbid Remove Tag",
"Duplicate": "Duplicate"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "Prohibir actualizar tarjeta",
"ForbidCreateCardPermission": "Prohibir crear tarjeta",
"ForbidAddTagPermission": "Prohibir añadir etiqueta",
"ForbidRemoveTag": "Prohibir eliminar etiqueta"
"ForbidRemoveTag": "Prohibir eliminar etiqueta",
"Duplicate": "Duplicar"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "Interdire la mise à jour de la fiche",
"ForbidCreateCardPermission": "Interdire la création de fiche",
"ForbidAddTagPermission": "Interdire l'ajout d'étiquette",
"ForbidRemoveTag": "Interdire la suppression d'étiquette"
"ForbidRemoveTag": "Interdire la suppression d'étiquette",
"Duplicate": "Dupliquer"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "Vieta aggiornamento scheda",
"ForbidCreateCardPermission": "Vieta creazione scheda",
"ForbidAddTagPermission": "Vieta aggiunta tag",
"ForbidRemoveTag": "Vieta rimozione tag"
"ForbidRemoveTag": "Vieta rimozione tag",
"Duplicate": "Duplicato"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "カード更新を禁止",
"ForbidCreateCardPermission": "カード作成を禁止",
"ForbidAddTagPermission": "タグ追加を禁止",
"ForbidRemoveTag": "タグ削除を禁止"
"ForbidRemoveTag": "タグ削除を禁止",
"Duplicate": "複製"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "Proibir atualização de cartão",
"ForbidCreateCardPermission": "Proibir criação de cartão",
"ForbidAddTagPermission": "Proibir adicionar etiqueta",
"ForbidRemoveTag": "Proibir remover etiqueta"
"ForbidRemoveTag": "Proibir remover etiqueta",
"Duplicate": "Duplicar"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "Запретить обновление карточки",
"ForbidCreateCardPermission": "Запретить создание карточки",
"ForbidAddTagPermission": "Запретить добавление тега",
"ForbidRemoveTag": "Запретить удаление тега"
"ForbidRemoveTag": "Запретить удаление тега",
"Duplicate": "Дублировать"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "Kart güncellemeyi yasakla",
"ForbidCreateCardPermission": "Kart oluşturmayı yasakla",
"ForbidAddTagPermission": "Etiket eklemeyi yasakla",
"ForbidRemoveTag": "Etiket kaldırmayı yasakla"
"ForbidRemoveTag": "Etiket kaldırmayı yasakla",
"Duplicate": "Çoğalt"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"ForbidUpdateCard": "禁止更新卡片",
"ForbidCreateCardPermission": "禁止创建卡片",
"ForbidAddTagPermission": "禁止添加标签",
"ForbidRemoveTag": "禁止移除标签"
"ForbidRemoveTag": "禁止移除标签",
"Duplicate": "复制"
}
}
3 changes: 2 additions & 1 deletion plugins/card-assets/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ loadMetadata(card.icon, {
Space: `${icons}#space`,
Expand: `${icons}#expand`,
Feed: `${icons}#feed`,
All: `${icons}#all`
All: `${icons}#all`,
Duplicate: `${icons}#duplicate`
})
4 changes: 3 additions & 1 deletion plugins/card-resources/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ import {
checkRelationsSectionVisibility,
getSpaceAccessPublicLink,
canGetSpaceAccessPublicLink,
cardFactory
cardFactory,
duplicateCard
} from './utils'
import ManageMasterTagsContent from './components/settings/ManageMasterTagsContent.svelte'
import ManageMasterTagsTools from './components/settings/ManageMasterTagsTools.svelte'
Expand Down Expand Up @@ -144,6 +145,7 @@ export default async (): Promise<Resources> => ({
},
actionImpl: {
DeleteMasterTag: deleteMasterTag,
DuplicateCard: duplicateCard,
EditSpace: editSpace
},
function: {
Expand Down
3 changes: 2 additions & 1 deletion plugins/card-resources/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export default mergeIds(cardId, card, {
CardTitle: '' as IntlString,
CardContent: '' as IntlString,
Post: '' as IntlString,
ShowLess: '' as IntlString
ShowLess: '' as IntlString,
Duplicate: '' as IntlString
}
})
92 changes: 86 additions & 6 deletions plugins/card-resources/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { type AccountClient, getClient as getAccountClientRaw } from '@hcengineering/account-client'
import { Analytics } from '@hcengineering/analytics'
import { type Card, CardEvents, cardId, type CardSpace, type MasterTag } from '@hcengineering/card'
import core, {
AccountRole,
Expand All @@ -25,6 +27,7 @@ import core, {
hasAccountRole,
type Hierarchy,
makeCollabId,
makeDocCollabId,
type Markup,
type MarkupBlobRef,
type Ref,
Expand All @@ -35,33 +38,34 @@ import core, {
type WithLookup
} from '@hcengineering/core'
import login from '@hcengineering/login'
import { getMetadata, translate } from '@hcengineering/platform'
import presentation, {
createMarkup,
getClient,
getMarkup,
IconWithEmoji,
MessageBox,
type ObjectSearchResult
} from '@hcengineering/presentation'
import { type AccountClient, getClient as getAccountClientRaw } from '@hcengineering/account-client'
import { makeRank } from '@hcengineering/rank'
import { EmptyMarkup, isEmptyMarkup } from '@hcengineering/text'
import {
getCurrentLocation,
getCurrentResolvedLocation,
getPanelURI,
type IconComponent,
type IconProps,
type Location,
navigate,
type ResolvedLocation,
showPopup
} from '@hcengineering/ui'
import view, { encodeObjectURI, canCopyLink } from '@hcengineering/view'
import view, { canCopyLink, encodeObjectURI } from '@hcengineering/view'
import { accessDeniedStore } from '@hcengineering/view-resources'
import workbench, { type LocationData, type Widget, type WidgetTab } from '@hcengineering/workbench'
import { translate, getMetadata } from '@hcengineering/platform'
import { makeRank } from '@hcengineering/rank'
import { Analytics } from '@hcengineering/analytics'
import { createWidgetTab } from '@hcengineering/workbench-resources'
import { EmptyMarkup, isEmptyMarkup } from '@hcengineering/text'

import attachment from '@hcengineering/attachment'
import CardSearchItem from './components/CardSearchItem.svelte'
import CreateSpace from './components/navigator/CreateSpace.svelte'
import card from './plugin'
Expand Down Expand Up @@ -92,6 +96,82 @@ export async function deleteMasterTag (tag: MasterTag | undefined, onDelete?: ()
}
}

export async function duplicateCard (origin: Card): Promise<void> {
const client = getClient()
const h = client.getHierarchy()
const props: Partial<Data<Card>> = {}
const base = h.getBaseClass(origin._class)
const mixins = h.findAllMixins(origin)
const attrs = h.getAllAttributes(base, core.class.Doc)

for (const [key, attr] of attrs) {
if (attr.readonly !== true && attr.hidden !== true) {
if (attr.type._class === core.class.Collection) {
;(props as any)[key] = 0
} else if (
attr.type._class !== core.class.TypeCollaborativeDoc &&
attr.type._class !== core.class.TypeIdentifier
) {
;(props as any)[key] = (origin as any)[key]
}
}
}
props.title = `${origin.title} (Copy)`
const targetId = generateId()
const relationsA = await client.findAll(core.class.Relation, { docA: origin._id })
const relationsB = await client.findAll(core.class.Relation, { docB: origin._id })

const markup = await getMarkup(makeDocCollabId(origin, 'content'), origin.content)
if (!isEmptyMarkup(markup)) {
const collabId = makeCollabId(base, targetId, 'content')
props.content = await createMarkup(collabId, markup)
}

const ops = client.apply(`Duplicate_card_${origin._id}`)
await ops.createDoc(base, origin.space, props, targetId)
for (const mixin of mixins) {
const mixinAttrs = h.getOwnAttributes(mixin)
const as = h.as(origin, mixin)
const attributes: Partial<Data<Doc>> = {}
for (const [key, attr] of mixinAttrs) {
if (attr.readonly !== true && attr.hidden !== true) {
;(attributes as any)[key] = (as as any)[key]
}
}
await ops.createMixin(targetId, base, origin.space, mixin, attributes)
}

for (const rel of relationsA) {
await ops.createDoc(core.class.Relation, origin.space, {
docA: targetId,
docB: rel.docB,
association: rel.association
})
}
for (const rel of relationsB) {
await ops.createDoc(core.class.Relation, origin.space, {
docA: rel.docA,
docB: targetId,
association: rel.association
})
}
await ops.commit()

const attachments = await client.findAll(attachment.class.Attachment, { attachedTo: origin._id })
const attachmentOps = client.apply(`Duplicate_attachments_${origin._id}`)
for (const att of attachments) {
const { _id, modifiedBy, modifiedOn, attachedTo, attachedToClass, collection, space, ...props } = att
await attachmentOps.addCollection(attachment.class.Attachment, origin.space, targetId, base, 'attachments', props)
}
await attachmentOps.commit()

const loc = getCurrentLocation()
loc.path[2] = cardId
loc.path[3] = targetId
loc.path.length = 4
navigate(loc)
}

export async function resolveLocation (loc: Location): Promise<ResolvedLocation | undefined> {
if (loc.path[2] !== cardId) {
return undefined
Expand Down
3 changes: 2 additions & 1 deletion plugins/card/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ const cardPlugin = plugin(cardId, {
Space: '' as Asset,
Expand: '' as Asset,
Feed: '' as Asset,
All: '' as Asset
All: '' as Asset,
Duplicate: '' as Asset
},
extensions: {
EditCardExtension: '' as ComponentExtensionId
Expand Down
Loading