mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-17 18:05:42 +02:00
EQMS-1635 Add generic action for copying link (#9668)
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
@@ -42,6 +42,7 @@
|
||||
"@hcengineering/time": "^0.6.0",
|
||||
"@hcengineering/card": "^0.6.0",
|
||||
"@hcengineering/model-attachment": "^0.6.0",
|
||||
"@hcengineering/model-guest": "^0.6.0",
|
||||
"@hcengineering/model-presentation": "^0.6.0",
|
||||
"@hcengineering/model-preference": "^0.6.0",
|
||||
"@hcengineering/workbench": "^0.6.16",
|
||||
|
||||
@@ -57,6 +57,7 @@ import {
|
||||
} from '@hcengineering/model'
|
||||
import attachment from '@hcengineering/model-attachment'
|
||||
import { TAttachedDoc, TClass, TDoc, TMixin, TSpace } from '@hcengineering/model-core'
|
||||
import { createPublicLinkAction } from '@hcengineering/model-guest'
|
||||
import presentation from '@hcengineering/model-presentation'
|
||||
import setting from '@hcengineering/model-setting'
|
||||
import view, { createAction, type Viewlet } from '@hcengineering/model-view'
|
||||
@@ -875,6 +876,8 @@ export function createModel (builder: Builder): void {
|
||||
match: card.function.CardCustomLinkMatch,
|
||||
encode: card.function.CardCustomLinkEncode
|
||||
})
|
||||
|
||||
createPublicLinkAction(builder, card.class.Card, card.action.PublicLink)
|
||||
}
|
||||
|
||||
function defineTabs (builder: Builder): void {
|
||||
|
||||
@@ -34,7 +34,8 @@ export default mergeIds(cardId, card, {
|
||||
action: {
|
||||
DeleteMasterTag: '' as Ref<Action>,
|
||||
SetParent: '' as Ref<Action<Doc, any>>,
|
||||
UnsetParent: '' as Ref<Action<Doc, any>>
|
||||
UnsetParent: '' as Ref<Action<Doc, any>>,
|
||||
PublicLink: '' as Ref<Action<Doc, any>>
|
||||
},
|
||||
category: {
|
||||
Card: '' as Ref<ActionCategory>,
|
||||
|
||||
@@ -97,7 +97,8 @@ function defineMessageActions (builder: Builder): void {
|
||||
mode: ['context', 'browser'],
|
||||
application: chunter.app.Chunter,
|
||||
group: 'copy'
|
||||
}
|
||||
},
|
||||
override: [view.action.CopyLink]
|
||||
},
|
||||
chunter.action.CopyChatMessageLink
|
||||
)
|
||||
|
||||
@@ -369,27 +369,6 @@ function defineDocument (builder: Builder): void {
|
||||
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,
|
||||
{
|
||||
|
||||
@@ -1286,7 +1286,8 @@ export function createModel (builder: Builder): void {
|
||||
mode: ['context', 'browser'],
|
||||
application: recruit.app.Recruit,
|
||||
group: 'copy'
|
||||
}
|
||||
},
|
||||
override: [view.action.CopyLink]
|
||||
},
|
||||
recruit.action.CopyApplicationLink
|
||||
)
|
||||
@@ -1306,7 +1307,8 @@ export function createModel (builder: Builder): void {
|
||||
mode: ['context', 'browser'],
|
||||
application: recruit.app.Recruit,
|
||||
group: 'copy'
|
||||
}
|
||||
},
|
||||
override: [view.action.CopyLink]
|
||||
},
|
||||
recruit.action.CopyCandidateLink
|
||||
)
|
||||
|
||||
@@ -633,7 +633,8 @@ export function createActions (builder: Builder, issuesId: string, componentsId:
|
||||
mode: ['context', 'browser'],
|
||||
application: tracker.app.Tracker,
|
||||
group: 'copy'
|
||||
}
|
||||
},
|
||||
override: [view.action.CopyLink]
|
||||
},
|
||||
tracker.action.CopyIssueLink
|
||||
)
|
||||
|
||||
@@ -746,6 +746,24 @@ export function createModel (builder: Builder): void {
|
||||
view.action.Delete
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
action: view.actionImpl.CopyTextToClipboard,
|
||||
actionProps: {
|
||||
textProvider: view.function.GetLink
|
||||
},
|
||||
label: view.string.CopyLink,
|
||||
icon: view.icon.CopyLink,
|
||||
category: view.category.General,
|
||||
input: 'any',
|
||||
target: core.class.Doc,
|
||||
context: { mode: ['context', 'browser'] },
|
||||
visibilityTester: view.function.CanCopyLink
|
||||
},
|
||||
view.action.CopyLink
|
||||
)
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
|
||||
@@ -120,7 +120,8 @@ export default mergeIds(viewId, view, {
|
||||
Navigation: '' as IntlString,
|
||||
Editor: '' as IntlString,
|
||||
MarkdownFormatting: '' as IntlString,
|
||||
HideArchived: '' as IntlString
|
||||
HideArchived: '' as IntlString,
|
||||
CopyLink: '' as IntlString
|
||||
},
|
||||
function: {
|
||||
FilterArrayAllResult: '' as FilterFunction,
|
||||
@@ -153,7 +154,9 @@ export default mergeIds(viewId, view, {
|
||||
CanLeaveSpace: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
|
||||
IsClipboardAvailable: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>,
|
||||
BlobImageMetadata: '' as Resource<(file: FileOrBlob, blob: Ref<Blob>) => Promise<BlobMetadata | undefined>>,
|
||||
BlobVideoMetadata: '' as Resource<(file: FileOrBlob, blob: Ref<Blob>) => Promise<BlobMetadata | undefined>>
|
||||
BlobVideoMetadata: '' as Resource<(file: FileOrBlob, blob: Ref<Blob>) => Promise<BlobMetadata | undefined>>,
|
||||
GetLink: '' as Resource<(doc?: Doc | Doc[]) => Promise<string>>,
|
||||
CanCopyLink: '' as Resource<(doc?: Doc | Doc[]) => Promise<boolean>>
|
||||
},
|
||||
pipeline: {
|
||||
PresentationMiddleware: '' as Ref<PresentationMiddlewareFactory>,
|
||||
|
||||
@@ -51,7 +51,6 @@ import {
|
||||
documentTitleProvider,
|
||||
getDocumentLink,
|
||||
getDocumentLinkId,
|
||||
getDocumentUrl,
|
||||
parseDocumentId,
|
||||
resolveLocation
|
||||
} from './utils'
|
||||
@@ -192,7 +191,6 @@ export default async (): Promise<Resources> => ({
|
||||
UnlockContent: unlockContent
|
||||
},
|
||||
function: {
|
||||
GetDocumentLink: getDocumentUrl,
|
||||
GetObjectLinkFragment: getDocumentLink,
|
||||
DocumentTitleProvider: documentTitleProvider,
|
||||
CanLockDocument: canLockDocument,
|
||||
|
||||
@@ -28,7 +28,6 @@ export default mergeIds(documentId, document, {
|
||||
},
|
||||
function: {
|
||||
DocumentTitleProvider: '' as Resource<<T extends Doc>(client: Client, ref: Ref<T>, doc?: T) => Promise<string>>,
|
||||
GetDocumentLink: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<string>>,
|
||||
GetObjectLinkFragment: '' as Resource<(doc: Doc, props: Record<string, any>) => Promise<Location>>,
|
||||
GetDocumentLinkId: '' as Resource<(doc: Doc) => Promise<string>>,
|
||||
ParseDocumentId: '' as Resource<(id: string) => Promise<Ref<Document> | undefined>>
|
||||
|
||||
@@ -44,7 +44,6 @@ export const documentPlugin = plugin(documentId, {
|
||||
DocumentSearchIcon: '' as AnyComponent
|
||||
},
|
||||
action: {
|
||||
CopyDocumentLink: '' as Ref<Action<Doc, any>>,
|
||||
CreateChildDocument: '' as Ref<Action>,
|
||||
CreateDocument: '' as Ref<Action>,
|
||||
EditTeamspace: '' as Ref<Action>
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
"PermissionWarningMessage": "Vaše současná role má omezená oprávnění. Chcete-li odemknout tuto a další pokročilé funkce, požádejte správce svého pracovního prostoru o zvýšení úrovně přístupu!",
|
||||
"Icon": "Ikona",
|
||||
"Color": "Barva",
|
||||
"AutomationOnly": "Pouze automatizace"
|
||||
"AutomationOnly": "Pouze automatizace",
|
||||
"CopyLink": "Kopírovat odkaz"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
"PermissionWarningMessage": "Ihre aktuelle Rolle hat eingeschränkte Berechtigungen. Um diese und andere erweiterte Funktionen freizuschalten, bitten Sie Ihren Arbeitsbereich-Administrator, Ihr Zugriffslevel zu erhöhen!",
|
||||
"Icon": "Symbol",
|
||||
"Color": "Farbe",
|
||||
"AutomationOnly": "Nur Automatisierung"
|
||||
"AutomationOnly": "Nur Automatisierung",
|
||||
"CopyLink": "Link kopieren"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
"PermissionWarningMessage": "Your current role has limited permissions. To unlock this and other advanced features, ask your workspace admin to upgrade your access level!",
|
||||
"Icon": "Icon",
|
||||
"Color": "Color",
|
||||
"AutomationOnly": "Automation only"
|
||||
"AutomationOnly": "Automation only",
|
||||
"CopyLink": "Copy link"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
"PermissionWarningMessage": "Tu rol actual tiene permisos limitados. ¡Para desbloquear esta y otras funciones avanzadas, pídele al administrador de tu espacio de trabajo que actualice tu nivel de acceso!",
|
||||
"Icon": "Icono",
|
||||
"Color": "Color",
|
||||
"AutomationOnly": "Solo automatización"
|
||||
"AutomationOnly": "Solo automatización",
|
||||
"CopyLink": "Copiar enlace"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
"PermissionWarningMessage": "Votre rôle actuel a des autorisations limitées. Pour débloquer cette fonctionnalité et d'autres fonctionnalités avancées, demandez à l'administrateur de votre espace de travail de mettre à jour votre niveau d'accès !",
|
||||
"Icon": "Icône",
|
||||
"Color": "Couleur",
|
||||
"AutomationOnly": "Automatisation uniquement"
|
||||
"AutomationOnly": "Automatisation uniquement",
|
||||
"CopyLink": "Copier le lien"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
"PermissionWarningMessage": "Il tuo ruolo attuale ha autorizzazioni limitate. Per sbloccare questa e altre funzionalità avanzate, chiedi all'amministratore del tuo spazio di lavoro di aggiornare il tuo livello di accesso!",
|
||||
"Icon": "Icona",
|
||||
"Color": "Colore",
|
||||
"AutomationOnly": "Solo automazione"
|
||||
"AutomationOnly": "Solo automazione",
|
||||
"CopyLink": "Copia link"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
"PermissionWarningMessage": "現在のロールの権限は制限されています。この機能や他の高度な機能を利用するには、ワークスペース管理者にアクセスレベルのアップグレードを依頼してください!",
|
||||
"Icon": "アイコン",
|
||||
"Color": "色",
|
||||
"AutomationOnly": "自動化のみ"
|
||||
"AutomationOnly": "自動化のみ",
|
||||
"CopyLink": "リンクをコピー"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
"PermissionWarningMessage": "Seu cargo atual tem permissões limitadas. Para desbloquear este e outros recursos avançados, peça ao administrador do seu espaço de trabalho para atualizar seu nível de acesso!",
|
||||
"Icon": "Ícone",
|
||||
"Color": "Cor",
|
||||
"AutomationOnly": "Apenas automação"
|
||||
"AutomationOnly": "Apenas automação",
|
||||
"CopyLink": "Copiar link"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"PermissionWarningMessage": "Ваша текущая роль имеет ограниченные права. Чтобы разблокировать эту и другие продвинутые функции, попросите администратора вашего пространства повысить ваш уровень доступа!",
|
||||
"Icon": "Иконка",
|
||||
"Color": "Цвет",
|
||||
"AutomationOnly": "Только автоматизация"
|
||||
"AutomationOnly": "Только автоматизация",
|
||||
"CopyLink": "Скопировать ссылку"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
"PermissionWarningMessage": "您当前角色的权限有限。要解锁此功能和其他高级功能,请让您的工作区管理员升级您的访问权限!",
|
||||
"Icon": "图标",
|
||||
"Color": "颜色",
|
||||
"AutomationOnly": "仅限自动化"
|
||||
"AutomationOnly": "仅限自动化",
|
||||
"CopyLink": "复制链接"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ import {
|
||||
canLeaveSpace,
|
||||
isClipboardAvailable
|
||||
} from './visibilityTester'
|
||||
import { openDocFromRef } from './utils'
|
||||
import { canCopyLink, getLink, openDocFromRef } from './utils'
|
||||
import ForbiddenNotification from './components/ForbiddenNotification.svelte'
|
||||
export { canArchiveSpace, canDeleteObject, canDeleteSpace, canEditSpace } from './visibilityTester'
|
||||
export { getActions, getContextActions, invokeAction, showMenu } from './actions'
|
||||
@@ -376,6 +376,8 @@ export default async (): Promise<Resources> => ({
|
||||
IsClipboardAvailable: isClipboardAvailable,
|
||||
BlobImageMetadata: blobImageMetadata,
|
||||
BlobVideoMetadata: blobVideoMetadata,
|
||||
OpenDocument: openDocFromRef
|
||||
OpenDocument: openDocFromRef,
|
||||
CanCopyLink: canCopyLink,
|
||||
GetLink: getLink
|
||||
}
|
||||
})
|
||||
|
||||
@@ -57,8 +57,8 @@ import core, {
|
||||
} from '@hcengineering/core'
|
||||
import { type Restrictions } from '@hcengineering/guest'
|
||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||
import { getResource, translate } from '@hcengineering/platform'
|
||||
import {
|
||||
import { getMetadata, getResource, translate } from '@hcengineering/platform'
|
||||
import presentation, {
|
||||
createQuery,
|
||||
getAttributePresenterClass,
|
||||
getClient,
|
||||
@@ -1238,6 +1238,36 @@ export async function openDocFromRef<T extends Doc = Doc> (_class: Ref<Class<T>>
|
||||
return false
|
||||
}
|
||||
|
||||
export async function canCopyLink (doc?: Doc | Doc[]): Promise<boolean> {
|
||||
if (doc === null || doc === undefined) {
|
||||
return false
|
||||
}
|
||||
if (Array.isArray(doc) && doc.length !== 1) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export async function getLink (doc?: Doc | Doc[]): Promise<string> {
|
||||
doc = Array.isArray(doc) ? doc[0] : doc
|
||||
if (doc === undefined) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
const panelComponent = hierarchy.classHierarchyMixin(doc._class, view.mixin.ObjectPanel)
|
||||
const comp = panelComponent?.component ?? view.component.EditDoc
|
||||
const loc = await getObjectLinkFragment(hierarchy, doc, {}, comp)
|
||||
const url = locationToUrl(loc)
|
||||
|
||||
const frontUrl = getMetadata(presentation.metadata.FrontUrl)
|
||||
const protocolAndHost = frontUrl ?? `${window.location.protocol}//${window.location.host}`
|
||||
|
||||
return `${protocolAndHost}${url}`
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
||||
@@ -154,7 +154,9 @@ const view = plugin(viewId, {
|
||||
// Edit document
|
||||
Open: '' as Ref<Action>,
|
||||
OpenInNewTab: '' as Ref<Action>,
|
||||
RemoveRelation: '' as Ref<Action>
|
||||
RemoveRelation: '' as Ref<Action>,
|
||||
|
||||
CopyLink: '' as Ref<Action<Doc, any>>
|
||||
},
|
||||
viewlet: {
|
||||
Table: '' as Ref<ViewletDescriptor>,
|
||||
|
||||
@@ -253,7 +253,7 @@ test.describe('Documents tests', () => {
|
||||
await documentsPage.clickOnButtonCreateDocument()
|
||||
await documentsPage.createDocument(newDocument)
|
||||
await documentsPage.selectMoreActionOfDocument(newDocument.title, 'Lock')
|
||||
await documentsPage.selectMoreActionOfDocument(newDocument.title, 'Copy document URL to clipboard')
|
||||
await documentsPage.selectMoreActionOfDocument(newDocument.title, 'Copy link')
|
||||
await context.grantPermissions(['clipboard-read'])
|
||||
const handle = await page.evaluateHandle(() => navigator.clipboard.readText())
|
||||
const clipboardContent = await handle.jsonValue()
|
||||
|
||||
Reference in New Issue
Block a user