diff --git a/models/all/src/index.ts b/models/all/src/index.ts index bbdd42c246..a5bebe490d 100644 --- a/models/all/src/index.ts +++ b/models/all/src/index.ts @@ -321,7 +321,7 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[ boardModel, boardId, { - label: board.string.ConfigLabel, + label: undefined, // board.string.ConfigLabel, description: board.string.ConfigDescription, enabled: false, beta: true, @@ -333,7 +333,7 @@ export default function buildModel (enabled: string[] = ['*'], disabled: string[ bitrixModel, bitrixId, { - label: bitrix.string.ConfigLabel, + label: undefined, // bitrix.string.ConfigLabel, description: bitrix.string.ConfigDescription, enabled: false, beta: true, diff --git a/models/controlled-documents/src/index.ts b/models/controlled-documents/src/index.ts index dd0f319167..6b75d476e7 100644 --- a/models/controlled-documents/src/index.ts +++ b/models/controlled-documents/src/index.ts @@ -32,6 +32,7 @@ import tracker from '@hcengineering/model-tracker' import view, { classPresenter, createAction } from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' import notification from '@hcengineering/notification' +import contacts from '@hcengineering/model-contact' import setting from '@hcengineering/setting' import tags from '@hcengineering/tags' import textEditor from '@hcengineering/text-editor' @@ -266,7 +267,7 @@ export function createModel (builder: Builder): void { }, { key: '$lookup.owner', - label: documents.string.Owner, + label: documents.string.Author, presenter: documents.component.OwnerPresenter, props: { shouldShowLabel: true, isEditable: false }, sortingKey: '$lookup.owner.name' @@ -329,7 +330,7 @@ export function createModel (builder: Builder): void { }, { key: '$lookup.owner', - label: documents.string.Owner, + label: documents.string.Author, presenter: documents.component.OwnerPresenter, props: { shouldShowLabel: true, isEditable: false }, sortingKey: '$lookup.owner.name' @@ -630,13 +631,23 @@ export function createModel (builder: Builder): void { 'state', 'space', 'template', - 'owner', + { + _class: documents.class.Document, + component: contacts.component.EmployeeFilter, + key: 'owner', + label: documents.string.Author + }, 'category', 'modifiedOn', 'labels', 'major', 'minor', - 'author' + { + _class: documents.class.Document, + component: contacts.component.EmployeeFilter, + key: 'author', + label: documents.string.Creator + } ], getVisibleFilters: documents.function.GetVisibleFilters }) diff --git a/models/setting/src/index.ts b/models/setting/src/index.ts index 234effc894..b2ad09cef8 100644 --- a/models/setting/src/index.ts +++ b/models/setting/src/index.ts @@ -289,8 +289,7 @@ export function createModel (builder: Builder): void { icon: setting.icon.Setting, component: setting.component.Configure, order: 1200, - role: AccountRole.Owner, - adminOnly: true + role: AccountRole.Owner }, setting.ids.Configure ) diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index 1811557b72..0ca4e2ccfe 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -268,6 +268,7 @@ export interface PluginConfiguration extends Doc { pluginId: Plugin transactions: Ref[] + // If not set will not be shown in configuration UI label?: IntlString icon?: Asset description?: IntlString diff --git a/plugins/controlled-documents-assets/lang/cs.json b/plugins/controlled-documents-assets/lang/cs.json index 4a8c9dc26d..3102fc9532 100644 --- a/plugins/controlled-documents-assets/lang/cs.json +++ b/plugins/controlled-documents-assets/lang/cs.json @@ -22,11 +22,12 @@ "Minor": "Vedlejší", "Patch": "Oprava", "ValidationWorkflow": "Ověřovací pracovní postup", - "ChangeOwner": "Změnit vlastníka dokumentu", - "ChangeOwnerHintBeginning": "Převést vlastnictví", + "Creator": "Tvůrce", + "ChangeOwner": "Změnit autora dokumentu", + "ChangeOwnerHintBeginning": "Převést autorství dokumentu", "ChangeOwnerHintEnd": "na jinou osobu.", "ChangeOwnerWarning": "Po této akci nebudete moci dokument upravovat.", - "SelectOwner": "Vyberte nového vlastníka", + "SelectOwner": "Vyberte nového autora", "CreateDocument": "Vytvořit nový dokument", "CreateTemplate": "Vytvořit novou šablonu", "Documents": "Dokumenty", diff --git a/plugins/controlled-documents-assets/lang/de.json b/plugins/controlled-documents-assets/lang/de.json index cf7445dca5..6b57ede440 100644 --- a/plugins/controlled-documents-assets/lang/de.json +++ b/plugins/controlled-documents-assets/lang/de.json @@ -22,11 +22,12 @@ "Minor": "Nebenversion", "Patch": "Patch", "ValidationWorkflow": "Validierungsworkflow", - "ChangeOwner": "Dokumentenbesitzer ändern", - "ChangeOwnerHintBeginning": "Übertragen Sie den Besitz des", + "Creator": "Ersteller", + "ChangeOwner": "Dokumentautor ändern", + "ChangeOwnerHintBeginning": "Autorschaft des Dokuments übertragen", "ChangeOwnerHintEnd": "an eine andere Person.", "ChangeOwnerWarning": "Sie können dieses Dokument nach dieser Aktion nicht mehr bearbeiten.", - "SelectOwner": "Neuen Besitzer auswählen", + "SelectOwner": "Neuen Autor auswählen", "CreateDocument": "Neues Dokument erstellen", "CreateTemplate": "Neue Vorlage erstellen", "Documents": "Dokumente", diff --git a/plugins/controlled-documents-assets/lang/en.json b/plugins/controlled-documents-assets/lang/en.json index 866c649147..d61c4193d2 100644 --- a/plugins/controlled-documents-assets/lang/en.json +++ b/plugins/controlled-documents-assets/lang/en.json @@ -22,11 +22,12 @@ "Minor": "Minor", "Patch": "Patch", "ValidationWorkflow": "Validation workflow", - "ChangeOwner": "Change document owner", - "ChangeOwnerHintBeginning": "Transfer ownership of the", + "Creator": "Creator", + "ChangeOwner": "Change document author", + "ChangeOwnerHintBeginning": "Transfer authorship of the", "ChangeOwnerHintEnd": "to another person.", "ChangeOwnerWarning": "You will not be able to edit this document after this action.", - "SelectOwner": "Select new owner", + "SelectOwner": "Select new author", "CreateDocument": "Create new document", "CreateTemplate": "Create new template", "Documents": "Documents", diff --git a/plugins/controlled-documents-assets/lang/fr.json b/plugins/controlled-documents-assets/lang/fr.json index 0ca6966086..fbd38b627f 100644 --- a/plugins/controlled-documents-assets/lang/fr.json +++ b/plugins/controlled-documents-assets/lang/fr.json @@ -22,11 +22,12 @@ "Minor": "Mineur", "Patch": "Correctif", "ValidationWorkflow": "Flux de validation", - "ChangeOwner": "Changer le propriétaire du document", - "ChangeOwnerHintBeginning": "Transférer la propriété du", + "Creator": "Créateur", + "ChangeOwner": "Changer l’auteur du document", + "ChangeOwnerHintBeginning": "Transférer l’auteur du document", "ChangeOwnerHintEnd": "à une autre personne.", "ChangeOwnerWarning": "Vous ne pourrez plus modifier ce document après cette action.", - "SelectOwner": "Sélectionner un nouveau propriétaire", + "SelectOwner": "Sélectionner un nouvel auteur", "CreateDocument": "Créer un nouveau document", "CreateTemplate": "Créer un nouveau modèle", "Documents": "Documents", diff --git a/plugins/controlled-documents-assets/lang/it.json b/plugins/controlled-documents-assets/lang/it.json index 2af465bb05..eea92939be 100644 --- a/plugins/controlled-documents-assets/lang/it.json +++ b/plugins/controlled-documents-assets/lang/it.json @@ -22,11 +22,12 @@ "Minor": "Minore", "Patch": "Patch", "ValidationWorkflow": "Flusso di convalida", - "ChangeOwner": "Cambia proprietario del documento", - "ChangeOwnerHintBeginning": "Trasferisci la proprietà del", + "Creator": "Creatore", + "ChangeOwner": "Modifica autore del documento", + "ChangeOwnerHintBeginning": "Trasferisci la paternità del documento", "ChangeOwnerHintEnd": "a un'altra persona.", "ChangeOwnerWarning": "Non potrai modificare questo documento dopo questa azione.", - "SelectOwner": "Seleziona nuovo proprietario", + "SelectOwner": "Seleziona un nuovo autore", "CreateDocument": "Crea nuovo documento", "CreateTemplate": "Crea nuovo modello", "Documents": "Documenti", diff --git a/plugins/controlled-documents-assets/lang/pt.json b/plugins/controlled-documents-assets/lang/pt.json index f229ec3679..35c2963167 100644 --- a/plugins/controlled-documents-assets/lang/pt.json +++ b/plugins/controlled-documents-assets/lang/pt.json @@ -22,11 +22,12 @@ "Minor": "Secundária", "Patch": "Correção", "ValidationWorkflow": "Fluxo de validação", - "ChangeOwner": "Alterar responsável pelo documento", - "ChangeOwnerHintBeginning": "Transferir a propriedade do", + "Creator": "Criador", + "ChangeOwner": "Alterar autor do documento", + "ChangeOwnerHintBeginning": "Transferir autoria do documento", "ChangeOwnerHintEnd": "para outra pessoa.", "ChangeOwnerWarning": "Você não poderá mais editar este documento após essa ação.", - "SelectOwner": "Selecionar novo responsável", + "SelectOwner": "Selecionar novo autor", "CreateDocument": "Criar novo documento", "CreateTemplate": "Criar novo modelo", "Documents": "Documentos", diff --git a/plugins/controlled-documents-assets/lang/ru.json b/plugins/controlled-documents-assets/lang/ru.json index 5e0e832780..664672b938 100644 --- a/plugins/controlled-documents-assets/lang/ru.json +++ b/plugins/controlled-documents-assets/lang/ru.json @@ -22,11 +22,12 @@ "Minor": "Минорная", "Patch": "Патч", "ValidationWorkflow": "Процесс валидации", - "ChangeOwner": "Изменить владельца документа", - "ChangeOwnerHintBeginning": "Передайте права владельца документа", + "Creator": "Создатель", + "ChangeOwner": "Сменить автора документа", + "ChangeOwnerHintBeginning": "Передать авторство документа", "ChangeOwnerHintEnd": "другому лицу.", "ChangeOwnerWarning": "Вы не сможете редактировать документ после этой операции.", - "SelectOwner": "Выберите нового владельца", + "SelectOwner": "Выберите нового автора", "CreateDocument": "Создать документ", "CreateTemplate": "Создать шаблон", "Documents": "Документы", diff --git a/plugins/controlled-documents-assets/lang/zh.json b/plugins/controlled-documents-assets/lang/zh.json index a068e52b05..cdb6ca09dc 100644 --- a/plugins/controlled-documents-assets/lang/zh.json +++ b/plugins/controlled-documents-assets/lang/zh.json @@ -22,11 +22,12 @@ "Minor": "次要", "Patch": "补丁", "ValidationWorkflow": "验证工作流程", - "ChangeOwner": "更改文档所有者", - "ChangeOwnerHintBeginning": "转移所有权", + "Creator": "创建者", + "ChangeOwner": "更改文档作者", + "ChangeOwnerHintBeginning": "转移文档的作者身份", "ChangeOwnerHintEnd": "给其他人。", "ChangeOwnerWarning": "此操作后您将无法编辑此文档。", - "SelectOwner": "选择新所有者", + "SelectOwner": "选择新的作者", "CreateDocument": "创建新文档", "CreateTemplate": "创建新模板", "Documents": "文档", diff --git a/plugins/controlled-documents-resources/src/components/document/right-panel/DocumentApprovalGuideItem.svelte b/plugins/controlled-documents-resources/src/components/document/right-panel/DocumentApprovalGuideItem.svelte index 49a007d959..75f66fb04c 100644 --- a/plugins/controlled-documents-resources/src/components/document/right-panel/DocumentApprovalGuideItem.svelte +++ b/plugins/controlled-documents-resources/src/components/document/right-panel/DocumentApprovalGuideItem.svelte @@ -1,5 +1,5 @@ diff --git a/plugins/controlled-documents-resources/src/components/document/right-panel/DocumentInfoTab.svelte b/plugins/controlled-documents-resources/src/components/document/right-panel/DocumentInfoTab.svelte index add54294cf..6e215f655f 100644 --- a/plugins/controlled-documents-resources/src/components/document/right-panel/DocumentInfoTab.svelte +++ b/plugins/controlled-documents-resources/src/components/document/right-panel/DocumentInfoTab.svelte @@ -143,7 +143,7 @@ - + - + diff --git a/plugins/controlled-documents-resources/src/stores/editors/document/canSendForApproval.ts b/plugins/controlled-documents-resources/src/stores/editors/document/canSendForApproval.ts index da89eedd2f..34d4257aa1 100644 --- a/plugins/controlled-documents-resources/src/stores/editors/document/canSendForApproval.ts +++ b/plugins/controlled-documents-resources/src/stores/editors/document/canSendForApproval.ts @@ -17,7 +17,14 @@ import { ControlledDocumentState, DocumentState } from '@hcengineering/controlle import { TrainingState } from '@hcengineering/training' import { combine } from 'effector' import { $documentComments } from './documentComments' -import { $controlledDocument, $documentState, $isLatestVersion, $reviewRequestHistory, $training } from './editor' +import { + $controlledDocument, + $documentState, + $isDocumentOwner, + $isLatestVersion, + $reviewRequestHistory, + $training +} from './editor' export const $canSendForApproval = combine( $controlledDocument, @@ -26,7 +33,10 @@ export const $canSendForApproval = combine( $documentComments, $training, $reviewRequestHistory, - (document, isLatestVersion, state, comments, training, reviewHistory) => { + $isDocumentOwner, + (document, isLatestVersion, state, comments, training, reviewHistory, isDocumentOwner) => { + if (!isDocumentOwner) return false + let haveBeenReviewedOnce = false if (document !== null) { const reviews = (reviewHistory ?? []).filter((review) => review.attachedTo === document._id) diff --git a/plugins/controlled-documents-resources/src/stores/editors/document/canSendForReview.ts b/plugins/controlled-documents-resources/src/stores/editors/document/canSendForReview.ts index 6203507e2e..b91b107e02 100644 --- a/plugins/controlled-documents-resources/src/stores/editors/document/canSendForReview.ts +++ b/plugins/controlled-documents-resources/src/stores/editors/document/canSendForReview.ts @@ -15,10 +15,11 @@ import { DocumentState } from '@hcengineering/controlled-documents' import { combine } from 'effector' -import { $documentState, $isLatestVersion } from './editor' +import { $documentState, $isDocumentOwner, $isLatestVersion } from './editor' export const $canSendForReview = combine( $isLatestVersion, $documentState, - (isLatestVersion, state) => isLatestVersion && state === DocumentState.Draft + $isDocumentOwner, + (isLatestVersion, state, isDocumentOwner) => isLatestVersion && state === DocumentState.Draft && isDocumentOwner ) diff --git a/plugins/controlled-documents/src/plugin.ts b/plugins/controlled-documents/src/plugin.ts index 4a3add07cc..c473979940 100644 --- a/plugins/controlled-documents/src/plugin.ts +++ b/plugins/controlled-documents/src/plugin.ts @@ -172,6 +172,7 @@ export const documentsPlugin = plugin(documentsId, { Category: '' as IntlString, Author: '' as IntlString, Owner: '' as IntlString, + Creator: '' as IntlString, Status: '' as IntlString, Labels: '' as IntlString, Description: '' as IntlString, diff --git a/plugins/login-resources/src/components/AdminWorkspaces.svelte b/plugins/login-resources/src/components/AdminWorkspaces.svelte index 1df5cf1eb3..fa672951bc 100644 --- a/plugins/login-resources/src/components/AdminWorkspaces.svelte +++ b/plugins/login-resources/src/components/AdminWorkspaces.svelte @@ -80,6 +80,7 @@ let showDeleted: boolean = false let showOther: boolean = true let showGrAttempts: boolean = true + let showSelectedRegionOnly: boolean = false $: sortedWorkspaces = workspaces .filter( @@ -88,6 +89,7 @@ (it.url?.includes(search) ?? false) || it.uuid?.includes(search) || it.createdBy?.includes(search)) && + (showSelectedRegionOnly ? it.region === selectedRegionId : true) && ((showActive && isActiveMode(it.mode)) || (showArchived && isArchivingMode(it.mode)) || (showDeleted && isDeletingMode(it.mode)) || @@ -180,9 +182,11 @@ const dayRanges = { Today: [-1, 1], Week: [1, 7], - Weeks: [7, 30], - Month: [30, 90], - Months: [90, 180], + Weeks: [7, 14], + Month: [14, 30], + Months1: [30, 60], + Months2: [60, 90], + Months3: [90, 180], 'Six Month': [180, 270], 'Nine Months': [270, 365], Years: [365, 10000000] @@ -291,6 +295,10 @@ Show attempts >=0 workspaces: +
+ Show selected region only: + +
diff --git a/qms-tests/sanity/tests/documents/documents.spec.ts b/qms-tests/sanity/tests/documents/documents.spec.ts index 8a1e3fd3ed..d8712e2e1a 100644 --- a/qms-tests/sanity/tests/documents/documents.spec.ts +++ b/qms-tests/sanity/tests/documents/documents.spec.ts @@ -133,13 +133,13 @@ test.describe('QMS. Documents tests', () => { await attachScreenshot('TESTS-125_create_child_document.png', page) }) - test('TESTS-126. Change document owner', async ({ page }) => { - await allure.description('Requirement\nUsers need to change document owner') + test('TESTS-126. Change document author', async ({ page }) => { + await allure.description('Requirement\nUsers need to change document author') await allure.tms('TESTS-126', 'https://front.hc.engineering/workbench/platform/tracker/TESTS-126') const changeDocument: NewDocument = { template: 'HR (HR)', - title: `Change document owner Document-${generateId()}`, - description: `Change document owner Document description-${generateId()}` + title: `Change document author Document-${generateId()}`, + description: `Change document author Document description-${generateId()}` } const documentDetails: DocumentDetails = { type: 'HR', @@ -152,11 +152,11 @@ test.describe('QMS. Documents tests', () => { await prepareDocumentStep(page, changeDocument) const documentContentPage = new DocumentContentPage(page) - await test.step('2. Change document owner', async () => { + await test.step('2. Change document author', async () => { await documentContentPage.checkDocumentTitle(changeDocument.title) await documentContentPage.checkDocument(documentDetails) - await documentContentPage.executeMoreActions('Change document owner') - await documentContentPage.fillChangeDocumentOwnerPopup('Dirak Kainin') + await documentContentPage.executeMoreActions('Change document author') + await documentContentPage.fillChangeDocumentAuthorPopup('Dirak Kainin') }) await test.step('3. Check the updated document information', async () => { @@ -681,7 +681,7 @@ test.describe('QMS. Documents tests', () => { }) }) - test('TESTS-155. Change document owner. QARA user changes owner from one user to another', async ({ + test('TESTS-155. Change document author. QARA user changes owner from one user to another', async ({ page, browser }) => { @@ -691,8 +691,8 @@ test.describe('QMS. Documents tests', () => { const newDocumentOwner = 'Dirak Kainin' const changeQaraDocument: NewDocument = { template: 'HR (HR)', - title: `Change document owner by QARA user Document-${generateId()}`, - description: `Change document owner by QARA user Document description-${generateId()}` + title: `Change document author by QARA user Document-${generateId()}`, + description: `Change document author by QARA user Document description-${generateId()}` } const documentDetails: DocumentDetails = { type: 'HR', @@ -720,8 +720,8 @@ test.describe('QMS. Documents tests', () => { const documentsPageQara = new DocumentsPage(qaraManagerPage) await documentsPageQara.openDocument(changeQaraDocument.title) - await documentContentPageQara.executeMoreActions('Change document owner') - await documentContentPageQara.fillChangeDocumentOwnerPopupByQaraManager(newDocumentOwner) + await documentContentPageQara.executeMoreActions('Change document author') + await documentContentPageQara.fillChangeDocumentAuthorPopupByQaraManager(newDocumentOwner) }) await test.step('4. As QARA manager Check the updated document information', async () => { diff --git a/qms-tests/sanity/tests/model/documents/document-content-page.ts b/qms-tests/sanity/tests/model/documents/document-content-page.ts index f0a99d1921..630b210360 100644 --- a/qms-tests/sanity/tests/model/documents/document-content-page.ts +++ b/qms-tests/sanity/tests/model/documents/document-content-page.ts @@ -14,10 +14,10 @@ export class DocumentContentPage extends DocumentCommonPage { readonly textCategory: Locator readonly textVersion: Locator readonly textStatus: Locator - readonly textOwner: Locator readonly textAuthor: Locator - readonly buttonSelectNewOwner: Locator - readonly buttonSelectNewOwnerChange: Locator + readonly textCreator: Locator + readonly buttonSelectNewAuthor: Locator + readonly buttonSelectNewAuthorChange: Locator readonly buttonSendForReview: Locator readonly buttonSendForApproval: Locator readonly buttonAddMembers: Locator @@ -41,7 +41,7 @@ export class DocumentContentPage extends DocumentCommonPage { readonly buttonDocument: Locator readonly buttonDocumentApprovals: Locator readonly textPageHeader: Locator - readonly buttonSelectNewOwnerChangeByQaraManager: Locator + readonly buttonSelectNewAuthorChangeByQaraManager: Locator readonly textId: Locator readonly contentLocator: Locator readonly addSpaceButton: Locator @@ -113,10 +113,10 @@ export class DocumentContentPage extends DocumentCommonPage { this.textCategory = page.locator('div.flex:has(div.label:text("Category")) div.field') this.textVersion = page.locator('div.flex:has(div.label:text("Version")) div.field') this.textStatus = page.locator('div.flex:has(div.label:text("Status")) div.field') - this.textOwner = page.locator('div.flex:has(div.label:text("Owner")) div.field') this.textAuthor = page.locator('div.flex:has(div.label:text("Author")) div.field') - this.buttonSelectNewOwner = page.locator('div.popup button.small') - this.buttonSelectNewOwnerChange = page.locator('div.popup button.dangerous') + this.textCreator = page.locator('div.flex:has(div.label:text("Creator")) div.field') + this.buttonSelectNewAuthor = page.locator('div.popup button.small') + this.buttonSelectNewAuthorChange = page.locator('div.popup button.dangerous') this.buttonSendForReview = page.locator('div.hulyHeader-buttonsGroup.extra button[type="button"] > span', { hasText: 'Send for review' }) @@ -152,7 +152,7 @@ export class DocumentContentPage extends DocumentCommonPage { this.buttonDocumentInformation = page.locator('button[id$="info"]') this.buttonDocumentApprovals = page.locator('button[id$="approvals"]') this.textPageHeader = page.locator('div.hulyNavPanel-header') - this.buttonSelectNewOwnerChangeByQaraManager = page.locator('div.popup button[type="submit"]') + this.buttonSelectNewAuthorChangeByQaraManager = page.locator('div.popup button[type="submit"]') this.textId = page.locator('div.flex:has(div.label:text("ID")) div.field') this.contentLocator = page.locator('div.textInput div.tiptap') this.addSpaceButton = page.locator('#tree-orgspaces') @@ -707,20 +707,20 @@ export class DocumentContentPage extends DocumentCommonPage { await expect(this.textStatus).toHaveText(data.status) } if (data.owner != null) { - await expect(this.textOwner).toHaveText(data.owner) + await expect(this.textAuthor).toHaveText(data.owner) } if (data.author != null) { - await expect(this.textAuthor).toHaveText(data.author) + await expect(this.textCreator).toHaveText(data.author) } if (data.id != null) { await expect(this.textId).toHaveText(data.id) } } - async fillChangeDocumentOwnerPopup (newOwner: string): Promise { - await this.buttonSelectNewOwner.click() - await this.selectListItemWithSearch(this.page, newOwner) - await this.buttonSelectNewOwnerChange.click() + async fillChangeDocumentAuthorPopup (newAuthor: string): Promise { + await this.buttonSelectNewAuthor.click() + await this.selectListItemWithSearch(this.page, newAuthor) + await this.buttonSelectNewAuthorChange.click() } async fillSelectReviewersForm (reviewers: Array): Promise { @@ -858,9 +858,9 @@ export class DocumentContentPage extends DocumentCommonPage { await this.buttonDocumentApprovals.click({ position: { x: 1, y: 1 }, force: true }) } - async fillChangeDocumentOwnerPopupByQaraManager (newOwner: string): Promise { - await this.buttonSelectNewOwner.click() + async fillChangeDocumentAuthorPopupByQaraManager (newOwner: string): Promise { + await this.buttonSelectNewAuthor.click() await this.selectListItemWithSearch(this.page, newOwner) - await this.buttonSelectNewOwnerChangeByQaraManager.click() + await this.buttonSelectNewAuthorChangeByQaraManager.click() } } diff --git a/server-plugins/controlled-documents-resources/src/index.ts b/server-plugins/controlled-documents-resources/src/index.ts index fa9a38a28d..fec5b12ee6 100644 --- a/server-plugins/controlled-documents-resources/src/index.ts +++ b/server-plugins/controlled-documents-resources/src/index.ts @@ -84,14 +84,6 @@ function updateMeta (doc: ControlledDocument, txFactory: TxFactory): Tx[] { ] } -function updateAuthor (doc: ControlledDocument, txFactory: TxFactory): Tx[] { - return [ - txFactory.createTxUpdateDoc(doc._class, doc.space, doc._id, { - author: doc.owner - }) - ] -} - // TODO: Find a way to avoid duplicate logic and reuse createTrainingRequest() from `training-resources` async function createDocumentTrainingRequest (doc: ControlledDocument, control: TriggerControl): Promise { if (!control.hierarchy.hasMixin(doc, documents.mixin.DocumentTraining)) { @@ -281,7 +273,6 @@ export async function OnDocHasBecomeEffective ( const olderEffective = await getDocsOlderThanDoc(doc, control, [DocumentState.Effective]) result.push( - ...updateAuthor(doc, control.txFactory), ...archiveDocs(olderEffective, control.txFactory), ...updateMeta(doc, control.txFactory), ...updateTemplate(doc, olderEffective, control), diff --git a/server-plugins/notification-resources/src/index.ts b/server-plugins/notification-resources/src/index.ts index f1f4b45ecc..5a101544ef 100644 --- a/server-plugins/notification-resources/src/index.ts +++ b/server-plugins/notification-resources/src/index.ts @@ -18,7 +18,7 @@ import activity, { ActivityMessage, DocUpdateMessage } from '@hcengineering/acti import chunter, { ChatMessage } from '@hcengineering/chunter' import contact, { Employee, type Person } from '@hcengineering/contact' import core, { - PersonId, + AccountUuid, AnyAttribute, ArrOf, AttachedDoc, @@ -30,6 +30,8 @@ import core, { DocumentUpdate, MeasureContext, MixinUpdate, + notEmpty, + PersonId, Ref, RefTo, SortingOrder, @@ -41,11 +43,7 @@ import core, { TxMixin, TxProcessor, TxRemoveDoc, - TxUpdateDoc, - AccountUuid, - notEmpty, - generateId, - toIdMap + TxUpdateDoc } from '@hcengineering/core' import notification, { ActivityInboxNotification, @@ -78,21 +76,21 @@ import { createPullCollaboratorsTx, createPushCollaboratorsTx, getHTMLPresenter, + getNotificationContent, getNotificationLink, getNotificationProviderControl, getObjectSpace, - getTextPresenter, getReceiversInfo, + getSenderInfo, + getTextPresenter, isAllowed, isMixinTx, isShouldNotifyTx, isUserEmployeeInFieldValueTypeMatch, messageToMarkup, - replaceAll, - updateNotifyContextsSpace, type NotificationProviderControl, - getNotificationContent, - getSenderInfo + replaceAll, + updateNotifyContextsSpace } from './utils' import { PushNotificationsHandler } from './push' @@ -564,11 +562,6 @@ async function createNotifyContext ( contextsCache.contexts.set(cacheKey, createTx.objectId) control.cache.set(ContextsCacheKey, contextsCache) await ctx.with('apply', {}, () => control.apply(control.ctx, [createTx])) - control.ctx.contextData.broadcast.targets['docNotifyContext' + createTx._id] = (it) => { - if (it._id === createTx._id) { - return [receiver.account] - } - } return createTx.objectId } @@ -668,12 +661,6 @@ async function updateContextsTimestamp ( }) res.push(updateTx) - - control.ctx.contextData.broadcast.targets['docNotifyContext' + updateTx._id] = (it) => { - if (it._id === updateTx._id) { - return [context.user] - } - } } if (res.length > 0) { @@ -700,12 +687,6 @@ async function removeContexts ( const removeTx = control.txFactory.createTxRemoveDoc(context._class, context.space, context._id) res.push(removeTx) - - control.ctx.contextData.broadcast.targets['docNotifyContext' + removeTx._id] = (it) => { - if (it._id === removeTx._id) { - return [context.user] - } - } } await control.apply(control.ctx, res) @@ -815,13 +796,7 @@ export async function createCollabDocInfo ( docMessages, settings ) - const ids = new Set(targetRes.map((it) => it._id)) - const id = generateId() as string - control.ctx.contextData.broadcast.targets[id] = (it) => { - if (ids.has(it._id)) { - return [receiver.account] - } - } + res = res.concat(targetRes) } return res @@ -1448,41 +1423,6 @@ export async function OnAttributeUpdate (txes: Tx[], control: TriggerControl): P return result } -async function applyUserTxes (ctx: MeasureContext, control: TriggerControl, txes: Tx[]): Promise { - const map: Map = new Map() - const res: Tx[] = [] - - for (const tx of txes) { - const ttx = tx as TxCUD - if ( - control.hierarchy.isDerived(ttx.objectClass, notification.class.InboxNotification) && - ttx._class === core.class.TxCreateDoc - ) { - const notification = TxProcessor.createDoc2Doc(ttx as TxCreateDoc) - - if (map.has(notification.user)) { - map.get(notification.user)?.push(tx) - } else { - map.set(notification.user, [tx]) - } - } else { - res.push(tx) - } - } - - for (const [user, txs] of map.entries()) { - await control.apply(ctx, txs) - const m1 = toIdMap(txs) - control.ctx.contextData.broadcast.targets.docNotifyContext = (it) => { - if (m1.has(it._id)) { - return [user] - } - } - } - - return res -} - async function updateCollaborators ( ctx: MeasureContext, control: TriggerControl, @@ -1565,19 +1505,16 @@ export async function createCollaboratorNotifications ( } if (tx.attachedTo !== undefined && !ignoreCollection) { - const res = await ctx.with('collectionCollabDoc', {}, (ctx) => + return await ctx.with('collectionCollabDoc', {}, (ctx) => collectionCollabDoc(ctx, tx as TxCUD, control, activityMessages, cache, true) ) - return await applyUserTxes(ctx, control, res) } switch (tx._class) { case core.class.TxCreateDoc: { - const res = await ctx.with('createCollaboratorDoc', {}, (ctx) => + return await ctx.with('createCollaboratorDoc', {}, (ctx) => createCollaboratorDoc(ctx, tx as TxCreateDoc, control, activityMessages, cache) ) - - return await applyUserTxes(ctx, control, res) } case core.class.TxUpdateDoc: case core.class.TxMixin: { @@ -1596,7 +1533,7 @@ export async function createCollaboratorNotifications ( ) ) ) - return await applyUserTxes(ctx, control, res) + return res } } diff --git a/server/middleware/package.json b/server/middleware/package.json index 92054c9a27..35d1fa81bb 100644 --- a/server/middleware/package.json +++ b/server/middleware/package.json @@ -42,7 +42,6 @@ "@hcengineering/platform": "^0.6.11", "@hcengineering/server-core": "^0.6.1", "@hcengineering/server-preference": "^0.6.0", - "@hcengineering/server-notification": "^0.6.1", "@hcengineering/query": "^0.6.12", "@hcengineering/analytics": "^0.6.0", "fast-equals": "^5.2.2" diff --git a/server/middleware/src/index.ts b/server/middleware/src/index.ts index 793e4b5509..2b787927c8 100644 --- a/server/middleware/src/index.ts +++ b/server/middleware/src/index.ts @@ -28,7 +28,6 @@ export * from './lookup' export * from './lowLevel' export * from './model' export * from './modified' -export * from './notifications' export * from './private' export * from './queryJoin' export * from './spacePermissions' @@ -37,3 +36,4 @@ export * from './triggers' export * from './txPush' export * from './queue' export * from './identity' +export * from './pluginConfig' diff --git a/server/middleware/src/notifications.ts b/server/middleware/src/notifications.ts deleted file mode 100644 index b20e3d45ab..0000000000 --- a/server/middleware/src/notifications.ts +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright © 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 core, { - Doc, - MeasureContext, - Tx, - TxCUD, - TxProcessor, - type SessionData, - TxApplyIf, - systemAccountUuid, - AccountUuid -} from '@hcengineering/core' -import platform, { PlatformError, Severity, Status } from '@hcengineering/platform' -import { BaseMiddleware, Middleware, TxMiddlewareResult, type PipelineContext } from '@hcengineering/server-core' -import { DOMAIN_USER_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_DOC_NOTIFY } from '@hcengineering/server-notification' - -/** - * @public - */ -export class NotificationsMiddleware extends BaseMiddleware implements Middleware { - private readonly targetDomains = [DOMAIN_USER_NOTIFY, DOMAIN_NOTIFICATION, DOMAIN_DOC_NOTIFY] - - private constructor (context: PipelineContext, next?: Middleware) { - super(context, next) - } - - static async create ( - ctx: MeasureContext, - context: PipelineContext, - next: Middleware | undefined - ): Promise { - return new NotificationsMiddleware(context, next) - } - - isTargetDomain (tx: Tx): boolean { - if (TxProcessor.isExtendsCUD(tx._class)) { - const txCUD = tx as TxCUD - const domain = this.context.hierarchy.getDomain(txCUD.objectClass) - return this.targetDomains.includes(domain) - } - return false - } - - processTx (ctx: MeasureContext, tx: Tx): void { - let target: AccountUuid[] | undefined - if (this.isTargetDomain(tx)) { - const account = ctx.contextData.account - if (!account.socialIds.includes(tx.modifiedBy) && account.uuid !== systemAccountUuid) { - throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) - } - const modifiedByAccount = ctx.contextData.socialStringsToUsers.get(tx.modifiedBy) - target = [account.uuid, systemAccountUuid] - if (modifiedByAccount !== undefined && !target.includes(modifiedByAccount)) { - target.push(modifiedByAccount) - } - ctx.contextData.broadcast.targets['checkDomain' + account.uuid] = (tx) => { - if (this.isTargetDomain(tx)) { - return target - } - } - } - } - - tx (ctx: MeasureContext, txes: Tx[]): Promise { - for (const tx of txes) { - if (this.context.hierarchy.isDerived(tx._class, core.class.TxApplyIf)) { - for (const ttx of (tx as TxApplyIf).txes) { - this.processTx(ctx, ttx) - } - } else { - this.processTx(ctx, tx) - } - } - - return this.provideTx(ctx, txes) - } - - isAvailable (ctx: MeasureContext, doc: Doc): boolean { - const domain = this.context.hierarchy.getDomain(doc._class) - if (!this.targetDomains.includes(domain)) return true - const account = ctx.contextData.account - const socialStrings = account.socialIds - return (doc.createdBy !== undefined && socialStrings.includes(doc.createdBy)) || account.uuid === systemAccountUuid - } -} diff --git a/server/middleware/src/pluginConfig.ts b/server/middleware/src/pluginConfig.ts new file mode 100644 index 0000000000..576cb2c35d --- /dev/null +++ b/server/middleware/src/pluginConfig.ts @@ -0,0 +1,73 @@ +// +// Copyright © 2025 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 { aiBotAccountEmail } from '@hcengineering/ai-bot' +import core, { + AccountRole, + MeasureContext, + Tx, + TxProcessor, + systemAccountUuid, + type Doc, + type SessionData, + type TxApplyIf, + type TxCUD +} from '@hcengineering/core' +import platform, { PlatformError, Severity, Status } from '@hcengineering/platform' +import { BaseMiddleware, Middleware, TxMiddlewareResult, type PipelineContext } from '@hcengineering/server-core' + +/** + * @public + */ +export class PluginConfigurationMiddleware extends BaseMiddleware implements Middleware { + private constructor (context: PipelineContext, next?: Middleware) { + super(context, next) + } + + static async create ( + ctx: MeasureContext, + context: PipelineContext, + next: Middleware | undefined + ): Promise { + return new PluginConfigurationMiddleware(context, next) + } + + tx (ctx: MeasureContext, txes: Tx[]): Promise { + const account = ctx.contextData.account + if (account.uuid === systemAccountUuid || account.fullSocialIds.some((it) => it.value === aiBotAccountEmail)) { + // We pass for system accounts and services. + return this.provideTx(ctx, txes) + } + function checkTx (tx: Tx): void { + if (TxProcessor.isExtendsCUD(tx._class)) { + const cud = tx as TxCUD + if (cud.objectClass === core.class.PluginConfiguration && ctx.contextData.account.role !== AccountRole.Owner) { + throw new PlatformError( + new Status(Severity.ERROR, platform.status.Forbidden, { + account: account.uuid + }) + ) + } + } + } + for (const tx of txes) { + checkTx(tx) + if (tx._class === core.class.TxApplyIf) { + const atx = tx as TxApplyIf + atx.txes.forEach(checkTx) + } + } + return this.provideTx(ctx, txes) + } +} diff --git a/server/server-pipeline/src/pipeline.ts b/server/server-pipeline/src/pipeline.ts index 050c80b075..59351e0de3 100644 --- a/server/server-pipeline/src/pipeline.ts +++ b/server/server-pipeline/src/pipeline.ts @@ -30,7 +30,7 @@ import { MarkDerivedEntryMiddleware, ModelMiddleware, ModifiedMiddleware, - NotificationsMiddleware, + PluginConfigurationMiddleware, PrivateMiddleware, QueryJoinMiddleware, QueueMiddleware, @@ -119,8 +119,8 @@ export function createServerPipeline ( LookupMiddleware.create, IdentityMiddleware.create, ModifiedMiddleware.create, + PluginConfigurationMiddleware.create, PrivateMiddleware.create, - NotificationsMiddleware.create, (ctx: MeasureContext, context: PipelineContext, next?: Middleware) => SpaceSecurityMiddleware.create(opt.adapterSecurity ?? false, ctx, context, next), SpacePermissionsMiddleware.create, diff --git a/services/github/pod-github/src/platform.ts b/services/github/pod-github/src/platform.ts index bddba3fb75..b38b15e5f5 100644 --- a/services/github/pod-github/src/platform.ts +++ b/services/github/pod-github/src/platform.ts @@ -798,6 +798,8 @@ export class PlatformWorker { return this.integrations.map((it) => it.workspace) } + checkedWorkspaces = new Set() + async checkWorkspaceIsActive ( token: string, workspace: WorkspaceUuid @@ -825,7 +827,10 @@ export class PlatformWorker { const lastVisit = (Date.now() - (workspaceInfo.lastVisit ?? 0)) / (3600 * 24 * 1000) // In days if (config.WorkspaceInactivityInterval > 0 && lastVisit > config.WorkspaceInactivityInterval) { - this.ctx.warn('Workspace is inactive for too long, skipping for now.', { workspace }) + if (!this.checkedWorkspaces.has(workspace)) { + this.checkedWorkspaces.add(workspace) + this.ctx.warn('Workspace is inactive for too long, skipping for now.', { workspace }) + } return { workspaceInfo: undefined, needRecheck: true } } return { workspaceInfo, needRecheck: true }