diff --git a/README.md b/README.md index 9cc2128e5e..82a8ce8cc7 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,8 @@ rush docker:up # Will set up all the containers Be aware `rush docker:build` will automatically execute all required phases like build, bundle, package. +> **Note:** For resource-constrained machines, you can use the minified variants `rush docker:min` and `rush docker:up:min` to build and run only the required services (excludes hulypulse, redis, process, backup, rating, preview, link-preview, elastic, fulltext, payment, stats, print, sign, hulygun, hulykvs). + Alternatively, you can just execute: ```bash @@ -204,7 +206,10 @@ sh ./scripts/build.sh By default, Docker volumes named dev_db, dev_elastic, and dev_files will be created for the MongoDB, Elasticsearch, and MinIO instances. -Add the following line to your /etc/hosts file +Add the following lines to your hosts file: + +- **macOS / Linux:** `/etc/hosts` +- **Windows:** `C:\Windows\System32\drivers\etc\hosts` ```plain 127.0.0.1 huly.local diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json index ca0c052aa3..0789bed6fc 100644 --- a/common/config/rush/command-line.json +++ b/common/config/rush/command-line.json @@ -243,7 +243,7 @@ "commandKind": "global", "name": "docker", "summary": "Build docker with platform", - "description": "use to build all docker containers required for platform", + "description": "Build all docker containers required for platform. Use --minified for resource-constrained dev (or rush docker:min).", "safeForSimultaneousRushProcesses": true, "shellCommand": "./common/scripts/docker.sh" }, @@ -271,6 +271,22 @@ "safeForSimultaneousRushProcesses": true, "shellCommand": "cd ./dev && docker compose up -d --force-recreate transactor_cockroach" }, + { + "commandKind": "global", + "name": "docker:min", + "summary": "Build minified docker images for resource-constrained dev", + "description": "Build docker images excluding optional services (hulypulse, redis, process, backup, rating, preview, link-preview, elastic, fulltext, payment, stats, print, sign, hulygun, hulykvs). Use with rush docker:up:min", + "safeForSimultaneousRushProcesses": true, + "shellCommand": "./common/scripts/docker.sh --minified" + }, + { + "commandKind": "global", + "name": "docker:up:min", + "summary": "Up minified development stack", + "description": "Start minified docker compose (docker-compose.yaml + docker-compose.min.yaml) for resource-constrained machines", + "safeForSimultaneousRushProcesses": true, + "shellCommand": "cd ./dev && docker compose -f docker-compose.yaml -f docker-compose.min.yaml up -d --force-recreate" + }, { "commandKind": "global", "name": "docker:local", diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 52193c56b7..e0b7ecbd40 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -165,6 +165,12 @@ importers: '@hcengineering/controlled-documents-resources': specifier: workspace:^0.7.0 version: link:../plugins/controlled-documents-resources + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../plugins/converter + '@hcengineering/converter-resources': + specifier: workspace:^0.7.0 + version: link:../plugins/converter-resources '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../foundations/core/packages/core @@ -1216,6 +1222,12 @@ importers: '@hcengineering/controlled-documents-resources': specifier: workspace:^0.7.0 version: link:../../plugins/controlled-documents-resources + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../../plugins/converter + '@hcengineering/converter-resources': + specifier: workspace:^0.7.0 + version: link:../../plugins/converter-resources '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core @@ -5962,6 +5974,9 @@ importers: '@hcengineering/model-controlled-documents': specifier: workspace:^0.7.0 version: link:../controlled-documents + '@hcengineering/model-converter': + specifier: workspace:^0.7.0 + version: link:../converter '@hcengineering/model-core': specifier: workspace:^0.7.0 version: link:../core @@ -6824,6 +6839,9 @@ importers: '@hcengineering/contact': specifier: workspace:^0.7.0 version: link:../../plugins/contact + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../../plugins/converter '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core @@ -6839,6 +6857,9 @@ importers: '@hcengineering/model-guest': specifier: workspace:^0.7.0 version: link:../guest + '@hcengineering/model-notification': + specifier: workspace:^0.7.0 + version: link:../notification '@hcengineering/model-preference': specifier: workspace:^0.7.0 version: link:../preference @@ -6854,6 +6875,9 @@ importers: '@hcengineering/model-workbench': specifier: workspace:^0.7.0 version: link:../workbench + '@hcengineering/notification': + specifier: workspace:^0.7.0 + version: link:../../plugins/notification '@hcengineering/platform': specifier: workspace:^0.7.19 version: link:../../foundations/core/packages/platform @@ -7345,6 +7369,9 @@ importers: '@hcengineering/controlled-documents-resources': specifier: workspace:^0.7.0 version: link:../../plugins/controlled-documents-resources + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../../plugins/converter '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core @@ -7467,6 +7494,67 @@ importers: specifier: ^5.9.3 version: 5.9.3 + ../../models/converter: + dependencies: + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../../plugins/converter + '@hcengineering/core': + specifier: workspace:^0.7.24 + version: link:../../foundations/core/packages/core + '@hcengineering/model': + specifier: workspace:^0.7.17 + version: link:../../foundations/core/packages/model + '@hcengineering/model-core': + specifier: workspace:^0.7.0 + version: link:../core + '@hcengineering/platform': + specifier: workspace:^0.7.19 + version: link:../../foundations/core/packages/platform + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.19 + version: link:../../foundations/utils/packages/platform-rig + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@types/node': + specifier: ^22.18.1 + version: 22.19.0 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.19.0)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.0))(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + ../../models/core: dependencies: '@hcengineering/collaboration': @@ -7985,9 +8073,18 @@ importers: '@hcengineering/model-core': specifier: workspace:^0.7.0 version: link:../core + '@hcengineering/model-notification': + specifier: workspace:^0.7.0 + version: link:../notification '@hcengineering/model-presentation': specifier: workspace:^0.7.0 version: link:../presentation + '@hcengineering/model-view': + specifier: workspace:^0.7.0 + version: link:../view + '@hcengineering/notification': + specifier: workspace:^0.7.0 + version: link:../../plugins/notification '@hcengineering/platform': specifier: workspace:^0.7.19 version: link:../../foundations/core/packages/platform @@ -7997,6 +8094,9 @@ importers: '@hcengineering/ui': specifier: workspace:^0.7.0 version: link:../../packages/ui + '@hcengineering/view': + specifier: workspace:^0.7.0 + version: link:../../plugins/view '@hcengineering/workbench': specifier: workspace:^0.7.0 version: link:../../plugins/workbench @@ -10331,6 +10431,9 @@ importers: '@hcengineering/server-core': specifier: workspace:^0.7.18 version: link:../../foundations/server/packages/core + '@hcengineering/server-notification': + specifier: workspace:^0.7.0 + version: link:../../server-plugins/notification devDependencies: '@hcengineering/platform-rig': specifier: workspace:^0.7.19 @@ -13252,6 +13355,9 @@ importers: '@hcengineering/contact': specifier: workspace:^0.7.0 version: link:../../plugins/contact + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../../plugins/converter '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core @@ -14873,12 +14979,6 @@ importers: dompurify: specifier: ^3.1.6 version: 3.3.0 - emojibase: - specifier: ^16.0.0 - version: 16.0.0 - emojibase-regex: - specifier: ^16.0.0 - version: 16.0.0 fast-equals: specifier: ^5.2.2 version: 5.3.2 @@ -17359,6 +17459,12 @@ importers: '@hcengineering/contact-resources': specifier: workspace:^0.7.0 version: link:../contact-resources + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../converter + '@hcengineering/converter-resources': + specifier: workspace:^0.7.0 + version: link:../converter-resources '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core @@ -18581,6 +18687,9 @@ importers: '@hcengineering/contact': specifier: workspace:^0.7.0 version: link:../contact + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../converter '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core @@ -18739,6 +18848,12 @@ importers: '@hcengineering/controlled-documents': specifier: workspace:^0.7.0 version: link:../controlled-documents + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../converter + '@hcengineering/converter-resources': + specifier: workspace:^0.7.0 + version: link:../converter-resources '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core @@ -18888,6 +19003,158 @@ importers: specifier: ^5.9.3 version: 5.9.3 + ../../plugins/converter: + dependencies: + '@hcengineering/core': + specifier: workspace:^0.7.24 + version: link:../../foundations/core/packages/core + '@hcengineering/platform': + specifier: workspace:^0.7.19 + version: link:../../foundations/core/packages/platform + '@hcengineering/presentation': + specifier: workspace:^0.7.0 + version: link:../../packages/presentation + '@hcengineering/ui': + specifier: workspace:^0.7.0 + version: link:../../packages/ui + '@hcengineering/view': + specifier: workspace:^0.7.0 + version: link:../view + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.19 + version: link:../../foundations/utils/packages/platform-rig + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.19.0)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@29.7.0)(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + ../../plugins/converter-resources: + dependencies: + '@hcengineering/contact': + specifier: workspace:^0.7.0 + version: link:../contact + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../converter + '@hcengineering/core': + specifier: workspace:^0.7.24 + version: link:../../foundations/core/packages/core + '@hcengineering/platform': + specifier: workspace:^0.7.19 + version: link:../../foundations/core/packages/platform + '@hcengineering/presentation': + specifier: workspace:^0.7.0 + version: link:../../packages/presentation + '@hcengineering/theme': + specifier: workspace:^0.7.0 + version: link:../../packages/theme + '@hcengineering/ui': + specifier: workspace:^0.7.0 + version: link:../../packages/ui + '@hcengineering/view': + specifier: workspace:^0.7.0 + version: link:../view + '@hcengineering/view-resources': + specifier: workspace:^0.7.0 + version: link:../view-resources + svelte: + specifier: ^4.2.20 + version: 4.2.20 + devDependencies: + '@hcengineering/platform-rig': + specifier: workspace:^0.7.19 + version: link:../../foundations/utils/packages/platform-rig + '@types/jest': + specifier: ^29.5.5 + version: 29.5.14 + '@typescript-eslint/eslint-plugin': + specifier: ^6.21.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^6.21.0 + version: 6.21.0(eslint@8.57.1)(typescript@5.9.3) + eslint: + specifier: ^8.54.0 + version: 8.57.1 + eslint-config-standard-with-typescript: + specifier: ^40.0.0 + version: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: + specifier: ^2.26.0 + version: 2.32.0(eslint@8.57.1) + eslint-plugin-n: + specifier: ^15.4.0 + version: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: + specifier: ^6.1.1 + version: 6.6.0(eslint@8.57.1) + eslint-plugin-svelte: + specifier: ^2.35.1 + version: 2.46.1(eslint@8.57.1)(svelte@4.2.20)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3)) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.19.0)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3)) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + prettier-plugin-svelte: + specifier: ^3.4.0 + version: 3.4.0(prettier@3.6.2)(svelte@4.2.20) + sass: + specifier: ^1.80.0 + version: 1.93.3 + svelte-check: + specifier: ^3.6.9 + version: 3.8.6(@babel/core@7.28.5)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.3)(svelte@4.2.20) + svelte-eslint-parser: + specifier: ^0.33.1 + version: 0.33.1(svelte@4.2.20) + svelte-loader: + specifier: ^3.2.0 + version: 3.2.4(svelte@4.2.20) + svelte-preprocess: + specifier: ^5.1.4 + version: 5.1.4(@babel/core@7.28.5)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.3)(svelte@4.2.20)(typescript@5.9.3) + ts-jest: + specifier: ^29.1.1 + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.0)(ts-node@10.9.2(@swc/core@1.15.1)(@types/node@22.19.0)(typescript@5.9.3)))(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + ../../plugins/desktop-downloads: dependencies: '@hcengineering/core': @@ -20341,6 +20608,9 @@ importers: '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core + '@hcengineering/notification': + specifier: workspace:^0.7.0 + version: link:../notification '@hcengineering/platform': specifier: workspace:^0.7.19 version: link:../../foundations/core/packages/platform @@ -20457,6 +20727,12 @@ importers: '@hcengineering/login': specifier: workspace:^0.7.0 version: link:../login + '@hcengineering/notification': + specifier: workspace:^0.7.0 + version: link:../notification + '@hcengineering/panel': + specifier: workspace:^0.7.0 + version: link:../../packages/panel '@hcengineering/platform': specifier: workspace:^0.7.19 version: link:../../foundations/core/packages/platform @@ -20469,6 +20745,12 @@ importers: '@hcengineering/ui': specifier: workspace:^0.7.0 version: link:../../packages/ui + '@hcengineering/view': + specifier: workspace:^0.7.0 + version: link:../view + '@hcengineering/view-resources': + specifier: workspace:^0.7.0 + version: link:../view-resources svelte: specifier: ^4.2.20 version: 4.2.20 @@ -24411,6 +24693,9 @@ importers: ../../plugins/process-resources: dependencies: + '@hcengineering/account-client': + specifier: workspace:^0.7.21 + version: link:../../foundations/core/packages/account-client '@hcengineering/analytics': specifier: workspace:^0.7.17 version: link:../../foundations/core/packages/analytics @@ -24432,6 +24717,9 @@ importers: '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core + '@hcengineering/login': + specifier: workspace:^0.7.0 + version: link:../login '@hcengineering/platform': specifier: workspace:^0.7.19 version: link:../../foundations/core/packages/platform @@ -27991,6 +28279,9 @@ importers: '@hcengineering/contact': specifier: workspace:^0.7.0 version: link:../contact + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../converter '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core @@ -28666,6 +28957,12 @@ importers: '@hcengineering/contact-resources': specifier: workspace:^0.7.0 version: link:../contact-resources + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../converter + '@hcengineering/converter-resources': + specifier: workspace:^0.7.0 + version: link:../converter-resources '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core @@ -29377,6 +29674,9 @@ importers: '@hcengineering/contact': specifier: workspace:^0.7.0 version: link:../contact + '@hcengineering/converter': + specifier: workspace:^0.7.0 + version: link:../converter '@hcengineering/core': specifier: workspace:^0.7.24 version: link:../../foundations/core/packages/core @@ -31955,6 +32255,9 @@ importers: '@hcengineering/server-core': specifier: workspace:^0.7.18 version: link:../../foundations/server/packages/core + '@hcengineering/server-notification': + specifier: workspace:^0.7.0 + version: link:../notification devDependencies: '@hcengineering/platform-rig': specifier: workspace:^0.7.19 @@ -32034,6 +32337,9 @@ importers: '@hcengineering/view': specifier: workspace:^0.7.0 version: link:../../plugins/view + '@hcengineering/workbench': + specifier: workspace:^0.7.0 + version: link:../../plugins/workbench devDependencies: '@hcengineering/platform-rig': specifier: workspace:^0.7.19 @@ -36269,6 +36575,12 @@ importers: '@hcengineering/drive-assets': specifier: workspace:^0.7.0 version: link:../../plugins/drive-assets + '@hcengineering/export': + specifier: workspace:^0.7.0 + version: link:../../plugins/export + '@hcengineering/export-assets': + specifier: workspace:^0.7.0 + version: link:../../plugins/export-assets '@hcengineering/github': specifier: workspace:^0.7.0 version: link:../../services/github/github @@ -61453,7 +61765,7 @@ snapshots: node-loader@2.0.0(webpack@5.102.1): dependencies: loader-utils: 2.0.4 - webpack: 5.102.1(@swc/core@1.15.1)(esbuild@0.25.12)(webpack-cli@5.1.4) + webpack: 5.102.1 node-localstorage@2.2.1: dependencies: @@ -63500,6 +63812,15 @@ snapshots: optionalDependencies: '@swc/core': 1.15.1 + terser-webpack-plugin@5.3.14(webpack@5.102.1): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.44.1 + webpack: 5.102.1 + terser@5.44.1: dependencies: '@jridgewell/source-map': 0.3.11 @@ -64168,6 +64489,38 @@ snapshots: webpack-sources@3.3.3: {} + webpack@5.102.1: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.28.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.14(webpack@5.102.1) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + webpack@5.102.1(@swc/core@1.15.1): dependencies: '@types/eslint-scope': 3.7.7 diff --git a/common/scripts/docker.sh b/common/scripts/docker.sh index 5263a290ad..3e550a1c5d 100755 --- a/common/scripts/docker.sh +++ b/common/scripts/docker.sh @@ -1,31 +1,61 @@ -rush docker:build -p 20 \ ---to @hcengineering/pod-server \ ---to @hcengineering/pod-front \ ---to @hcengineering/prod \ ---to @hcengineering/pod-account \ ---to @hcengineering/pod-workspace \ ---to @hcengineering/pod-collaborator \ ---to @hcengineering/tool \ ---to @hcengineering/pod-print \ ---to @hcengineering/pod-sign \ ---to @hcengineering/pod-analytics-collector \ ---to @hcengineering/rekoni-service \ ---to @hcengineering/pod-ai-bot \ ---to @hcengineering/import-tool \ ---to @hcengineering/pod-stats \ ---to @hcengineering/pod-fulltext \ ---to @hcengineering/pod-love \ ---to @hcengineering/pod-mail \ ---to @hcengineering/pod-datalake \ ---to @hcengineering/pod-mail-worker \ ---to @hcengineering/pod-export \ ---to @hcengineering/pod-media \ ---to @hcengineering/pod-preview \ ---to @hcengineering/pod-link-preview \ ---to @hcengineering/pod-external \ ---to @hcengineering/pod-backup \ ---to @hcengineering/backup-api-pod \ ---to @hcengineering/pod-billing \ ---to @hcengineering/pod-process \ ---to @hcengineering/pod-rating \ ---to @hcengineering/pod-payment +#!/bin/bash +# Supports minified mode for resource-constrained dev machines: +# rush docker --minified or rush docker:min + +MINIFIED=false +for arg in "$@"; do + if [ "$arg" = "--minified" ]; then + MINIFIED=true + break + fi +done + +if [ "$MINIFIED" = true ]; then + echo "Building minified docker images (excluding optional services)..." + rush docker:build -p 20 \ + --to @hcengineering/pod-server \ + --to @hcengineering/pod-front \ + --to @hcengineering/prod \ + --to @hcengineering/pod-account \ + --to @hcengineering/pod-workspace \ + --to @hcengineering/pod-collaborator \ + --to @hcengineering/tool \ + --to @hcengineering/pod-analytics-collector \ + --to @hcengineering/rekoni-service \ + --to @hcengineering/pod-datalake \ + --to @hcengineering/pod-export \ + --to @hcengineering/pod-media \ + --to @hcengineering/pod-external +else + rush docker:build -p 20 \ + --to @hcengineering/pod-server \ + --to @hcengineering/pod-front \ + --to @hcengineering/prod \ + --to @hcengineering/pod-account \ + --to @hcengineering/pod-workspace \ + --to @hcengineering/pod-collaborator \ + --to @hcengineering/tool \ + --to @hcengineering/pod-print \ + --to @hcengineering/pod-sign \ + --to @hcengineering/pod-analytics-collector \ + --to @hcengineering/rekoni-service \ + --to @hcengineering/pod-ai-bot \ + --to @hcengineering/import-tool \ + --to @hcengineering/pod-stats \ + --to @hcengineering/pod-fulltext \ + --to @hcengineering/pod-love \ + --to @hcengineering/pod-mail \ + --to @hcengineering/pod-datalake \ + --to @hcengineering/pod-mail-worker \ + --to @hcengineering/pod-export \ + --to @hcengineering/pod-media \ + --to @hcengineering/pod-preview \ + --to @hcengineering/pod-link-preview \ + --to @hcengineering/pod-external \ + --to @hcengineering/pod-backup \ + --to @hcengineering/backup-api-pod \ + --to @hcengineering/pod-billing \ + --to @hcengineering/pod-process \ + --to @hcengineering/pod-rating \ + --to @hcengineering/pod-payment +fi diff --git a/desktop/package.json b/desktop/package.json index 2a89a1e6ea..7fe030affb 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -88,6 +88,8 @@ "@hcengineering/view": "workspace:^0.7.0", "@hcengineering/view-assets": "workspace:^0.7.0", "@hcengineering/view-resources": "workspace:^0.7.0", + "@hcengineering/converter": "workspace:^0.7.0", + "@hcengineering/converter-resources": "workspace:^0.7.0", "@hcengineering/contact": "workspace:^0.7.0", "@hcengineering/contact-resources": "workspace:^0.7.0", "@hcengineering/task": "workspace:^0.7.0", diff --git a/desktop/src/ui/platform.ts b/desktop/src/ui/platform.ts index 62c74178cb..b4a2bbd8e8 100644 --- a/desktop/src/ui/platform.ts +++ b/desktop/src/ui/platform.ts @@ -33,6 +33,7 @@ import { cardId } from '@hcengineering/card' import { chunterId } from '@hcengineering/chunter' import client, { clientId } from '@hcengineering/client' import contactPlugin, { contactId } from '@hcengineering/contact' +import { converterId } from '@hcengineering/converter' import { documentsId } from '@hcengineering/controlled-documents' import { desktopPreferencesId } from '@hcengineering/desktop-preferences' import { desktopDownloadsId } from '@hcengineering/desktop-downloads' @@ -411,7 +412,7 @@ export async function configurePlatform (onWorkbenchConnect?: () => Promise l.trim()) - : ['en', 'ru', 'es', 'pt', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'tr'] + : ['en', 'ru', 'es', 'pt', 'pt-br', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'tr'] setMetadata(uiPlugin.metadata.Languages, languages) @@ -435,6 +436,7 @@ export async function configurePlatform (onWorkbenchConnect?: () => Promise await import('@hcengineering/onboard-resources')) addLocation(workbenchId, async () => await import('@hcengineering/workbench-resources')) addLocation(viewId, async () => await import('@hcengineering/view-resources')) + addLocation(converterId, async () => await import('@hcengineering/converter-resources')) addLocation(taskId, async () => await import('@hcengineering/task-resources')) addLocation(contactId, async () => await import('@hcengineering/contact-resources')) addLocation(chunterId, async () => await import('@hcengineering/chunter-resources')) diff --git a/dev/docker-compose.min.yaml b/dev/docker-compose.min.yaml index 43dc5d7055..637f5d41c4 100644 --- a/dev/docker-compose.min.yaml +++ b/dev/docker-compose.min.yaml @@ -1,326 +1,69 @@ +# Min override: excludes optional services from min deployment. +# Use with: docker compose -f docker-compose.yaml -f docker-compose.min.yaml up +# (rush docker:up:min) +# Excluded: preview, link-preview, elastic, redis, stats, payment, fulltext_cockroach, +# print, sign, hulykvs, hulygun, hulypulse, process-service, backup-cockroach, +# backup-api, rating_cockroach +# Overrides below remove dependencies on excluded services so the min project validates. + services: - cockroach: - image: cockroachdb/cockroach:latest-v24.3 - extra_hosts: - - 'huly.local:host-gateway' - ports: - - '26257:26257' - - '8089:8080' - command: start-single-node --insecure - volumes: - - cockroach_db:/cockroach/cockroach-data - restart: unless-stopped - redpanda: - image: docker.redpanda.com/redpandadata/redpanda:v24.3.6 - extra_hosts: - - 'huly.local:host-gateway' - command: - - redpanda - - start - - --kafka-addr internal://0.0.0.0:9092,external://0.0.0.0:19092 - - --advertise-kafka-addr internal://redpanda:9092,external://localhost:19092 - - --pandaproxy-addr internal://0.0.0.0:8082,external://0.0.0.0:18082 - - --advertise-pandaproxy-addr internal://redpanda:8082,external://localhost:18082 - - --schema-registry-addr internal://0.0.0.0:8081,external://0.0.0.0:18081 - - --rpc-addr redpanda:33145 - - --advertise-rpc-addr redpanda:33145 - - --mode dev-container - - --smp 1 - - --default-log-level=info - container_name: redpanda - volumes: - - redpanda:/var/lib/redpanda/data - ports: - - 18081:18081 - - 18082:18082 - - 19092:19092 - - 19644:9644 - healthcheck: - test: ['CMD', 'rpk', 'cluster', 'info', '-X', 'user=superuser', '-X', 'pass=secretpassword'] - interval: 10s - timeout: 5s - retries: 10 - minio: - image: 'minio/minio' - command: server /data --address ":9000" --console-address ":9001" - extra_hosts: - - 'huly.local:host-gateway' - expose: - - 9000 - - 9001 - ports: - - 9000:9000 - - 9001:9001 - volumes: - - files:/data - healthcheck: - test: ["CMD", "mc", "ready", "local"] - interval: 5s - retries: 10 - restart: unless-stopped + preview: + profiles: ["full"] + link-preview: + profiles: ["full"] elastic: - image: 'elasticsearch:7.14.2' - expose: - - 9200 - extra_hosts: - - 'huly.local:host-gateway' - volumes: - - elastic:/usr/share/elasticsearch/data - ports: - - 9200:9200 - environment: - - ELASTICSEARCH_PORT_NUMBER=9200 - - BITNAMI_DEBUG=true - - discovery.type=single-node - - ES_JAVA_OPTS=-Xms1024m -Xmx1024m - healthcheck: - interval: 20s - retries: 10 - test: curl -s http://localhost:9200/_cluster/health | grep -vq '"status":"red"' - restart: unless-stopped - account: - image: hardcoreeng/account - extra_hosts: - - 'huly.local:host-gateway' - links: - - cockroach - - minio - - stats - ports: - - 3000:3000 - volumes: - - ./branding.json:/var/cfg/branding.json - environment: - - ACCOUNT_PORT=3000 - - QUEUE_CONFIG=${QUEUE_CONFIG} - - SERVER_SECRET=secret - - ADMIN_EMAILS=admin,${PLATFORM_ADMIN_EMAILS} - - STATS_URL=http://huly.local:4900 - - WORKSPACE_LIMIT_PER_USER=10000 - - DB_URL=${DB_CR_URL} - # - DB_URL=${MONGO_URL} - # - DB_NS=account-2 - # Pass only one region to disallow selection for new workspaces. - - REGION_INFO=cockroach|CockroachDB - - TRANSACTOR_URL=ws://huly.local:3333,ws://huly.local:3332;;cockroach, - - MAIL_URL= - - STORAGE_CONFIG=${STORAGE_CONFIG} - - FRONT_URL=http://huly.local:8087 - - MODEL_ENABLED=* - - LAST_NAME_FIRST=true - # - WS_LIVENESS_DAYS=1 - - ACCOUNTS_URL=http://huly.local:3000 - - BRANDING_PATH=/var/cfg/branding.json - # - DISABLE_SIGNUP=true - restart: unless-stopped + profiles: ["full"] + redis: + profiles: ["full"] stats: - image: hardcoreeng/stats - extra_hosts: - - 'huly.local:host-gateway' - ports: - - 4900:4900 - environment: - - PORT=4900 - - SERVER_SECRET=secret - restart: unless-stopped + profiles: ["full"] payment: - image: hardcoreeng/payment - extra_hosts: - - 'huly.local:host-gateway' - ports: - - 3040:3040 - environment: - - SECRET=secret - - PORT=3040 - - ACCOUNTS_URL=http://huly.local:3000 - - FRONT_URL=http://huly.local:8087 - - USE_SANDBOX=true - - POLAR_ACCESS_TOKEN=${POLAR_ACCESS_TOKEN} - - POLAR_WEBHOOK_SECRET=${POLAR_WEBHOOK_SECRET} - - POLAR_SUBSCRIPTION_PLANS=${POLAR_SUBSCRIPTION_PLANS} - restart: unless-stopped + profiles: ["full"] + fulltext_cockroach: + profiles: ["full"] + print: + profiles: ["full"] + sign: + profiles: ["full"] + hulykvs: + profiles: ["full"] + hulygun: + profiles: ["full"] + hulypulse: + profiles: ["full"] + process-service: + profiles: ["full"] + backup-cockroach: + profiles: ["full"] + backup-api: + profiles: ["full"] + rating_cockroach: + profiles: ["full"] + account: + links: !override [] workspace_cockroach: - image: hardcoreeng/workspace - extra_hosts: - - 'huly.local:host-gateway' - links: + links: !override - cockroach - minio - - stats - volumes: - - ./branding.json:/var/cfg/branding.json - environment: - - WS_OPERATION=all+backup - - REGION=cockroach - - SERVER_SECRET=secret - - QUEUE_CONFIG=${QUEUE_CONFIG} - - DB_URL=${DB_CR_URL} - - STATS_URL=http://huly.local:4900 - - STORAGE_CONFIG=${STORAGE_CONFIG} - - MODEL_ENABLED=* - - ACCOUNTS_URL=http://huly.local:3000 - - ACCOUNTS_DB_URL=${DB_CR_URL} - - BRANDING_PATH=/var/cfg/branding.json - - BACKUP_STORAGE=${BACKUP_STORAGE_CONFIG} - - BACKUP_BUCKET=${BACKUP_BUCKET_NAME} - - MAIL_URL= - - INIT_WORKSPACE=staging-dev - restart: unless-stopped collaborator: - image: hardcoreeng/collaborator - extra_hosts: - - 'huly.local:host-gateway' - links: - - cockroach + links: !override - minio - transactor_cockroach - - stats - ports: - - 3078:3078 - environment: - - COLLABORATOR_PORT=3078 - - SECRET=secret - - ACCOUNTS_URL=http://huly.local:3000 - - STORAGE_CONFIG=${STORAGE_CONFIG} - - STATS_URL=http://huly.local:4900 - restart: unless-stopped front: - image: hardcoreeng/front - extra_hosts: - - 'huly.local:host-gateway' - links: - - cockroach - - minio - - elastic - - transactor_cockroach - - collaborator - - stats - ports: - - 8087:8080 - - 8088:8080 - environment: - - SERVER_PORT=8080 - - SERVER_SECRET=secret - - ACCOUNTS_URL=http://huly.local:3000 - - STATS_URL=http://huly.local:4900 - - UPLOAD_URL=/files - - GMAIL_URL=http://huly.local:8088 - - CALENDAR_URL=http://huly.local:8095 - - TELEGRAM_URL=http://huly.local:8086 - - REKONI_URL=http://huly.local:4004 - - COLLABORATOR_URL=ws://huly.local:3078 - - STORAGE_CONFIG=${STORAGE_CONFIG} - - GITHUB_URL=http://huly.local:3500 - - PRINT_URL=http://huly.local:4005 - - SIGN_URL=http://huly.local:4006 -# - ANALYTICS_COLLECTOR_URL=http://huly.local:4017 - - DESKTOP_UPDATES_URL=https://dist.huly.io - - DESKTOP_UPDATES_CHANNEL=dev - - DESKTOP_UPDATES_CHANNELS=dev;tracex:dev-tracex - - BRANDING_URL=http://huly.local:8087/branding.json - - STREAM_URL=http://huly.local:1080/recording - - PAYMENT_URL=http://huly.local:3040 - # - DISABLE_SIGNUP=true - restart: unless-stopped + links: !override [] transactor_cockroach: - image: hardcoreeng/transactor - extra_hosts: - - 'huly.local:host-gateway' - links: + links: !override - cockroach - minio - account - - stats - ports: - - 3332:3332 - volumes: - - ./branding.json:/var/cfg/branding.json - environment: - - QUEUE_CONFIG=${QUEUE_CONFIG} - - SERVER_PORT=3332 - - REGION=cockroach - - SERVER_SECRET=secret - - ENABLE_COMPRESSION=true - - FULLTEXT_URL=http://huly.local:4702 - - STATS_URL=http://huly.local:4900 - - DB_URL=${DB_CR_URL} - - METRICS_CONSOLE=false - - METRICS_FILE=metrics.txt - - STORAGE_CONFIG=${STORAGE_CONFIG} - - FRONT_URL=http://huly.local:8087 - - MAIL_URL='' - - ACCOUNTS_URL=http://huly.local:3000 - - LAST_NAME_FIRST=true - - BRANDING_PATH=/var/cfg/branding.json - - AI_BOT_URL=http://huly.local:4010 - - COMMUNICATION_TIME_LOGGING_ENABLED=true - restart: unless-stopped - fulltext_cockroach: - image: hardcoreeng/fulltext - extra_hosts: - - 'huly.local:host-gateway' - restart: unless-stopped - links: - - elastic - - cockroach - ports: - - 4702:4702 - environment: - - PORT=4702 - - REGION=cockroach - - SERVER_SECRET=secret - - QUEUE_CONFIG=${QUEUE_CONFIG} - - DB_URL=${DB_CR_URL} - - FULLTEXT_DB_URL=http://huly.local:9200 - - ELASTIC_INDEX_NAME=local_storage_index # Same index for simplicity - - STORAGE_CONFIG=${STORAGE_CONFIG} - - STATS_URL=http://huly.local:4900 - - REKONI_URL=http://huly.local:4004 - - ACCOUNTS_URL=http://huly.local:3000 + export: + links: !override + - minio datalake: - image: hardcoreeng/datalake - extra_hosts: - - 'huly.local:host-gateway' - depends_on: + depends_on: !override minio: condition: service_healthy cockroach: condition: service_started - stats: - condition: service_started account: condition: service_started - ports: - - 4030:4030 - environment: - - PORT=4030 - - SECRET=secret - - ACCOUNTS_URL=http://huly.local:3000 - - STATS_URL=http://huly.local:4900 - - STREAM_URL=http://huly.local:1080/recording - - DB_URL=${DB_CR_URL} - - BUCKETS=blobs,eu|http://minio:9000?accessKey=minioadmin&secretKey=minioadmin - restart: unless-stopped - redis: - image: redis:8.0.2-alpine3.21 - ports: - - 6379:6379 - restart: unless-stopped - hulypulse: - image: hardcoreeng/hulypulse - depends_on: - redis: - condition: service_started - ports: - - 8099:8099 - environment: - - HULY_REDIS_URLS=redis://redis:6379 - - HULY_BIND_PORT=8099 - restart: unless-stopped -volumes: - db: - dbpg: - files: - elastic: - cockroach_db: - redpanda: diff --git a/dev/prod/package.json b/dev/prod/package.json index 5e83bdde1b..be3d41825c 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -265,6 +265,8 @@ "@hcengineering/view": "workspace:^0.7.0", "@hcengineering/view-assets": "workspace:^0.7.0", "@hcengineering/view-resources": "workspace:^0.7.0", + "@hcengineering/converter": "workspace:^0.7.0", + "@hcengineering/converter-resources": "workspace:^0.7.0", "@hcengineering/workbench": "workspace:^0.7.0", "@hcengineering/workbench-assets": "workspace:^0.7.0", "@hcengineering/workbench-resources": "workspace:^0.7.0", diff --git a/dev/prod/public/branding.json b/dev/prod/public/branding.json index f1789b2516..389cfb8db9 100644 --- a/dev/prod/public/branding.json +++ b/dev/prod/public/branding.json @@ -1,7 +1,7 @@ { "huly.local:8080": { "title": "Huly", - "languages": "en,ru,pt,es,zh,fr,de,ja", + "languages": "en,ru,pt,pt-br,es,zh,fr,de,ja,tr", "defaultLanguage": "en", "defaultApplication": "tracker", "defaultSpace": "tracker:project:DefaultProject", @@ -30,7 +30,7 @@ }, "huly.local:8087": { "title": "Huly", - "languages": "en,ru,pt,es,zh,fr,de,ja", + "languages": "en,ru,pt,pt-br,es,zh,fr,de,ja,tr", "defaultLanguage": "en", "defaultApplication": "tracker", "defaultSpace": "tracker:project:DefaultProject", diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index a1c6f7f470..59d7f0adff 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -25,6 +25,7 @@ import { cardId } from '@hcengineering/card' import { chunterId } from '@hcengineering/chunter' import client, { clientId } from '@hcengineering/client' import contactPlugin, { contactId } from '@hcengineering/contact' +import { converterId } from '@hcengineering/converter' import { documentsId } from '@hcengineering/controlled-documents' import { desktopPreferencesId } from '@hcengineering/desktop-preferences' import { diffviewId } from '@hcengineering/diffview' @@ -542,7 +543,7 @@ export async function configurePlatform() { const languages = myBranding.languages ? myBranding.languages.split(',').map((l) => l.trim()) - : ['en', 'ru', 'es', 'pt', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'tr'] + : ['en', 'ru', 'es', 'pt', 'pt-br', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'tr'] setMetadata(uiPlugin.metadata.Languages, languages) @@ -570,6 +571,7 @@ export async function configurePlatform() { async () => await import(/* webpackChunkName: "workbench" */ '@hcengineering/workbench-resources') ) addLocation(viewId, async () => await import(/* webpackChunkName: "view" */ '@hcengineering/view-resources')) + addLocation(converterId, async () => await import(/* webpackChunkName: "converter" */ '@hcengineering/converter-resources')) addLocation(taskId, async () => await import(/* webpackChunkName: "task" */ '@hcengineering/task-resources')) addLocation(contactId, async () => await import(/* webpackChunkName: "contact" */ '@hcengineering/contact-resources')) addLocation(chunterId, async () => await import(/* webpackChunkName: "chunter" */ '@hcengineering/chunter-resources')) diff --git a/foundations/core/packages/account-client/src/client.ts b/foundations/core/packages/account-client/src/client.ts index a974028cf3..14a7379ac8 100644 --- a/foundations/core/packages/account-client/src/client.ts +++ b/foundations/core/packages/account-client/src/client.ts @@ -42,6 +42,7 @@ import type { LoginInfo, LoginInfoByToken, LoginInfoRequestData, + InviteInfo, LoginInfoWithWorkspaces, MailboxInfo, MailboxOptions, @@ -124,7 +125,9 @@ export interface AccountClient { } ) => Promise checkJoin: (inviteId: string) => Promise + joinByToken: (inviteId: string) => Promise checkAutoJoin: (inviteId: string, firstName?: string, lastName?: string) => Promise + getInviteInfo: (inviteId: string) => Promise getWorkspaceInfo: (updateLastVisit?: boolean) => Promise getWorkspacesInfo: (workspaces: WorkspaceUuid[]) => Promise updateLastVisit: (workspaces: WorkspaceUuid[]) => Promise @@ -577,6 +580,15 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } + async joinByToken (inviteId: string): Promise { + const request = { + method: 'joinByToken' as const, + params: { inviteId } + } + + return await this.rpc(request) + } + async checkAutoJoin (inviteId: string, firstName?: string, lastName?: string): Promise { const request = { method: 'checkAutoJoin' as const, @@ -586,6 +598,15 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } + async getInviteInfo (inviteId: string): Promise { + const request = { + method: 'getInviteInfo' as const, + params: { inviteId } + } + + return await this.rpc(request) + } + async getWorkspacesInfo (ids: WorkspaceUuid[]): Promise { const request = { method: 'getWorkspacesInfo' as const, diff --git a/foundations/core/packages/account-client/src/types.ts b/foundations/core/packages/account-client/src/types.ts index a267ae06b9..f055a74d70 100644 --- a/foundations/core/packages/account-client/src/types.ts +++ b/foundations/core/packages/account-client/src/types.ts @@ -80,6 +80,11 @@ export interface WorkspaceInviteInfo { name?: string } +/** Public invite details from getInviteInfo (no auth required). */ +export interface InviteInfo { + workspaceName: string | null +} + export interface OtpInfo { sent: boolean retryOn: Timestamp diff --git a/foundations/core/packages/core/lang/pt-br.json b/foundations/core/packages/core/lang/pt-br.json new file mode 100644 index 0000000000..c128d88b0c --- /dev/null +++ b/foundations/core/packages/core/lang/pt-br.json @@ -0,0 +1,68 @@ +{ + "string": { + "Id": "Id", + "Space": "Espaço", + "Spaces": "Espaços", + "SpacesDescription": "Gerenciamento do tipo de espaço para todos os espaços", + "Modified": "Modificado", + "ModifiedDate": "Data de modificação", + "ModifiedBy": "Modificado por", + "Class": "Classe", + "AttachedTo": "Anexado à", + "AttachedToClass": "Classe anexada", + "Name": "Nome", + "Description": "Descrição", + "Private": "Privado", + "Archived": "Arquivado", + "ClassLabel": "Tipo", + "ClassPropertyLabel": "Legenda", + "String": "Texto", + "Markup": "Marcação", + "Number": "Número", + "Boolean": "Caixa de seleção", + "Timestamp": "Marca de data/hora", + "Date": "Data", + "IntlString": "Texto internacional", + "Ref": "Referência", + "Collection": "Coleção", + "Array": "Seleção múltipla", + "Enum": "Seleção", + "Members": "Membros", + "Hyperlink": "URL", + "MarkupBlobRef": "Colaborativo", + "Object": "Objeto", + "System": "Sistema", + "CreatedBy": "Criado por", + "CreatedDate": "Data de criação", + "Status": "Estado", + "StatusCategory": "Categoria do estado", + "Account": "Conta", + "Rank": "Ranking", + "Owners": "Proprietários", + "Permission": "Permissão", + "CreateObject": "Criar objeto", + "UpdateObject": "Atualizar objeto", + "DeleteObject": "Apagar objeto", + "ForbidDeleteObject": "Proibir apagar objeto", + "UpdateSpace": "Atualizar espaço", + "ArchiveSpace": "Arquivar espaço", + "CreateObjectDescription": "Concede aos usuários a capacidade de criar objetos no espaço", + "UpdateObjectDescription": "Concede aos usuários a capacidade de atualizar objetos no espaço", + "DeleteObjectDescription": "Concede aos usuários a capacidade de apagar objetos no espaço", + "ForbidDeleteObjectDescription": "Proíbe aos usuários a capacidade de apagar objetos no espaço", + "UpdateSpaceDescription": "Concede aos usuários a capacidade de atualizar o espaço", + "ArchiveSpaceDescription": "Concede aos usuários a capacidade de arquivar o espaço", + "AutoJoin": "Auto adesão", + "AutoJoinDescr": "Adesão automática de novos funcionários a este espaço", + "RBAC": "Controle de acesso baseado em funções", + "RBACDescr": "Exigir acesso baseado em funções para executar ações neste espaço", + "BlobSize": "Tamanho", + "BlobContentType": "Tipo de conteúdo", + "Relation": "Relação", + "Relations": "Relações", + "AddRelation": "Adicionar relação", + "PersonId": "ID de pessoa", + "AccountId": "Conta", + "Version": "Versão" + } +} diff --git a/foundations/core/packages/core/src/classes.ts b/foundations/core/packages/core/src/classes.ts index 579bfaaba9..4307dbf502 100644 --- a/foundations/core/packages/core/src/classes.ts +++ b/foundations/core/packages/core/src/classes.ts @@ -947,6 +947,7 @@ export interface ClassCollaborators extends Doc { allFields?: boolean // for all (PersonId | Ref | PersonId[] | Ref[]) attributes fields: (keyof T)[] // PersonId | Ref | PersonId[] | Ref[] provideSecurity?: boolean // If true, will provide security for collaborators + provideAttachedSecurity?: boolean // If true, will provide security for collaborators of attached doc } export interface Collaborator extends AttachedDoc { diff --git a/foundations/core/packages/core/src/memdb.ts b/foundations/core/packages/core/src/memdb.ts index 29cdb08891..8e07c32266 100644 --- a/foundations/core/packages/core/src/memdb.ts +++ b/foundations/core/packages/core/src/memdb.ts @@ -249,6 +249,7 @@ export abstract class MemDb extends TxProcessor implements Storage { // We need to filter instances without mixin was set result = result.filter((r) => (r as any)[_class] !== undefined) } + if (options?.sort !== undefined) resultSort(result, options?.sort, _class, this.hierarchy, this) const total = result.length result = result.slice(0, options?.limit) diff --git a/foundations/core/packages/measurements/src/__tests__/performance.test.ts b/foundations/core/packages/measurements/src/__tests__/performance.test.ts index 14f0ff3e35..fdb6065973 100644 --- a/foundations/core/packages/measurements/src/__tests__/performance.test.ts +++ b/foundations/core/packages/measurements/src/__tests__/performance.test.ts @@ -42,7 +42,7 @@ describe('performance', () => { // Overhead should be reasonable (typically < 50% for simple operations) // This is informational rather than a strict assertion - expect(overheadPercentage).toBeLessThan(200) + expect(overheadPercentage).toBeLessThan(400) }) it('should measure overhead with deep nested contexts', async () => { @@ -154,7 +154,7 @@ describe('performance', () => { console.log(` Overhead: ${overhead.toFixed(2)}ms (${overheadPercentage.toFixed(2)}%)`) // NoMetricsContext should have very low overhead - expect(overheadPercentage).toBeLessThan(50) + expect(overheadPercentage).toBeLessThan(100) }) }) @@ -195,7 +195,7 @@ describe('performance', () => { console.log(` Collected operations: ${aggregated.measurements.request?.operations ?? 0}`) expect(aggregated.measurements.request).toBeDefined() - expect(overheadPercentage).toBeLessThan(100) // Should be less than 100% overhead + expect(overheadPercentage).toBeLessThan(200) // Should be less than 100% overhead }) }) }) diff --git a/foundations/core/packages/platform/lang/pt-br.json b/foundations/core/packages/platform/lang/pt-br.json new file mode 100644 index 0000000000..fb2673913a --- /dev/null +++ b/foundations/core/packages/platform/lang/pt-br.json @@ -0,0 +1,31 @@ +{ + "status": { + "LoadingPlugin": "Carregando plugin {plugin}...", + "UnknownError": "Erro desconhecido: {message}", + "InvalidId": "Id inválido: {id}", + "BadRequest": "Pedido inválido", + "Forbidden": "Proibido", + "Conflict": "Conflito", + "ExpiredLink": "Este link de convite expirou", + "Unauthorized": "Não autorizado", + "UnknownMethod": "Método desconhecido: {method}", + "InternalServerError": "Erro interno do servidor", + "MaintenanceWarning": "Manutenção programada dentro de", + "MaintenanceWarningTime": "{time, plural, =1 {menos de um minuto} other {# minutos}}", + "AccountNotFound": "Conta não encontrada", + "AccountNotConfirmed": "Conta não confirmada", + "WorkspaceNotFound": "Espaço de trabalho não encontrado", + "InvalidPassword": "Senha inválida", + "AccountAlreadyExists": "Conta já existe", + "WorkspaceRateLimit": "O servidor está ocupado. Por favor, espere um pouco e tente novamente", + "AccountAlreadyConfirmed": "Conta já confirmada", + "WorkspaceAlreadyExists": "Espaço de trabalho já existe", + "InvalidOtp": "Código inválido", + "PasswordLoginLocked": "O login com senha está bloqueado devido a muitas tentativas incorretas. Use um método de login OTP para desbloquear sua conta.", + "InviteNotFound": "Convite com e-mail:{email} não encontrado.", + "WorkspaceLimitReached": "Você atingiu o limite de espaços de trabalho. Entre em contato conosco...", + "ReadOnlyAccount": "Demonstração anônima de convidado", + "SystemAccount": "Conta do sistema", + "SocialIdAlreadyExists": "ID social já existe" + } +} diff --git a/foundations/core/packages/storage-client/src/client/datalake.ts b/foundations/core/packages/storage-client/src/client/datalake.ts index 24c9476d13..ad1dc1f7aa 100644 --- a/foundations/core/packages/storage-client/src/client/datalake.ts +++ b/foundations/core/packages/storage-client/src/client/datalake.ts @@ -17,6 +17,11 @@ import { concatLink } from '@hcengineering/core' import { FileStorage, FileStorageUploadOptions } from '../types' import { uploadMultipart, uploadXhr } from '../upload' +const getPathname = (url: string): string => { + const base = window?.location?.href !== undefined ? window.location.href : 'http://localhost' + return new URL(url, base).pathname +} + /** @public */ export class DatalakeStorage implements FileStorage { constructor (private readonly baseUrl: string) {} @@ -26,6 +31,11 @@ export class DatalakeStorage implements FileStorage { return concatLink(this.baseUrl, path) } + getCookiePath (workspace: string): string { + const url = concatLink(this.baseUrl, `/blob/${workspace}`) + return getPathname(url) + } + async getFileMeta (token: string, workspace: string, file: string): Promise> { const url = concatLink(this.baseUrl, `/meta/${encodeURIComponent(workspace)}/${encodeURIComponent(file)}`) try { diff --git a/foundations/core/packages/storage-client/src/client/front.ts b/foundations/core/packages/storage-client/src/client/front.ts index 67ade0229c..54fa4cd0b9 100644 --- a/foundations/core/packages/storage-client/src/client/front.ts +++ b/foundations/core/packages/storage-client/src/client/front.ts @@ -17,6 +17,11 @@ import { concatLink } from '@hcengineering/core' import { FileStorage, FileStorageUploadOptions } from '../types' import { uploadXhr } from '../upload' +const getPathname = (url: string): string => { + const base = window?.location?.href !== undefined ? window.location.href : 'http://localhost' + return new URL(url, base).pathname +} + /** @public */ export class FrontStorage implements FileStorage { constructor (private readonly baseUrl: string) {} @@ -26,6 +31,11 @@ export class FrontStorage implements FileStorage { return concatLink(this.baseUrl, path) } + getCookiePath (workspace: string): string { + const url = concatLink(this.baseUrl, `/${workspace}`) + return getPathname(url) + } + async getFileMeta (token: string, workspace: string, file: string): Promise> { return {} } diff --git a/foundations/core/packages/storage-client/src/client/hulylake.ts b/foundations/core/packages/storage-client/src/client/hulylake.ts index 397815c015..00acdb31f6 100644 --- a/foundations/core/packages/storage-client/src/client/hulylake.ts +++ b/foundations/core/packages/storage-client/src/client/hulylake.ts @@ -17,6 +17,11 @@ import { concatLink } from '@hcengineering/core' import { FileStorage, FileStorageUploadOptions } from '../types' import { uploadXhr } from '../upload' +const getPathname = (url: string): string => { + const base = window?.location?.href !== undefined ? window.location.href : 'http://localhost' + return new URL(url, base).pathname +} + /** @public */ export class HulylakeStorage implements FileStorage { constructor (private readonly baseUrl: string) {} @@ -26,6 +31,11 @@ export class HulylakeStorage implements FileStorage { return concatLink(this.baseUrl, path) } + getCookiePath (workspace: string): string { + const url = concatLink(this.baseUrl, `/api/${workspace}`) + return getPathname(url) + } + async getFileMeta (token: string, workspace: string, file: string): Promise> { return {} } diff --git a/foundations/core/packages/storage-client/src/types.ts b/foundations/core/packages/storage-client/src/types.ts index bcbe88be8f..45924bc3dd 100644 --- a/foundations/core/packages/storage-client/src/types.ts +++ b/foundations/core/packages/storage-client/src/types.ts @@ -29,6 +29,7 @@ export interface FileStorageUploadOptions { /** @public */ export interface FileStorage { getFileUrl: (workspace: string, file: string, filename?: string) => string + getCookiePath: (workspace: string) => string getFileMeta: (token: string, workspace: string, file: string) => Promise> uploadFile: ( token: string, diff --git a/foundations/server/packages/middleware/src/spacePermissions.ts b/foundations/server/packages/middleware/src/spacePermissions.ts index 18090c0673..6acd7f1634 100644 --- a/foundations/server/packages/middleware/src/spacePermissions.ts +++ b/foundations/server/packages/middleware/src/spacePermissions.ts @@ -37,7 +37,7 @@ import core, { } from '@hcengineering/core' import platform, { PlatformError, Severity, Status } from '@hcengineering/platform' import { type Middleware, type TxMiddlewareResult, type PipelineContext } from '@hcengineering/server-core' - +import contact from '@hcengineering/contact' import { BaseMiddleware } from '@hcengineering/server-core' /** @@ -446,10 +446,21 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle this.checkSpacePermissions(ctx, cudTx, cudTx.objectSpace) if (isSpace) { + this.checkSpaceTypePermissions(ctx, cudTx as TxCUD) this.checkSpacePermissions(ctx, cudTx, cudTx.objectId as Ref, true) } } + private checkSpaceTypePermissions (ctx: MeasureContext, cudTx: TxCUD): void { + const account = ctx.contextData.account + const h = this.context.hierarchy + if (account.primarySocialId === core.account.System) return + + if (h.isDerived(cudTx.objectClass, contact.class.PersonSpace)) { + this.throwForbidden() + } + } + private checkSpacePermissions ( ctx: MeasureContext, cudTx: TxCUD, diff --git a/foundations/server/packages/middleware/src/spaceSecurity.ts b/foundations/server/packages/middleware/src/spaceSecurity.ts index 09279c19a1..4c8bd4223e 100644 --- a/foundations/server/packages/middleware/src/spaceSecurity.ts +++ b/foundations/server/packages/middleware/src/spaceSecurity.ts @@ -484,10 +484,7 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar const space = this.spacesMap.get(tx.objectSpace) if (space === undefined) return undefined - // For all other spaces broadcast to space members + guests that are collaborators for objects with collab security enabled - let collabTargets: AccountUuid[] = [] - const collabSec = getClassCollaborators(this.context.modelDb, this.context.hierarchy, cud.objectClass) - if (collabSec?.provideSecurity === true) { + const getCollabTargets = async (_id: Ref): Promise => { const guests = new Set() for (const val of ctx.contextData.socialStringsToUsers.values()) { if ([AccountRole.Guest, AccountRole.ReadOnlyGuest].includes(val.role)) { @@ -495,11 +492,30 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar } } const collaboratorObjs = (await this.next?.findAll(ctx, core.class.Collaborator, { - attachedTo: cud.objectId + attachedTo: _id })) as Collaborator[] - collabTargets = collaboratorObjs.map((it) => it.collaborator).filter((it) => guests.has(it)) + return collaboratorObjs.map((it) => it.collaborator).filter((it) => guests.has(it)) } + + // For all other spaces broadcast to space members + // + guests that are collaborators for objects with collab security enabled + // + guests that are collaborators for attached objects with collab security enabled + let collabTargets: AccountUuid[] = [] + const collabSec = getClassCollaborators(this.context.modelDb, this.context.hierarchy, cud.objectClass) + if (collabSec?.provideSecurity === true) { + collabTargets = await getCollabTargets(cud.objectId) + } else if (cud.attachedTo != null && cud.attachedToClass != null) { + const attachedCollabSec = getClassCollaborators( + this.context.modelDb, + this.context.hierarchy, + cud.attachedToClass + ) + if (attachedCollabSec?.provideSecurity === true) { + collabTargets = await getCollabTargets(cud.attachedTo) + } + } + const spaceTargets = space.members.length === 0 ? [] : this.getTargets(space?.members) const target = [...collabTargets, ...spaceTargets] @@ -672,7 +688,7 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar findResult.lookupMap ) } - if (!isOwner(account, ctx) && account.role !== AccountRole.DocGuest) { + if (account.role !== AccountRole.DocGuest) { if (options?.lookup !== undefined) { for (const object of findResult) { if (object.$lookup !== undefined) { @@ -721,19 +737,29 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar if (Object.keys(lookup).length === 0) return const account = ctx.contextData.account if (isSystem(account, ctx)) return + const owner = isOwner(account, ctx) + const h = this.context.hierarchy const allowedSpaces = new Set(this.getAllAllowedSpaces(account, true, showArchived)) for (const key in lookup) { const val = lookup[key] if (Array.isArray(val)) { const arr: AttachedDoc[] = [] for (const value of val) { - if (allowedSpaces.has(value.space)) { + const isSpace = '_class' in value && h.isDerived(value._class, core.class.Space) + const availableForOwner = owner && isSpace + const availableSpace = isSpace && allowedSpaces.has(value._id) + const availableDoc = !isSpace && allowedSpaces.has(value.space) + if (availableForOwner || availableSpace || availableDoc) { arr.push(value) } } lookup[key] = arr as any } else if (val !== undefined) { - if (!allowedSpaces.has(val.space)) { + const isSpace = '_class' in val && h.isDerived(val._class, core.class.Space) + const availableForOwner = owner && isSpace + const availableSpace = isSpace && allowedSpaces.has(val._id as Ref) + const availableDoc = !isSpace && allowedSpaces.has(val.space) + if (!availableForOwner && !availableSpace && !availableDoc) { // allow attached lookups for guests when collaborator security is enabled // do not check if collaborator of the doc because it's being checked on the storage (DB) level // as otherwise there will be no doc here at all diff --git a/foundations/server/packages/postgres/src/storage.ts b/foundations/server/packages/postgres/src/storage.ts index b89cf5a838..c2fec29622 100644 --- a/foundations/server/packages/postgres/src/storage.ts +++ b/foundations/server/packages/postgres/src/storage.ts @@ -638,11 +638,16 @@ abstract class PostgresAdapterBase implements DbAdapter { const res = `EXISTS (SELECT 1 FROM ${translateDomain(DOMAIN_SPACE)} sec WHERE sec._id = ${domain}.${key} AND sec."workspaceId" = ${vars.add(this.workspaceId, '::uuid')} AND ${q})` const collabSec = getClassCollaborators(this.modelDb, this.hierarchy, _class) - if (collabSec?.provideSecurity === true && [AccountRole.Guest, AccountRole.ReadOnlyGuest].includes(acc.role)) { - const collab = `OR EXISTS (SELECT 1 FROM ${translateDomain(DOMAIN_COLLABORATOR)} collab_sec WHERE collab_sec."workspaceId" = ${vars.add(this.workspaceId, '::uuid')} AND collab_sec."attachedTo" = ${domain}._id AND collab_sec.collaborator = '${acc.uuid}')` - return `AND (${res} ${collab})` + let collabRes = '' + if ([AccountRole.Guest, AccountRole.ReadOnlyGuest].includes(acc.role)) { + if (collabSec?.provideSecurity === true) { + collabRes += ` OR EXISTS (SELECT 1 FROM ${translateDomain(DOMAIN_COLLABORATOR)} collab_sec WHERE collab_sec."workspaceId" = ${vars.add(this.workspaceId, '::uuid')} AND collab_sec."attachedTo" = ${domain}._id AND collab_sec.collaborator = '${acc.uuid}')` + } + if (collabSec?.provideAttachedSecurity === true) { + collabRes += ` OR EXISTS (SELECT 1 FROM ${translateDomain(DOMAIN_COLLABORATOR)} collab_sec WHERE collab_sec."workspaceId" = ${vars.add(this.workspaceId, '::uuid')} AND collab_sec."attachedTo" = ${domain}."attachedTo" AND collab_sec.collaborator = '${acc.uuid}')` + } } - return `AND (${res})` + return `AND (${res}${collabRes})` } } } diff --git a/models/activity/src/index.ts b/models/activity/src/index.ts index 01c603b0c9..82b32c7fed 100644 --- a/models/activity/src/index.ts +++ b/models/activity/src/index.ts @@ -279,6 +279,8 @@ export function createModel (builder: Builder): void { ) builder.mixin(activity.class.Reaction, core.class.Class, core.mixin.TxAccessLevel, { + createAccessLevel: AccountRole.Guest, + updateAccessLevel: AccountRole.Guest, removeAccessLevel: AccountRole.Guest }) diff --git a/models/activity/src/notification.ts b/models/activity/src/notification.ts index c7c05922ce..0370c131b8 100644 --- a/models/activity/src/notification.ts +++ b/models/activity/src/notification.ts @@ -44,12 +44,14 @@ export function buildNotifications (builder: Builder): void { builder.createDoc>(core.class.ClassCollaborators, core.space.Model, { attachedTo: activity.class.ActivityMessage, - fields: ['createdBy', 'repliedPersons'] + fields: ['createdBy', 'repliedPersons'], + provideAttachedSecurity: true }) builder.createDoc>(core.class.ClassCollaborators, core.space.Model, { attachedTo: activity.class.DocUpdateMessage, - fields: ['createdBy', 'repliedPersons'] + fields: ['createdBy', 'repliedPersons'], + provideAttachedSecurity: true }) builder.mixin(activity.class.ActivityMessage, core.class.Class, notification.mixin.NotificationContextPresenter, { diff --git a/models/all/package.json b/models/all/package.json index 5fd9d1b953..e5c92c789a 100644 --- a/models/all/package.json +++ b/models/all/package.json @@ -42,6 +42,7 @@ "@hcengineering/model": "workspace:^0.7.17", "@hcengineering/model-core": "workspace:^0.7.0", "@hcengineering/model-view": "workspace:^0.7.0", + "@hcengineering/model-converter": "workspace:^0.7.0", "@hcengineering/model-workbench": "workspace:^0.7.0", "@hcengineering/model-contact": "workspace:^0.7.0", "@hcengineering/model-task": "workspace:^0.7.0", diff --git a/models/all/src/index.ts b/models/all/src/index.ts index 7b8a005635..47cb2ed9fa 100644 --- a/models/all/src/index.ts +++ b/models/all/src/index.ts @@ -82,6 +82,7 @@ import tracker, { trackerId, createModel as trackerModel } from '@hcengineering/ import { uploaderId, createModel as uploaderModel } from '@hcengineering/model-uploader' import view, { viewId, createModel as viewModel } from '@hcengineering/model-view' import workbench, { workbenchId, createModel as workbenchModel } from '@hcengineering/model-workbench' +import { converterId, createModel as converterModel } from '@hcengineering/model-converter' import document, { documentId, createModel as documentModel } from '@hcengineering/model-document' import { serverDocumentId, createModel as serverDocumentModel } from '@hcengineering/model-server-document' @@ -524,6 +525,7 @@ export default function buildModel (): Builder { classFilter: defaultFilter } ], + [converterModel, converterId], [serverCoreModel, serverCoreId], [serverAttachmentModel, serverAttachmentId], diff --git a/models/attachment/src/index.ts b/models/attachment/src/index.ts index 8a0c853893..f4adb00a12 100644 --- a/models/attachment/src/index.ts +++ b/models/attachment/src/index.ts @@ -22,7 +22,7 @@ import type { Photo, SavedAttachments } from '@hcengineering/attachment' -import { IndexKind, type Blob, type Class, type Doc, type Domain, type Ref } from '@hcengineering/core' +import { AccountRole, IndexKind, type Blob, type Class, type Doc, type Domain, type Ref } from '@hcengineering/core' import { Hidden, Index, @@ -124,6 +124,10 @@ export function createModel (builder: Builder): void { editor: attachment.component.Attachments }) + builder.mixin(attachment.class.Attachment, core.class.Class, core.mixin.TxAccessLevel, { + createAccessLevel: AccountRole.Guest + }) + builder.mixin(attachment.class.Photo, core.class.Class, view.mixin.CollectionEditor, { editor: attachment.component.Photos }) diff --git a/models/card/package.json b/models/card/package.json index e706197bd4..72ce95c6fb 100644 --- a/models/card/package.json +++ b/models/card/package.json @@ -37,8 +37,11 @@ "@hcengineering/activity": "workspace:^0.7.0", "@hcengineering/contact": "workspace:^0.7.0", "@hcengineering/communication": "workspace:^0.7.0", + "@hcengineering/converter": "workspace:^0.7.0", "@hcengineering/core": "workspace:^0.7.24", "@hcengineering/model": "workspace:^0.7.17", + "@hcengineering/notification": "workspace:^0.7.0", + "@hcengineering/model-notification": "workspace:^0.7.0", "@hcengineering/chunter": "workspace:^0.7.0", "@hcengineering/model-setting": "workspace:^0.7.0", "@hcengineering/model-view": "workspace:^0.7.0", diff --git a/models/card/src/index.ts b/models/card/src/index.ts index 42241d89f6..bf8285fa0b 100644 --- a/models/card/src/index.ts +++ b/models/card/src/index.ts @@ -75,6 +75,7 @@ import presentation from '@hcengineering/model-presentation' import setting from '@hcengineering/model-setting' import view, { type Viewlet } from '@hcengineering/model-view' import workbench, { WidgetType } from '@hcengineering/model-workbench' +import converter from '@hcengineering/converter' import { type Asset, getEmbeddedLabel, type IntlString, type Resource } from '@hcengineering/platform' import time, { type ToDo } from '@hcengineering/time' import { PaletteColorIndexes } from '@hcengineering/ui/src/colors' @@ -83,6 +84,7 @@ import { type BuildModelKey } from '@hcengineering/view' import { createActions } from './actions' import { definePermissions } from './permissions' import card from './plugin' +import notification from '@hcengineering/notification' export { cardId } from '@hcengineering/card' @@ -148,6 +150,9 @@ export class TCard extends TDoc implements Card { @Hidden() @ReadOnly() peerId?: string + + @Prop(Collection(chunter.class.ChatMessage), chunter.string.Comments) + comments?: number } @Model(card.class.CardSpace, core.class.TypedSpace, DOMAIN_SPACE) @@ -327,6 +332,10 @@ export function createSystemType ( } }) + builder.mixin(card.class.Card, core.class.Class, converter.mixin.MarkdownValueFormatter, { + formatter: card.function.FormatCardMarkdownValue + }) + builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: type, descriptor: view.viewlet.Table, @@ -440,6 +449,57 @@ export function createModel (builder: Builder): void { PaletteColorIndexes.Arctic ) + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: card.string.Card, + icon: card.icon.Card + }, + card.ids.CardNotificationGroup + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + label: card.string.CardUpdated, + group: card.ids.CardNotificationGroup, + txClasses: [core.class.TxUpdateDoc, core.class.TxMixin], + objectClass: card.class.Card, + defaultEnabled: false, + templates: { + textTemplate: '{body}', + htmlTemplate: '

{body}

{link}

', + subjectTemplate: '{title} updated' + } + }, + card.ids.CardNotification + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + label: chunter.string.Comments, + group: card.ids.CardNotificationGroup, + txClasses: [core.class.TxCreateDoc], + objectClass: chunter.class.ChatMessage, + attachedToClass: card.class.Card, + defaultEnabled: true, + templates: { + textTemplate: 'New message in {title} ({link}) from {senderName}: {message}', + htmlTemplate: '

New message in {title} from {senderName}: {message}

{link}

', + subjectTemplate: 'New message from {senderName} in {title}' + } + }, + card.ids.CardMessageNotification + ) + builder.createDoc(view.class.Viewlet, core.space.Model, { attachTo: card.class.CardSpace, descriptor: view.viewlet.Table, @@ -678,6 +738,60 @@ export function createModel (builder: Builder): void { card.viewlet.CardRelationshipTable ) + builder.createDoc( + presentation.class.ComponentPointExtension, + core.space.Model, + { + extension: converter.extensions.CopyAsMarkdownAction, + component: converter.component.CopyAsMarkdownButton + }, + converter.extensions.CopyAsMarkdownButton + ) + + builder.createDoc( + view.class.ViewletViewAction, + core.space.Model, + { + descriptor: view.viewlet.RelationshipTable, + extension: converter.extensions.CopyAsMarkdownAction, + applicableToClass: card.class.Card + }, + card.specialViewAction.CardRelationshipTable + ) + + builder.createDoc( + view.class.ViewletViewAction, + core.space.Model, + { + descriptor: view.viewlet.Table, + extension: converter.extensions.CopyAsMarkdownAction, + applicableToClass: card.class.Card + }, + card.specialViewAction.CardTable + ) + + builder.createDoc( + view.class.ViewletViewAction, + core.space.Model, + { + descriptor: view.viewlet.Table, + extension: converter.extensions.CopyAsMarkdownAction, + applicableToClass: card.class.Card + }, + card.specialViewAction.CopyAsMarkdownTable + ) + + builder.createDoc( + view.class.ViewletViewAction, + core.space.Model, + { + descriptor: view.viewlet.RelationshipTable, + extension: converter.extensions.CopyAsMarkdownAction, + applicableToClass: card.class.Card + }, + card.specialViewAction.CopyAsMarkdownRelationshipTable + ) + builder.mixin(card.class.Card, core.class.Class, view.mixin.ObjectPresenter, { presenter: card.component.CardPresenter }) diff --git a/models/card/src/plugin.ts b/models/card/src/plugin.ts index b17913c44c..09c392a755 100644 --- a/models/card/src/plugin.ts +++ b/models/card/src/plugin.ts @@ -13,6 +13,7 @@ // limitations under the License. // +import type { ViewletViewAction, Action, ActionCategory, ViewAction } from '@hcengineering/view' import { type Card, cardId } from '@hcengineering/card' import card from '@hcengineering/card-resources/src/plugin' import type { Client, Doc, Ref } from '@hcengineering/core' @@ -20,8 +21,8 @@ import {} from '@hcengineering/core' import { mergeIds, type Resource } from '@hcengineering/platform' import { type TagCategory } from '@hcengineering/tags' import { type Location, type ResolvedLocation } from '@hcengineering/ui/src/types' -import { type Action, type ActionCategory, type ViewAction } from '@hcengineering/view' import { type LocationData } from '@hcengineering/workbench' +import { type NotificationGroup, type NotificationType } from '@hcengineering/notification' export default mergeIds(cardId, card, { app: { @@ -43,10 +44,19 @@ export default mergeIds(cardId, card, { Card: '' as Ref, Labels: '' as Ref }, + specialViewAction: { + CardRelationshipTable: '' as Ref, + CardTable: '' as Ref, + CopyAsMarkdownTable: '' as Ref, + CopyAsMarkdownRelationshipTable: '' as Ref + }, ids: { MasterTags: '' as Ref, ManageMasterTags: '' as Ref, - TagRelations: '' as Ref + TagRelations: '' as Ref, + CardNotificationGroup: '' as Ref, + CardNotification: '' as Ref, + CardMessageNotification: '' as Ref }, resolver: { Location: '' as Resource<(loc: Location) => Promise>, diff --git a/models/controlled-documents/package.json b/models/controlled-documents/package.json index 2c4e04f9f9..58dfbfa824 100644 --- a/models/controlled-documents/package.json +++ b/models/controlled-documents/package.json @@ -49,6 +49,7 @@ "@hcengineering/model": "workspace:^0.7.17", "@hcengineering/setting": "workspace:^0.7.0", "@hcengineering/core": "workspace:^0.7.24", + "@hcengineering/converter": "workspace:^0.7.0", "@hcengineering/ui": "workspace:^0.7.0", "@hcengineering/platform": "workspace:^0.7.19", "@hcengineering/view": "workspace:^0.7.0", diff --git a/models/controlled-documents/src/index.ts b/models/controlled-documents/src/index.ts index 685064f13c..1268bf8c0c 100644 --- a/models/controlled-documents/src/index.ts +++ b/models/controlled-documents/src/index.ts @@ -21,6 +21,7 @@ import documentsPlugin, { DocumentState, type Document, type DocumentSpace, + type DocumentTemplate, type ProjectDocument, type ChangeControl, type DocumentRequest @@ -36,13 +37,14 @@ import request from '@hcengineering/model-request' import tracker from '@hcengineering/model-tracker' import view, { classPresenter, createAction } from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' +import converter from '@hcengineering/converter' 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' -import { AccountRole, type ClassCollaborators, type Class, type Doc, type Ref } from '@hcengineering/core' +import { AccountRole, type ClassCollaborators, type Class, type Doc, type Lookup, type Ref } from '@hcengineering/core' import { type Action } from '@hcengineering/view' import { definePermissions } from './permissions' import documents from './plugin' @@ -111,6 +113,10 @@ export function createModel (builder: Builder): void { titleProvider: documents.function.ControlledDocumentTitleProvider }) + builder.mixin(documents.class.Document, core.class.Class, converter.mixin.MarkdownValueFormatter, { + formatter: documents.function.FormatDocumentMarkdownValue + }) + builder.mixin(documents.class.DocumentApprovalRequest, core.class.Class, view.mixin.ObjectPresenter, { presenter: documents.component.DocumentApprovalRequestPresenter }) @@ -250,6 +256,11 @@ export function createModel (builder: Builder): void { ) // Workflow + const documentTableLookup: Lookup = { + owner: contact.mixin.Employee, + category: documents.class.DocumentCategory, + template: documents.mixin.DocumentTemplate + } builder.createDoc( view.class.Viewlet, core.space.Model, @@ -303,16 +314,27 @@ export function createModel (builder: Builder): void { 'modifiedOn' ], options: { - lookup: { - owner: contact.mixin.Employee, - category: documents.class.DocumentCategory, - template: documents.mixin.DocumentTemplate - } + lookup: documentTableLookup } }, documents.viewlet.TableDocument ) + builder.createDoc( + view.class.ViewletViewAction, + core.space.Model, + { + descriptor: view.viewlet.Table, + extension: converter.extensions.CopyAsMarkdownAction, + applicableToClass: documents.class.Document + }, + documents.specialViewAction.TableDocument + ) + + const documentTemplateTableLookup: Lookup = { + owner: contact.mixin.Employee, + category: documents.class.DocumentCategory + } builder.createDoc( view.class.Viewlet, core.space.Model, @@ -362,10 +384,7 @@ export function createModel (builder: Builder): void { hiddenKeys: ['attachedTo'] }, options: { - lookup: { - owner: contact.mixin.Employee, - category: documents.class.DocumentCategory - } + lookup: documentTemplateTableLookup } }, documents.viewlet.TableDocumentTemplate @@ -1041,7 +1060,7 @@ export function createModel (builder: Builder): void { label: print.string.PrintToPDF, icon: print.icon.Print, category: view.category.General, - input: 'focus', // NOTE: should only work for one doc for now, not bulk + input: 'any', target, context: { mode: ['context', 'browser'], group: 'tools' }, visibilityTester: documents.function.CanPrintDocument, @@ -1064,6 +1083,11 @@ export function createModel (builder: Builder): void { export function defineNotifications (builder: Builder): void { builder.mixin(documents.class.ControlledDocument, core.class.Class, activity.mixin.ActivityDoc, {}) + builder.createDoc(activity.class.ActivityExtension, core.space.Model, { + ofClass: documents.class.ControlledDocument, + components: { input: { component: chunter.component.ChatMessageInput } } + }) + builder.createDoc(activity.class.ActivityExtension, core.space.Model, { ofClass: documents.class.DocumentComment, components: { input: { component: chunter.component.ChatMessageInput } } diff --git a/models/controlled-documents/src/plugin.ts b/models/controlled-documents/src/plugin.ts index e0b5eca013..b82f64e56a 100644 --- a/models/controlled-documents/src/plugin.ts +++ b/models/controlled-documents/src/plugin.ts @@ -20,7 +20,7 @@ import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineer import { mergeIds, type Resource } from '@hcengineering/platform' import { type TagCategory } from '@hcengineering/tags' import { type AnyComponent } from '@hcengineering/ui/src/types' -import { type ActionCategory, type ViewAction } from '@hcengineering/view' +import type { ActionCategory, ViewAction, Viewlet, ViewletViewAction } from '@hcengineering/view' import { type NotificationType, type NotificationGroup } from '@hcengineering/notification' import { type TextActionVisibleFunction, type TextActionFunction } from '@hcengineering/text-editor' @@ -78,11 +78,14 @@ export default mergeIds(documentsId, documents, { TransferDocument: '' as ViewAction, EditDocSpace: '' as ViewAction }, + specialViewAction: { + TableDocument: '' as Ref + }, viewlet: { - TableDocument: '' as Ref, - ListDocument: '' as Ref, - TableDocumentTemplate: '' as Ref, - TableDocumentDomain: '' as Ref + TableDocument: '' as Ref, + ListDocument: '' as Ref, + TableDocumentTemplate: '' as Ref, + TableDocumentDomain: '' as Ref }, notification: { DocumentsNotificationGroup: '' as Ref, diff --git a/models/converter/.eslintrc.js b/models/converter/.eslintrc.js new file mode 100644 index 0000000000..c1cf82cba0 --- /dev/null +++ b/models/converter/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/model/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/models/converter/.npmignore b/models/converter/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/models/converter/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/models/converter/config/rig.json b/models/converter/config/rig.json new file mode 100644 index 0000000000..2f6be36605 --- /dev/null +++ b/models/converter/config/rig.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig", + "rigProfile": "model" +} diff --git a/models/converter/jest.config.js b/models/converter/jest.config.js new file mode 100644 index 0000000000..2cfd408b67 --- /dev/null +++ b/models/converter/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ["./src"], + coverageReporters: ["text-summary", "html"] +} diff --git a/models/converter/package.json b/models/converter/package.json new file mode 100644 index 0000000000..9824ea82e7 --- /dev/null +++ b/models/converter/package.json @@ -0,0 +1,43 @@ +{ + "name": "@hcengineering/model-converter", + "version": "0.7.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "author": "Copyright © Hardcore Engineering Inc.", + "template": "@hcengineering/model-package", + "license": "EPL-2.0", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "_phase:build": "compile transpile src", + "_phase:format": "format src", + "_phase:validate": "compile validate", + "_phase:test": "jest --passWithNoTests --silent --forceExit", + "test": "jest --passWithNoTests --silent --forceExit" + }, + "devDependencies": { + "@hcengineering/platform-rig": "workspace:^0.7.19", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier": "^3.6.2", + "typescript": "^5.9.3", + "@types/node": "^22.18.1", + "jest": "^29.7.0", + "@types/jest": "^29.5.5", + "ts-jest": "^29.1.1" + }, + "dependencies": { + "@hcengineering/core": "workspace:^0.7.24", + "@hcengineering/model-core": "workspace:^0.7.0", + "@hcengineering/model": "workspace:^0.7.17", + "@hcengineering/platform": "workspace:^0.7.19", + "@hcengineering/converter": "workspace:^0.7.0" + } +} diff --git a/models/converter/src/index.ts b/models/converter/src/index.ts new file mode 100644 index 0000000000..59eaa1ad4b --- /dev/null +++ b/models/converter/src/index.ts @@ -0,0 +1,30 @@ +// +// Copyright © 2026 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 { type Builder, Mixin } from '@hcengineering/model' +import core, { TClass } from '@hcengineering/model-core' +import converter, { type MarkdownValueFormatter, type ValueFormatter } from '@hcengineering/converter' +import type { Resource } from '@hcengineering/platform' + +export { converterId } from '@hcengineering/converter' + +@Mixin(converter.mixin.MarkdownValueFormatter, core.class.Class) +export class TMarkdownValueFormatter extends TClass implements MarkdownValueFormatter { + formatter!: Resource +} + +export function createModel (builder: Builder): void { + builder.createModel(TMarkdownValueFormatter) +} diff --git a/models/converter/tsconfig.json b/models/converter/tsconfig.json new file mode 100644 index 0000000000..367a8578c9 --- /dev/null +++ b/models/converter/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/model/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "lib", "dist", "types", "bundle"] +} \ No newline at end of file diff --git a/models/core/src/core.ts b/models/core/src/core.ts index 29fa90e9c7..a2ffa2eef8 100644 --- a/models/core/src/core.ts +++ b/models/core/src/core.ts @@ -416,6 +416,7 @@ export class TClassCollaborators extends TDoc implements ClassCollaborators allFields?: boolean fields!: (keyof Doc)[] provideSecurity?: boolean + provideAttachedSecurity?: boolean } @Model(core.class.Collaborator, core.class.Doc, DOMAIN_COLLABORATOR) diff --git a/models/desktop-preferences/src/index.ts b/models/desktop-preferences/src/index.ts index c6a87520d3..35b1d949f6 100644 --- a/models/desktop-preferences/src/index.ts +++ b/models/desktop-preferences/src/index.ts @@ -13,6 +13,7 @@ // limitations under the License. // +import { AccountRole } from '@hcengineering/core' import { Model, type Builder } from '@hcengineering/model' import core from '@hcengineering/model-core' import preference, { TPreference } from '@hcengineering/model-preference' @@ -33,6 +34,12 @@ export class TDesktopNotificationPreference extends TPreference implements Deskt export function createModel (builder: Builder): void { builder.createModel(TDesktopNotificationPreference) + builder.mixin(desktopPreferences.class.DesktopNotificationPreference, core.class.Class, core.mixin.TxAccessLevel, { + createAccessLevel: AccountRole.Guest, + updateAccessLevel: AccountRole.Guest, + removeAccessLevel: AccountRole.Guest + }) + builder.createDoc( notification.class.NotificationPreferencesGroup, core.space.Model, diff --git a/models/drive/src/index.ts b/models/drive/src/index.ts index 68171798fd..5614644a89 100644 --- a/models/drive/src/index.ts +++ b/models/drive/src/index.ts @@ -234,6 +234,12 @@ function defineDrive (builder: Builder): void { icon: drive.icon.Drive, baseClass: drive.class.Drive, availablePermissions: [ + drive.permission.CreateFolder, + drive.permission.UpdateFolder, + drive.permission.RemoveFolder, + drive.permission.CreateFile, + drive.permission.UpdateFile, + drive.permission.RemoveFile, core.permission.UpdateSpace, core.permission.ArchiveSpace, core.permission.ForbidDeleteObject @@ -326,7 +332,8 @@ function defineDrive (builder: Builder): void { mode: ['context', 'browser'], application: drive.app.Drive, group: 'create' - } + }, + visibilityTester: drive.function.CanCreateFolder }, drive.action.CreateRootFolder ) @@ -475,6 +482,7 @@ function defineFolder (builder: Builder): void { actions: [ view.action.Open, view.action.OpenInNewTab, + view.action.Delete, print.action.Print, tracker.action.EditRelatedTargets, tracker.action.NewRelatedIssue @@ -494,7 +502,8 @@ function defineFolder (builder: Builder): void { mode: ['context', 'browser'], application: drive.app.Drive, group: 'create' - } + }, + visibilityTester: drive.function.CanCreateFolder }, drive.action.CreateChildFolder ) @@ -518,6 +527,25 @@ function defineFolder (builder: Builder): void { drive.action.RenameFolder ) + createAction( + builder, + { + action: view.actionImpl.Delete, + label: view.string.Delete, + icon: view.icon.Delete, + category: drive.category.Drive, + input: 'none', + target: drive.class.Folder, + context: { + mode: ['context', 'browser'], + application: drive.app.Drive, + group: 'edit' + }, + visibilityTester: drive.function.CanDeleteFolder + }, + drive.action.DeleteFolder + ) + createAction(builder, { ...actionTemplates.move, action: view.actionImpl.ShowPopup, @@ -529,6 +557,7 @@ function defineFolder (builder: Builder): void { } }, target: drive.class.Folder, + visibilityTester: drive.function.CanUpdateFolder, context: { mode: ['browser', 'context'], group: 'tools' @@ -569,7 +598,8 @@ function defineFileVersion (builder: Builder): void { mode: ['context', 'browser'], application: drive.app.Drive, group: 'edit' - } + }, + visibilityTester: drive.function.CanUpdateFileVersion }, drive.action.RestoreFileVersion ) @@ -649,6 +679,7 @@ function defineFile (builder: Builder): void { actions: [ view.action.Open, view.action.OpenInNewTab, + view.action.Delete, print.action.Print, tracker.action.EditRelatedTargets, tracker.action.NewRelatedIssue @@ -692,6 +723,25 @@ function defineFile (builder: Builder): void { drive.action.RenameFile ) + createAction( + builder, + { + action: view.actionImpl.Delete, + label: view.string.Delete, + icon: view.icon.Delete, + category: drive.category.Drive, + input: 'none', + target: drive.class.File, + context: { + mode: ['context', 'browser'], + application: drive.app.Drive, + group: 'edit' + }, + visibilityTester: drive.function.CanDeleteFile + }, + drive.action.DeleteFile + ) + // createAction( // builder, // { @@ -721,6 +771,7 @@ function defineFile (builder: Builder): void { } }, target: drive.class.File, + visibilityTester: drive.function.CanUpdateFile, context: { mode: ['browser', 'context'], group: 'tools' diff --git a/models/drive/src/migration.ts b/models/drive/src/migration.ts index 317028c417..1e628420ec 100644 --- a/models/drive/src/migration.ts +++ b/models/drive/src/migration.ts @@ -14,7 +14,7 @@ // import core, { type Blob, type Ref, DOMAIN_BLOB, generateId, toIdMap } from '@hcengineering/core' -import type { Drive, File, FileVersion, Resource } from '@hcengineering/drive' +import type { File, FileVersion, Resource } from '@hcengineering/drive' import { type MigrateOperation, type MigrationClient, @@ -56,7 +56,7 @@ async function migrateFileVersions (client: MigrationClient): Promise { collection: 'versions', modifiedOn: file.modifiedOn, modifiedBy: file.modifiedBy, - space: file.space as Ref, + space: file.space, title: exfile.title, file: blob._id, size: blob.size, diff --git a/models/drive/src/permissions.ts b/models/drive/src/permissions.ts index 89b28e7f09..15da59471c 100644 --- a/models/drive/src/permissions.ts +++ b/models/drive/src/permissions.ts @@ -3,6 +3,84 @@ import core from '@hcengineering/core' import drive from '@hcengineering/drive' export function definePermissions (builder: Builder): void { + builder.createDoc( + core.class.Permission, + core.space.Model, + { + scope: 'space', + label: drive.string.CreateFilePermission, + description: drive.string.CreateFilePermissionDescription, + txClass: core.class.TxCreateDoc, + objectClass: drive.class.File + }, + drive.permission.CreateFile + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + scope: 'space', + label: drive.string.UpdateFilePermission, + description: drive.string.UpdateFilePermissionDescription, + txClass: core.class.TxUpdateDoc, + objectClass: drive.class.File + }, + drive.permission.UpdateFile + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + scope: 'space', + label: drive.string.RemoveFilePermission, + description: drive.string.RemoveFilePermissionDescription, + txClass: core.class.TxRemoveDoc, + objectClass: drive.class.File + }, + drive.permission.RemoveFile + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + scope: 'space', + label: drive.string.CreateFolderPermission, + description: drive.string.CreateFolderPermissionDescription, + txClass: core.class.TxCreateDoc, + objectClass: drive.class.Folder + }, + drive.permission.CreateFolder + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + scope: 'space', + label: drive.string.UpdateFolderPermission, + description: drive.string.UpdateFolderPermissionDescription, + txClass: core.class.TxUpdateDoc, + objectClass: drive.class.Folder + }, + drive.permission.UpdateFolder + ) + + builder.createDoc( + core.class.Permission, + core.space.Model, + { + scope: 'space', + label: drive.string.RemoveFolderPermission, + description: drive.string.RemoveFolderPermissionDescription, + txClass: core.class.TxRemoveDoc, + objectClass: drive.class.Folder + }, + drive.permission.RemoveFolder + ) + builder.createDoc( core.class.Permission, core.space.Model, diff --git a/models/drive/src/plugin.ts b/models/drive/src/plugin.ts index ddfe0386f1..d2738d1f23 100644 --- a/models/drive/src/plugin.ts +++ b/models/drive/src/plugin.ts @@ -53,8 +53,14 @@ export default mergeIds(driveId, drive, { DriveLinkProvider: '' as Resource<(doc: Doc, props: Record) => Promise>, FolderLinkProvider: '' as Resource<(doc: Doc, props: Record) => Promise>, FileLinkProvider: '' as Resource<(doc: Doc, props: Record) => Promise>, + CanCreateFolder: '' as Resource, + CanUpdateFile: '' as Resource, + CanUpdateFolder: '' as Resource, + CanDeleteFile: '' as Resource, + CanDeleteFolder: '' as Resource, CanRenameFile: '' as Resource, CanRenameFolder: '' as Resource, + CanUpdateFileVersion: '' as Resource, CanDeleteFileVersion: '' as Resource, FileTitleProvider: '' as Resource<(client: Client, ref: Ref, doc?: Doc) => Promise>, FolderTitleProvider: '' as Resource<(client: Client, ref: Ref, doc?: Doc) => Promise> @@ -79,6 +85,8 @@ export default mergeIds(driveId, drive, { CreateRootFolder: '' as Ref, EditDrive: '' as Ref, DownloadFile: '' as Ref, + DeleteFile: '' as Ref, + DeleteFolder: '' as Ref, RenameFile: '' as Ref, RenameFolder: '' as Ref, DeleteFileVersion: '' as Ref, diff --git a/models/export/package.json b/models/export/package.json index 75cbe35085..0d3912b849 100644 --- a/models/export/package.json +++ b/models/export/package.json @@ -42,7 +42,11 @@ "@hcengineering/workbench": "workspace:^0.7.0", "@hcengineering/presentation": "workspace:^0.7.0", "@hcengineering/model-presentation": "workspace:^0.7.0", + "@hcengineering/model-view": "workspace:^0.7.0", + "@hcengineering/model-notification": "workspace:^0.7.0", "@hcengineering/export-resources": "workspace:^0.7.0", - "@hcengineering/export": "workspace:^0.7.0" + "@hcengineering/export": "workspace:^0.7.0", + "@hcengineering/view": "workspace:^0.7.0", + "@hcengineering/notification": "workspace:^0.7.0" } } diff --git a/models/export/src/index.ts b/models/export/src/index.ts index f4117b6b15..14fc130562 100644 --- a/models/export/src/index.ts +++ b/models/export/src/index.ts @@ -13,24 +13,99 @@ // limitations under the License. // -import { type Builder } from '@hcengineering/model' -import core from '@hcengineering/model-core' +import type { Class, Doc, Domain, Ref } from '@hcengineering/core' +import type { ExportResultRecord } from '@hcengineering/export' +import { type Builder, Model, Prop, ArrOf, TypeRef, TypeString, TypeNumber, UX } from '@hcengineering/model' +import core, { TDoc } from '@hcengineering/model-core' import presentation from '@hcengineering/model-presentation' import workbench from '@hcengineering/workbench' -import exportPlugin from './plugin' +import view from '@hcengineering/model-view' +import notification from '@hcengineering/notification' +import exportPlugin from '@hcengineering/export' + +import exportModelPlugin from './plugin' export { exportId } from '@hcengineering/export' export * from './migration' -export default exportPlugin +export default exportModelPlugin + +export const DOMAIN_EXPORT = 'export' as Domain + +@Model(exportPlugin.class.ExportResultRecord, core.class.Doc, DOMAIN_EXPORT) +@UX(exportPlugin.string.ImportCompleted, exportPlugin.icon.Export) +export class TExportResultRecord extends TDoc implements ExportResultRecord { + @Prop(TypeString(), exportPlugin.string.SourceWorkspace) + sourceWorkspace!: string + + @Prop(TypeString(), exportPlugin.string.TargetWorkspace) + targetWorkspace!: string + + @Prop(TypeNumber(), exportPlugin.string.ExportedCount) + exportedCount!: number + + @Prop(ArrOf(TypeRef(core.class.Doc)), exportPlugin.string.ExportedDocumentIds) + exportedDocumentIds!: Ref[] + + @Prop(TypeRef(core.class.Class), exportPlugin.string.ExportedDocumentClass) + objectClass!: Ref> + + @Prop(TypeString(), view.string.Title) + title?: string +} export function createModel (builder: Builder): void { + builder.createModel(TExportResultRecord) + + builder.mixin(exportPlugin.class.ExportResultRecord, core.class.Class, view.mixin.ObjectPresenter, { + presenter: view.component.BaseDocPresenter + }) + + builder.mixin(exportPlugin.class.ExportResultRecord, core.class.Class, view.mixin.ObjectPanel, { + component: exportPlugin.component.ExportResultPanel + }) + + builder.mixin(exportPlugin.class.ExportResultRecord, core.class.Class, view.mixin.ObjectTitle, { + titleProvider: exportPlugin.function.ExportResultTitleProvider + }) + + builder.createDoc( + notification.class.NotificationGroup, + core.space.Model, + { + label: exportPlugin.string.Import, + icon: exportPlugin.icon.Export, + objectClass: exportPlugin.class.ExportResultRecord + }, + exportPlugin.ids.ImportNotificationGroup + ) + + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + label: exportPlugin.string.ImportedDocuments, + group: exportPlugin.ids.ImportNotificationGroup, + txClasses: [], + objectClass: exportPlugin.class.ExportResultRecord, + defaultEnabled: true, + templates: { + textTemplate: '{body}', + htmlTemplate: '

{body}

{link}

', + subjectTemplate: '{title}' + } + }, + exportPlugin.ids.ImportedDocumentsNotification + ) + builder.createDoc( presentation.class.ComponentPointExtension, core.space.Model, { extension: workbench.extensions.SpecialViewAction, - component: exportPlugin.component.ExportButton + component: exportModelPlugin.component.ExportButton }, - exportPlugin.extensions.ExportButton + exportModelPlugin.extensions.ExportButton ) } diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts index 66184f4e89..c9675d6c3d 100644 --- a/models/notification/src/index.ts +++ b/models/notification/src/index.ts @@ -573,6 +573,24 @@ export function createModel (builder: Builder): void { removeAccessLevel: AccountRole.Guest }) + builder.mixin(notification.class.NotificationTypeSetting, core.class.Class, core.mixin.TxAccessLevel, { + createAccessLevel: AccountRole.Guest, + updateAccessLevel: AccountRole.Guest, + removeAccessLevel: AccountRole.Guest + }) + + builder.mixin(notification.class.NotificationProviderSetting, core.class.Class, core.mixin.TxAccessLevel, { + createAccessLevel: AccountRole.Guest, + updateAccessLevel: AccountRole.Guest, + removeAccessLevel: AccountRole.Guest + }) + + builder.mixin(notification.class.DocNotifyContext, core.class.Class, core.mixin.TxAccessLevel, { + createAccessLevel: AccountRole.Guest, + updateAccessLevel: AccountRole.Guest, + removeAccessLevel: AccountRole.Guest + }) + builder.createDoc( notification.class.NotificationType, core.space.Model, diff --git a/models/print/src/index.ts b/models/print/src/index.ts index 1b6e523d98..773183f4bc 100644 --- a/models/print/src/index.ts +++ b/models/print/src/index.ts @@ -21,7 +21,7 @@ export function createModel (builder: Builder): void { label: print.string.PrintToPDF, icon: print.icon.Print, category: view.category.General, - input: 'focus', // NOTE: should only work for one doc for now, not bulk + input: 'any', target: core.class.Doc, context: { mode: ['context', 'browser'], group: 'tools' }, visibilityTester: print.function.CanPrint diff --git a/models/process/src/actions.ts b/models/process/src/actions.ts index edf1d91533..16dbbe6d1a 100644 --- a/models/process/src/actions.ts +++ b/models/process/src/actions.ts @@ -31,6 +31,22 @@ export function defineMethods (builder: Builder): void { process.method.RunSubProcess ) + builder.createDoc( + process.class.Method, + core.space.Model, + { + label: process.string.RequestApproval, + objectClass: process.class.ApproveRequest, + editor: process.component.ApproveRequestEditor, + presenter: process.component.ToDoPresenter, + createdContext: { + _class: process.class.ApproveRequest + }, + requiredParams: ['user'] + }, + process.method.RequestApproval + ) + builder.createDoc( process.class.Method, core.space.Model, @@ -103,4 +119,84 @@ export function defineMethods (builder: Builder): void { }, process.method.AddTag ) + + builder.createDoc( + process.class.Method, + core.space.Model, + { + label: process.string.CancelToDo, + editor: process.component.CancelToDoEditor, + presenter: process.component.ToDoValuePresenter, + objectClass: process.class.ProcessToDo, + requiredParams: ['_id'], + createdContext: null + }, + process.method.CancelToDo + ) + + builder.createDoc( + process.class.Method, + core.space.Model, + { + label: process.string.CancelProcess, + editor: process.component.CancelSubProcessEditor, + presenter: process.component.SubProcessPresenter, + objectClass: process.class.Process, + requiredParams: ['_id'], + createdContext: null + }, + process.method.CancelSubProcess + ) + + builder.createDoc( + process.class.Method, + core.space.Model, + { + label: process.string.LockCard, + objectClass: card.class.Card, + requiredParams: [], + createdContext: null + }, + process.method.LockCard + ) + + builder.createDoc( + process.class.Method, + core.space.Model, + { + label: process.string.LockSection, + objectClass: card.class.Card, + editor: process.component.LockSectionEditor, + presenter: process.component.LockSectionPresenter, + requiredParams: ['_id'], + createdContext: null + }, + process.method.LockSection + ) + + builder.createDoc( + process.class.Method, + core.space.Model, + { + label: process.string.UnlockCard, + objectClass: card.class.Card, + requiredParams: [], + createdContext: null + }, + process.method.UnlockCard + ) + + builder.createDoc( + process.class.Method, + core.space.Model, + { + label: process.string.UnlockSection, + objectClass: card.class.Card, + editor: process.component.LockSectionEditor, + presenter: process.component.UnLockSectionPresenter, + requiredParams: ['_id'], + createdContext: null + }, + process.method.UnlockSection + ) } diff --git a/models/process/src/index.ts b/models/process/src/index.ts index f265763b71..15928063f2 100644 --- a/models/process/src/index.ts +++ b/models/process/src/index.ts @@ -43,9 +43,10 @@ import presentation from '@hcengineering/model-presentation' import { TToDo } from '@hcengineering/model-time' import view, { createAction } from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' -import notification from '@hcengineering/notification' +import notification, { type NotificationGroup } from '@hcengineering/notification' import { type Asset, type IntlString, type Resource } from '@hcengineering/platform' import { + type ApproveRequest, type CheckFunc, type ContextId, type CreatedContext, @@ -198,12 +199,27 @@ export class TExecution extends TDoc implements Execution { @Model(process.class.ProcessToDo, time.class.ToDo) @UX(process.string.ToDo) export class TProcessToDo extends TToDo implements ProcessToDo { - execution!: Ref + @Prop(TypeRef(process.class.Execution), process.string.Execution) + execution!: Ref @Prop(TypeBoolean(), process.string.Rollback) withRollback!: boolean } +@Model(process.class.ApproveRequest, process.class.ProcessToDo) +@UX(process.string.ApproveRequest) +export class TApproveRequest extends TProcessToDo implements ApproveRequest { + @Prop(TypeBoolean(), process.string.IsApproved) + approved?: boolean + + @Prop(TypeString(), process.string.RejectionReason) + reason?: string + + group!: string + + card!: Ref +} + @Model(process.class.Method, core.class.Doc, DOMAIN_MODEL) export class TMethod extends TDoc implements Method { label!: IntlString @@ -290,6 +306,7 @@ export function createModel (builder: Builder): void { TProcess, TExecution, TProcessToDo, + TApproveRequest, TMethod, TState, TProcessFunction, @@ -323,6 +340,28 @@ export function createModel (builder: Builder): void { process.ids.ProcessToDoCreated ) + builder.createDoc( + notification.class.NotificationType, + core.space.Model, + { + hidden: false, + generated: false, + allowedForAuthor: true, + label: process.string.ApproveRequest, + group: time.ids.TimeNotificationGroup, + txClasses: [core.class.TxCreateDoc], + objectClass: process.class.ApproveRequest, + onlyOwn: true, + defaultEnabled: true, + templates: { + textTemplate: '{body}', + htmlTemplate: '

{body}

', + subjectTemplate: '{title}' + } + }, + process.ids.ApproveRequestCreated + ) + createAction(builder, { action: view.actionImpl.Delete, label: view.string.Delete, @@ -413,6 +452,46 @@ export function createModel (builder: Builder): void { presenter: process.component.StatePresenter }) + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + variant: 'cardRequests', + attachTo: process.class.ApproveRequest, + descriptor: view.viewlet.List, + props: { + baseMenuClass: process.class.ApproveRequest + }, + viewOptions: { + groupBy: ['user', 'approved', 'execution'], + orderBy: [ + ['approved', SortingOrder.Descending], + ['modifiedOn', SortingOrder.Descending], + ['createdOn', SortingOrder.Descending] + ], + other: [] + }, + configOptions: { + strict: true + }, + config: [ + 'user', + { + key: '', + presenter: view.component.GrowPresenter, + displayProps: { grow: true } + }, + 'reason', + { + key: '', + label: process.string.ApproveRequest, + presenter: process.component.ApproveRequestPresenter + } + ] + }, + process.viewlet.CardRequests + ) + builder.createDoc( view.class.Viewlet, core.space.Model, @@ -600,6 +679,12 @@ export function createModel (builder: Builder): void { props: {} }) + builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, { + extension: card.extensions.EditCardExtension, + component: process.component.RequestsExtension, + props: {} + }) + builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, { extension: card.extensions.EditCardHeaderExtension, component: process.component.ProcessesHeaderExtension, @@ -623,12 +708,44 @@ export function createModel (builder: Builder): void { process.section.CardProcesses ) + builder.createDoc( + card.class.CardSection, + core.space.Model, + { + label: process.string.ApproveRequest, + component: process.component.RequestsCardSection, + checkVisibility: process.function.CheckRequestsSectionVisibility, + order: 360, + navigation: [] + }, + process.section.CardApproveRequest + ) + builder.createDoc(card.class.MasterTagEditorSection, core.space.Model, { id: 'processes', label: process.string.Processes, component: process.component.ProcessesSettingSection }) + builder.createDoc(notification.class.NotificationType, core.space.Model, { + hidden: false, + generated: false, + allowedForAuthor: true, + label: process.string.NewProcessToDo, + group: time.ids.TimeNotificationGroup as Ref, + txClasses: [core.class.TxCreateDoc], + objectClass: process.class.ProcessToDo, + txMatch: { + objectClass: process.class.ProcessToDo + }, + defaultEnabled: true, + templates: { + textTemplate: '{body}', + htmlTemplate: '

{body}

', + subjectTemplate: '{title}' + } + }) + // builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, { // extension: workbench.extensions.WorkbenchExtensions, // component: process.component.NotifierExtension diff --git a/models/process/src/plugin.ts b/models/process/src/plugin.ts index d788ab848f..728b3b1ef6 100644 --- a/models/process/src/plugin.ts +++ b/models/process/src/plugin.ts @@ -24,14 +24,16 @@ export default mergeIds(processId, process, { Process: '' as Ref }, section: { - CardProcesses: '' as Ref + CardProcesses: '' as Ref, + CardApproveRequest: '' as Ref }, pipeline: { ProcessMiddleware: '' as Ref }, ids: { ProcessSettings: '' as Ref, - ProcessToDoCreated: '' as Ref + ProcessToDoCreated: '' as Ref, + ApproveRequestCreated: '' as Ref }, actionImpl: { ContinueExecution: '' as ViewAction diff --git a/models/process/src/triggers.ts b/models/process/src/triggers.ts index 600dd2b84b..a96bc53aab 100644 --- a/models/process/src/triggers.ts +++ b/models/process/src/triggers.ts @@ -16,6 +16,38 @@ import { type Builder } from '@hcengineering/model' import process from './plugin' export function defineTriggers (builder: Builder): void { + builder.createDoc( + process.class.Trigger, + core.space.Model, + { + label: process.string.OnApproveRequestApproved, + icon: process.icon.ToDo, + editor: process.component.ApproveRequestTriggerEditor, + presenter: process.component.ApproveRequestTriggerPresenter, + requiredParams: ['_id'], + checkFunction: process.triggerCheck.ApproveRequestApproved, + init: false, + auto: true + }, + process.trigger.OnApproveRequestApproved + ) + + builder.createDoc( + process.class.Trigger, + core.space.Model, + { + label: process.string.OnApproveRequestRejected, + icon: process.icon.ToDoRemove, + editor: process.component.ApproveRequestTriggerEditor, + presenter: process.component.ApproveRequestTriggerPresenter, + requiredParams: ['_id'], + checkFunction: process.triggerCheck.ApproveRequestRejected, + init: false, + auto: true + }, + process.trigger.OnApproveRequestRejected + ) + builder.createDoc( process.class.Trigger, core.space.Model, diff --git a/models/recruit/src/index.ts b/models/recruit/src/index.ts index 9da4ca3b8e..c210f996b7 100644 --- a/models/recruit/src/index.ts +++ b/models/recruit/src/index.ts @@ -188,31 +188,7 @@ export function createModel (builder: Builder): void { createEvent: RecruitEvents.PlusTalentButtonClicked, createLabel: recruit.string.TalentCreateLabel, createComponent: recruit.component.CreateCandidate, - createComponentProps: { shouldSaveDraft: false }, - actionVisible: true, - actionConfig: { - skipAttributes: ['_class', 'avatarType', 'space'], - attributeKeyMap: { city: 'location' }, - attributeTransforms: { - channels: { - operations: [ - { - type: 'group_by', - config: { - keyField: 'data.provider', - valueField: 'data.value' - } - }, - { - type: 'join', - config: { - delimiter: ', ' - } - } - ] - } - } - } + createComponentProps: { shouldSaveDraft: false } }, position: 'vacancy' }, @@ -323,6 +299,41 @@ export function createModel (builder: Builder): void { recruit.viewlet.TableCandidate ) + const tableCandidateExportConfig = { + skipAttributes: ['_class', 'avatarType', 'space'], + attributeKeyMap: { city: 'location' }, + attributeTransforms: { + channels: { + operations: [ + { + type: 'group_by', + config: { + keyField: 'data.provider', + valueField: 'data.value' + } + }, + { + type: 'join', + config: { + delimiter: ', ' + } + } + ] + } + } + } + builder.createDoc( + view.class.ViewletViewAction, + core.space.Model, + { + descriptor: view.viewlet.Table, + extension: workbench.extensions.SpecialViewAction, + config: tableCandidateExportConfig, + applicableToClass: recruit.mixin.Candidate + }, + recruit.specialViewAction.TableCandidate + ) + builder.createDoc( view.class.Viewlet, core.space.Model, diff --git a/models/recruit/src/plugin.ts b/models/recruit/src/plugin.ts index 86bfa59d63..1d5e2c9547 100644 --- a/models/recruit/src/plugin.ts +++ b/models/recruit/src/plugin.ts @@ -29,7 +29,8 @@ import type { ViewAction, ViewActionAvailabilityFunction, ViewQueryAction, - Viewlet + Viewlet, + ViewletViewAction } from '@hcengineering/view' export default mergeIds(recruitId, recruit, { @@ -132,6 +133,9 @@ export default mergeIds(recruitId, recruit, { template: { DefaultVacancy: '' as Ref }, + specialViewAction: { + TableCandidate: '' as Ref + }, viewlet: { TableCandidate: '' as Ref, TableVacancy: '' as Ref, diff --git a/models/server-card/package.json b/models/server-card/package.json index c36b9bf3cd..909cfadf07 100644 --- a/models/server-card/package.json +++ b/models/server-card/package.json @@ -39,6 +39,7 @@ "@hcengineering/platform": "workspace:^0.7.19", "@hcengineering/card": "workspace:^0.7.0", "@hcengineering/communication": "workspace:^0.7.0", + "@hcengineering/server-notification": "workspace:^0.7.0", "@hcengineering/server-card": "workspace:^0.7.0", "@hcengineering/server-core": "workspace:^0.7.18" } diff --git a/models/server-card/src/index.ts b/models/server-card/src/index.ts index 9008fbee70..32d1be5798 100644 --- a/models/server-card/src/index.ts +++ b/models/server-card/src/index.ts @@ -20,6 +20,7 @@ import serverCore from '@hcengineering/server-core' import serverCard from '@hcengineering/server-card' import card from '@hcengineering/card' import communication from '@hcengineering/communication' +import serverNotification from '@hcengineering/server-notification' export { serverCardId } from '@hcengineering/server-card' @@ -129,4 +130,12 @@ export function createModel (builder: Builder): void { }, title: [['title']] }) + + builder.mixin(card.class.Card, core.class.Class, serverNotification.mixin.TextPresenter, { + presenter: serverCard.function.CardTextPresenter + }) + + builder.mixin(card.class.Card, core.class.Class, serverNotification.mixin.HTMLPresenter, { + presenter: serverCard.function.CardHTMLPresenter + }) } diff --git a/models/server-process/src/index.ts b/models/server-process/src/index.ts index 0de963d6db..5311e13197 100644 --- a/models/server-process/src/index.ts +++ b/models/server-process/src/index.ts @@ -70,6 +70,16 @@ export function createModel (builder: Builder): void { serverCheckFunc: serverProcess.func.FieldChangedCheck }) + builder.mixin(process.trigger.OnApproveRequestApproved, process.class.Trigger, serverProcess.mixin.TriggerImpl, { + preventRollback: true, + serverCheckFunc: serverProcess.func.ApproveRequestApproved + }) + + builder.mixin(process.trigger.OnApproveRequestRejected, process.class.Trigger, serverProcess.mixin.TriggerImpl, { + preventRollback: true, + serverCheckFunc: serverProcess.func.ApproveRequestRejected + }) + builder.mixin(process.trigger.OnExecutionStart, process.class.Trigger, serverProcess.mixin.TriggerImpl, { preventRollback: true }) @@ -97,6 +107,10 @@ export function createModel (builder: Builder): void { func: serverProcess.func.RunSubProcess }) + builder.mixin(process.method.CancelSubProcess, process.class.Method, serverProcess.mixin.MethodImpl, { + func: serverProcess.func.CancelSubProcess + }) + builder.mixin(process.method.CreateToDo, process.class.Method, serverProcess.mixin.MethodImpl, { func: serverProcess.func.CreateToDo }) @@ -117,6 +131,30 @@ export function createModel (builder: Builder): void { func: serverProcess.func.AddTag }) + builder.mixin(process.method.RequestApproval, process.class.Method, serverProcess.mixin.MethodImpl, { + func: serverProcess.func.RequestApproval + }) + + builder.mixin(process.method.CancelToDo, process.class.Method, serverProcess.mixin.MethodImpl, { + func: serverProcess.func.CancelToDo + }) + + builder.mixin(process.method.LockCard, process.class.Method, serverProcess.mixin.MethodImpl, { + func: serverProcess.func.LockCard + }) + + builder.mixin(process.method.LockSection, process.class.Method, serverProcess.mixin.MethodImpl, { + func: serverProcess.func.LockSection + }) + + builder.mixin(process.method.UnlockCard, process.class.Method, serverProcess.mixin.MethodImpl, { + func: serverProcess.func.UnlockCard + }) + + builder.mixin(process.method.UnlockSection, process.class.Method, serverProcess.mixin.MethodImpl, { + func: serverProcess.func.UnlockSection + }) + builder.mixin(process.function.FirstValue, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, { func: serverProcess.transform.FirstValue }) diff --git a/models/tracker/package.json b/models/tracker/package.json index a7cf3083ac..0bdad7a260 100644 --- a/models/tracker/package.json +++ b/models/tracker/package.json @@ -37,6 +37,7 @@ "@hcengineering/activity": "workspace:^0.7.0", "@hcengineering/chunter": "workspace:^0.7.0", "@hcengineering/contact": "workspace:^0.7.0", + "@hcengineering/converter": "workspace:^0.7.0", "@hcengineering/core": "workspace:^0.7.24", "@hcengineering/model": "workspace:^0.7.17", "@hcengineering/model-attachment": "workspace:^0.7.0", diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts index 04395afc0e..1f45a0e400 100644 --- a/models/tracker/src/index.ts +++ b/models/tracker/src/index.ts @@ -23,6 +23,7 @@ import presentation from '@hcengineering/model-presentation' import task from '@hcengineering/model-task' import view from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' +import converter from '@hcengineering/converter' import notification from '@hcengineering/notification' import setting from '@hcengineering/setting' import pluginState, { type Issue, trackerId } from '@hcengineering/tracker' @@ -82,6 +83,10 @@ export const classicIssueTaskStatuses: TaskStatusFactory[] = [ ] function defineSortAndGrouping (builder: Builder): void { + builder.mixin(tracker.class.Issue, core.class.Class, converter.mixin.MarkdownValueFormatter, { + formatter: tracker.function.FormatIssueMarkdownValue + }) + builder.mixin(tracker.class.IssueStatus, core.class.Class, view.mixin.SortFuncs, { func: tracker.function.IssueStatusSort }) @@ -482,6 +487,17 @@ export function createModel (builder: Builder): void { defineViewlets(builder) + builder.createDoc( + view.class.ViewletViewAction, + core.space.Model, + { + descriptor: view.viewlet.List, + extension: converter.extensions.CopyAsMarkdownAction, + applicableToClass: tracker.class.Issue + }, + tracker.specialViewAction.IssueList + ) + const issuesId = 'issues' const componentsId = 'components' const milestonesId = 'milestones' diff --git a/models/tracker/src/plugin.ts b/models/tracker/src/plugin.ts index 9c84de8eac..4c27d0a153 100644 --- a/models/tracker/src/plugin.ts +++ b/models/tracker/src/plugin.ts @@ -22,7 +22,7 @@ import { type ProjectType } from '@hcengineering/task' import { trackerId } from '@hcengineering/tracker' import tracker from '@hcengineering/tracker-resources/src/plugin' import type { AnyComponent } from '@hcengineering/ui/src/types' -import { type Action, type ViewAction, type Viewlet } from '@hcengineering/view' +import { type Action, type ViewAction, type Viewlet, type ViewletViewAction } from '@hcengineering/view' import { type Application } from '@hcengineering/workbench' export default mergeIds(trackerId, tracker, { @@ -63,6 +63,9 @@ export default mergeIds(trackerId, tracker, { app: { Tracker: '' as Ref }, + specialViewAction: { + IssueList: '' as Ref + }, viewlet: { IssueList: '' as Ref, IssueTemplateList: '' as Ref, diff --git a/models/tracker/src/viewlets.ts b/models/tracker/src/viewlets.ts index 28cc626394..3ec1ad6bb6 100644 --- a/models/tracker/src/viewlets.ts +++ b/models/tracker/src/viewlets.ts @@ -116,7 +116,9 @@ export function issueConfig ( key: '', label: tracker.string.Title, presenter: tracker.component.TitlePresenter, - props: compact ? { shouldUseMargin: true, showParent: false } : {}, + props: compact + ? { shouldUseMargin: true, showParent: false, grow: true, minWidth: '5rem' } + : { grow: true, minWidth: '5rem' }, displayProps: { key: key + 'title' } }, { @@ -150,7 +152,8 @@ export function issueConfig ( props: { kind: 'list', size: 'small', - shouldShowPlaceholder: false + shouldShowPlaceholder: false, + maxWidth: '30rem' }, displayProps: { key: key + 'milestone', @@ -169,7 +172,8 @@ export function issueConfig ( props: { kind: 'list', size: 'small', - shouldShowPlaceholder: false + shouldShowPlaceholder: false, + maxWidth: '30rem' }, displayProps: { key: key + 'component', @@ -425,7 +429,8 @@ export function defineViewlets (builder: Builder): void { props: { kind: 'list', size: 'small', - shouldShowPlaceholder: false + shouldShowPlaceholder: false, + maxWidth: '30rem' }, displayProps: { key: 'component', compression: true } }, @@ -436,7 +441,8 @@ export function defineViewlets (builder: Builder): void { props: { kind: 'list', size: 'small', - shouldShowPlaceholder: false + shouldShowPlaceholder: false, + maxWidth: '30rem' }, displayProps: { key: 'milestone', compression: true } }, diff --git a/models/view/src/index.ts b/models/view/src/index.ts index e92af38b33..d0afec43fc 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -29,12 +29,17 @@ import { type Ref, type Space } from '@hcengineering/core' -import { type Builder, Mixin, Model, UX } from '@hcengineering/model' +import { type Builder, Mixin, Model, Prop, TypeRecord, TypeRef, TypeString, UX } from '@hcengineering/model' import core, { TClass, TDoc } from '@hcengineering/model-core' import preference, { TPreference } from '@hcengineering/model-preference' import presentation from '@hcengineering/model-presentation' -import { type Asset, type IntlString, type Resource, type Status } from '@hcengineering/platform' -import { type AnyComponent, type LabelAndProps, type Location } from '@hcengineering/ui/src/types' +import { type Asset, getEmbeddedLabel, type IntlString, type Resource, type Status } from '@hcengineering/platform' +import { + type AnyComponent, + type ComponentExtensionId, + type LabelAndProps, + type Location +} from '@hcengineering/ui/src/types' import { type TypeEditor, type Action, @@ -99,7 +104,8 @@ import { type ViewOptionsModel, type Viewlet, type ViewletDescriptor, - type ViewletPreference + type ViewletPreference, + type ViewletViewAction } from '@hcengineering/view' import view from './plugin' @@ -318,6 +324,28 @@ export class TViewletDescriptor extends TDoc implements ViewletDescriptor { label!: IntlString } +@Model(view.class.ViewletViewAction, core.class.Doc, DOMAIN_MODEL) +@UX(view.string.ViewletViewAction) +export class TViewletViewAction extends TDoc implements ViewletViewAction { + @Prop(TypeRef(view.class.Viewlet), getEmbeddedLabel('Viewlet')) + declare viewlet?: Ref + + @Prop(TypeRef(view.class.ViewletDescriptor), getEmbeddedLabel('Descriptor')) + declare descriptor?: Ref + + @Prop(TypeString(), getEmbeddedLabel('Extension')) + declare extension: ComponentExtensionId + + @Prop(TypeRecord(), getEmbeddedLabel('Config')) + declare config?: Record + + @Prop(TypeRef(core.class.Class), getEmbeddedLabel('ApplicableToClass')) + declare applicableToClass?: Ref> + + @Prop(TypeRef(core.class.Class), getEmbeddedLabel('DisabledForClass')) + declare disabledForClass?: Ref> +} + @Model(view.class.Viewlet, core.class.Doc, DOMAIN_MODEL) export class TViewlet extends TDoc implements Viewlet { attachTo!: Ref> @@ -472,6 +500,7 @@ export function createModel (builder: Builder): void { TViewletPreference, TViewletDescriptor, TViewlet, + TViewletViewAction, TAction, TActionCategory, TObjectValidator, diff --git a/models/workbench/src/index.ts b/models/workbench/src/index.ts index 0b90976296..a23d9ba355 100644 --- a/models/workbench/src/index.ts +++ b/models/workbench/src/index.ts @@ -17,6 +17,7 @@ import { AccountRole, type AccountUuid, type Class, DOMAIN_MODEL, type Ref, type import { type Builder, Mixin, Model, Prop, TypeRef, UX } from '@hcengineering/model' import preference, { TPreference } from '@hcengineering/model-preference' import { createAction } from '@hcengineering/model-view' +import core, { TClass, TDoc } from '@hcengineering/model-core' import { type Asset, getEmbeddedLabel, type IntlString, type Resource } from '@hcengineering/platform' import view, { type KeyBinding } from '@hcengineering/view' import type { @@ -32,7 +33,6 @@ import type { WorkbenchTab } from '@hcengineering/workbench' import { type AnyComponent } from '@hcengineering/ui/src/types' -import core, { TClass, TDoc } from '@hcengineering/model-core' import presentation from '@hcengineering/model-presentation' import workbench from './plugin' diff --git a/packages/presentation/lang/pt-br.json b/packages/presentation/lang/pt-br.json new file mode 100644 index 0000000000..3769f744a9 --- /dev/null +++ b/packages/presentation/lang/pt-br.json @@ -0,0 +1,64 @@ +{ + "string": { + "Create": "Criar", + "Cancel": "Cancelar", + "Ok": "Ok", + "Save": "Salvar", + "Saved": "Salvo...", + "Download": "Fazer download", + "DownloadOriginal": "Fazer download do original", + "Delete": "Excluir", + "Close": "Fechar", + "NotSelected": "Não selecionado", + "Deselect": "Desmarcar", + "Archived": "(arquivado)", + "AddSocialLinks": "Adicionar links sociais", + "EditSocialLinks": "Editar links sociais", + "Change": "Alterar", + "Remove": "Remover", + "Search": "Pesquisar...", + "Spaces": "Espaços", + "NumberSpaces": "{count, plural, =0 {Em} =1 {Em 1 local} other {Em # locais}}", + "InThis": "Neste {space}", + "NoMatchesInThis": "Sem resultados no espaço {space}", + "NoMatchesFound": "Sem resultados encontrados", + "NotInThis": "Não está no espaço {space}", + "Match": "Resultado", + "Add": "Adicionar", + "Edit": "Editar", + "DocumentPreview": "Visualizar", + "MakePrivate": "Tornar privado", + "MakePrivateDescription": "Apenas os membros podem ver", + "Created": "Criado", + "Selected": "Selecionado", + "NoResults": "Sem resultados para mostrar", + "Next": "Próximo", + "FailedToPreview": "Falha ao pré-visualizar", + "ContentType": "Tipo de conteúdo", + "ContentTypeNotSupported": "A visualização não está disponível para este tipo de conteúdo", + "StartDrawing": "Começar a desenhar", + "DrawingHistory": "Histórico de desenhos", + "ColorAdd": "Adicionar predefinição de cor", + "ColorRemove": "Remover predefinição de cor", + "ColorReset": "Resetar predefinições de cores", + "Copy": "Copiar", + "DocumentUrlCopied": "Copiado!", + "CopyLink": "Copiar link", + "AccessDenied": "O objeto não existe ou você não tem permissão para acessá-lo.", + "UnableToFollowMention": "Não foi possível abrir o objeto mencionado", + "Undo": "Desfazer", + "Redo": "Refazer", + "ClearCanvas": "Limpar tela", + "PenTool": "Caneta", + "EraserTool": "Borracha", + "PanTool": "Mover", + "TextTool": "Texto", + "LineTool": "Linha", + "RectangleTool": "Retângulo", + "EllipseTool": "Elipse", + "PaletteManagementMenu": "Gerenciar predefinições de cor" + }, + "status": { + "FileTooLarge": "Arquivo muito grande" + } +} diff --git a/packages/presentation/src/utils.ts b/packages/presentation/src/utils.ts index 43b320c19e..8771da2ab4 100644 --- a/packages/presentation/src/utils.ts +++ b/packages/presentation/src/utils.ts @@ -891,15 +891,19 @@ export function isSpaceClass (_class: Ref>): boolean { } export function setPresentationCookie (token: string, workspaceUuid: WorkspaceUuid): void { - function setToken (path: string): void { - const res = - encodeURIComponent(plugin.metadata.Token.replaceAll(':', '-')) + - '=' + - encodeURIComponent(token) + - `; path=${path}` - document.cookie = res + const cookieName = encodeURIComponent(plugin.metadata.Token.replaceAll(':', '-')) + const cookieValue = encodeURIComponent(token) + + const storage = getMetadata(plugin.metadata.FileStorage) + if (storage !== undefined) { + let path = `/files/${workspaceUuid}` + try { + path = storage.getCookiePath(workspaceUuid) + } catch {} + + const normalized = path.startsWith('/') ? path : `/${path}` + document.cookie = `${cookieName}=${cookieValue}; path=${normalized}` } - setToken('/files/' + workspaceUuid) } export const upgradeDownloadProgress = writable(-1) diff --git a/packages/theme/styles/_layouts.scss b/packages/theme/styles/_layouts.scss index e1c0c7c1de..90f3929ae4 100644 --- a/packages/theme/styles/_layouts.scss +++ b/packages/theme/styles/_layouts.scss @@ -886,6 +886,7 @@ a.no-line { font-size: 1rem; color: var(--theme-caption-color); user-select: none; + text-rendering: optimizeLegibility; } .trans-title { text-transform: uppercase; @@ -893,6 +894,7 @@ a.no-line { font-size: .75rem; color: var(--theme-dark-color); user-select: none; + text-rendering: optimizeLegibility; } .text-16px { font-size: 16px; } .text-xs { font-size: .625rem; } diff --git a/packages/theme/styles/dialogs.scss b/packages/theme/styles/dialogs.scss index b2d24bb19b..44fdd23542 100644 --- a/packages/theme/styles/dialogs.scss +++ b/packages/theme/styles/dialogs.scss @@ -65,6 +65,7 @@ min-width: 0; line-height: 150%; color: var(--theme-caption-color); + text-rendering: optimizeLegibility; &.accentHeader { font-size: 1rem; } } diff --git a/packages/theme/styles/panel.scss b/packages/theme/styles/panel.scss index 150653789e..cb870e0770 100644 --- a/packages/theme/styles/panel.scss +++ b/packages/theme/styles/panel.scss @@ -55,6 +55,7 @@ font-size: 1.25rem; line-height: 1.5rem; color: var(--global-primary-TextColor); + text-rendering: optimizeLegibility; &:not(.withButton) { min-height: 4.25rem; diff --git a/packages/theme/styles/prose.scss b/packages/theme/styles/prose.scss index 6c6c1b541b..235628047c 100644 --- a/packages/theme/styles/prose.scss +++ b/packages/theme/styles/prose.scss @@ -574,13 +574,26 @@ pre.proseCodeBlock>pre.proseCode { .mermaidPreviewContainer { padding: 0.5rem; cursor: default; - overflow-x: auto; + overflow: auto; } &:not(.folded) .mermaidPreviewContainer { border-top: 1px solid var(--border-color); min-height: 6rem; } + + .mermaidPreview { + width: 100%; + display: flex; + justify-content: center; + + svg { + width: auto; + max-width: 100%; + height: auto; + display: block; + } + } } .proseInlineCommentHighlight { @@ -602,4 +615,4 @@ pre.proseCodeBlock>pre.proseCode { .theme-light { @include meta.load-css('./github-light.scss'); -} \ No newline at end of file +} diff --git a/packages/ui/lang/cs.json b/packages/ui/lang/cs.json index 7698769d63..5bb6ebb6aa 100644 --- a/packages/ui/lang/cs.json +++ b/packages/ui/lang/cs.json @@ -50,6 +50,7 @@ "English": "English", "Russian": "Русский", "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", "Spanish": "Español", "Chinese": "中文", "French": "Français", diff --git a/packages/ui/lang/de.json b/packages/ui/lang/de.json index 638f70c222..90faf66346 100644 --- a/packages/ui/lang/de.json +++ b/packages/ui/lang/de.json @@ -50,6 +50,7 @@ "English": "Englisch", "Russian": "Russisch", "Portuguese": "Portugiesisch", + "PortugueseBrazil": "Português (Brasil)", "Spanish": "Spanisch", "Chinese": "Chinesisch", "French": "Französisch", diff --git a/packages/ui/lang/en.json b/packages/ui/lang/en.json index 7c325e107f..4e77c59088 100644 --- a/packages/ui/lang/en.json +++ b/packages/ui/lang/en.json @@ -50,6 +50,7 @@ "English": "English", "Russian": "Русский", "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", "Spanish": "Español", "Chinese": "中文", "French": "Français", diff --git a/packages/ui/lang/es.json b/packages/ui/lang/es.json index 81e01a8627..a4cdd82b0d 100644 --- a/packages/ui/lang/es.json +++ b/packages/ui/lang/es.json @@ -50,6 +50,7 @@ "English": "English", "Russian": "Русский", "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", "Spanish": "Español", "Chinese": "中文", "French": "Français", diff --git a/packages/ui/lang/fr.json b/packages/ui/lang/fr.json index 2de9794825..353bf2e553 100644 --- a/packages/ui/lang/fr.json +++ b/packages/ui/lang/fr.json @@ -50,6 +50,7 @@ "English": "English", "Russian": "Русский", "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", "Spanish": "Español", "Chinese": "中文", "French": "Français", diff --git a/packages/ui/lang/it.json b/packages/ui/lang/it.json index 0854a3f1f4..5012a88957 100644 --- a/packages/ui/lang/it.json +++ b/packages/ui/lang/it.json @@ -50,6 +50,7 @@ "English": "Inglese", "Russian": "Русский", "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", "Spanish": "Español", "Chinese": "中文", "French": "Français", diff --git a/packages/ui/lang/ja.json b/packages/ui/lang/ja.json index 902609e4a9..7206f2e501 100644 --- a/packages/ui/lang/ja.json +++ b/packages/ui/lang/ja.json @@ -50,6 +50,7 @@ "English": "English", "Russian": "Русский", "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", "Spanish": "Español", "Chinese": "中文", "French": "Français", diff --git a/packages/ui/lang/pt-br.json b/packages/ui/lang/pt-br.json new file mode 100644 index 0000000000..14bad314d9 --- /dev/null +++ b/packages/ui/lang/pt-br.json @@ -0,0 +1,114 @@ +{ + "string": { + "EditBoxPlaceholder": "Digite o texto...", + "Ok": "Ok", + "Cancel": "Cancelar", + "Save": "Salvar", + "Publish": "Publicar", + "SaveDraft": "Salvar rascunho", + "MinutesAgo": "{minutes, plural, =0 {há menos de um minuto} =1 {há um minuto} other {há # minutos}}", + "HoursAgo": "{hours, plural, =0 {há menos de uma hora} =1 {há uma hora} other {há # horas}}", + "DaysAgo": "{days, plural, =0 {hoje} =1 {ontem} other {há # dias}}", + "MonthsAgo": "{months, plural, =0 {este mês} =1 {há um mês} other {há # meses}}", + "YearsAgo": "{years, plural, =0 {este ano} =1 {há um ano} other {há # anos}}", + "Minutes": "{minutes, plural, =0 {menos de um minuto} =1 {um minuto} other {# minutos}}", + "Hours": "{hours, plural, =0 {menos de uma hora} =1 {uma hora} other {# horas}}", + "Days": "{days, plural, =0 {hoje} =1 {1 dia} other {# dias}}", + "Weeks": "{weeks, plural, =0 {esta semana} =1 {1 semana} other {# semanas}}", + "Months": "{months, plural, =0 {este mês} =1 {1 mês} other {# meses}}", + "Years": "{years, plural, =0 {este ano} =1 {um ano} other {# anos}}", + "Before": "Antes", + "After": "Depois", + "MinutesShort": "{value}m", + "HoursShort": "{value}h", + "DaysShort": "{value}d", + "ShowMore": "Mostrar mais", + "ShowLess": "Mostrar menos", + "Search": "Pesquisar", + "SearchDots": "Pesquisar...", + "SearchResults": "Resultados da pesquisa", + "Suggested": "Sugestões", + "TimeTooltip": "{value}", + "SelectDate": "Selecionar data", + "None": "Nenhum", + "NotSelected": "Não selecionado", + "Today": "Hoje", + "NoDate": "Sem data", + "StartDate": "Data de início", + "TargetDate": "Data alvo", + "Overdue": "Atrasado", + "DueDate": "Data de vencimento", + "AddDueDate": "Adicionar data de vencimento", + "SetDueDate": "Definir data de vencimento", + "EditDueDate": "Editar data de vencimento", + "SaveDueDate": "Salvar data de vencimento", + "NeedsToBeCompletedByThisDate": "Precisa ser concluído até esta data", + "DueDatePopupTitle": "Vence em {value}", + "DueDatePopupOverdueTitle": "Venceu em {value}", + "DueDatePopupDescription": "{value, plural, =0 {Hoje} =1 {Amanhã} other {# dias restantes}}", + "DueDatePopupOverdueDescription": "{value, plural, =1 {1 dia em atraso} other {# dias em atraso}}", + "English": "English", + "Russian": "Русский", + "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", + "Spanish": "Español", + "Chinese": "中文", + "French": "Français", + "Italian": "Italiano", + "Czech": "Čeština", + "German": "Deutsch", + "Japanese": "日本語", + "Turkish": "Türkçe", + "MinutesBefore": "{minutes, plural, =1 {um minuto antes} other {# minutos antes}}", + "HoursBefore": "{hours, plural, =1 {uma hora antes} other {# horas antes}}", + "DaysBefore": "{days, plural, =1 {um dia antes} other {# dias antes}}", + "WeeksBefore": "{weeks, plural, =1 {uma semana antes} other {# semanas antes}}", + "MonthsBefore": "{months, plural, =1 {um mês antes} other {# meses antes}}", + "MinutesAfter": "{minutes, plural, =1 {em um minuto} other {em # minutos}}", + "HoursAfter": "{hours, plural, =1 {em uma hora} other {em # horas}}", + "DaysAfter": "{days, plural, =1 {em um dia} other {em # dias}}", + "WeeksAfter": "{weeks, plural, =1 {daqui a um dia} other {daqui a # semanas}}", + "MonthsAfter": "{months, plural, =1 {daqui a um mês} other {daqui a # meses}}", + "NoActionsDefined": "Nenhuma ação aplicável", + "Incoming": "Entrada", + "HoursLabel": "Horas", + "Back": "Voltar", + "Next": "Próximo", + "DropdownDefaultLabel": "Selecionar item", + "DD": "DD", + "MM": "MM", + "YYYY": "AAAA", + "HH": "HH", + "Remove": "Remover", + "NoResults": "Sem resultados", + "MoreCount": "{count} mais", + "Spacious": "Espaçoso", + "Compact": "Compacto", + "ThemeLight": "Claro", + "ThemeDark": "Escuro", + "ThemeSystem": "Sistema", + "NoTimeZonesFound": "Nenhum fuso horário encontrado", + "Selected": "Selecionado:", + "Submit": "Enviar", + "NextStep": "Próximo passo", + "TypeHere": "Digite aqui...", + "NormalSize": "Tamanho normal", + "FullSize": "Tamanho completo", + "UseMaxWidth": "Largura máxima", + "Sidebar": "Barra lateral", + "FontSize": "Tamanho da fonte", + "Language": "Idioma", + "DaysWOValue": "{days, plural, =1 {dia} other {dias}}", + "WeeksWOValue": "{weeks, plural, =1 {semana} other {semanas}}", + "MonthsWOValue": "{months, plural, =1 {mês} other {meses}}", + "Filter": "Filtrar", + "Clear": "Limpar", + "Yesterday": "Ontem", + "ThisWeek": "Esta semana", + "ThisMonth": "Este mês", + "ThisYear": "Este ano", + "EmojiStyle": "Estilo de emoji", + "EmojiSystem": "Padrão do sistema", + "EmojiNoto": "Noto Color" + } +} diff --git a/packages/ui/lang/pt.json b/packages/ui/lang/pt.json index 116b30fbb7..d7bb2b944b 100644 --- a/packages/ui/lang/pt.json +++ b/packages/ui/lang/pt.json @@ -50,6 +50,7 @@ "English": "English", "Russian": "Русский", "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", "Spanish": "Español", "Chinese": "中文", "French": "Français", diff --git a/packages/ui/lang/ru.json b/packages/ui/lang/ru.json index 28145e035b..6bac241ab9 100644 --- a/packages/ui/lang/ru.json +++ b/packages/ui/lang/ru.json @@ -51,6 +51,7 @@ "Russian": "Русский", "Spanish": "Español", "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", "Chinese": "中文", "French": "Français", "Italian": "Italiano", diff --git a/packages/ui/lang/tr.json b/packages/ui/lang/tr.json index b6b406f3e9..f7f5a89c2f 100644 --- a/packages/ui/lang/tr.json +++ b/packages/ui/lang/tr.json @@ -50,6 +50,7 @@ "English": "English", "Russian": "Русский", "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", "Spanish": "Español", "Chinese": "中文", "French": "Français", diff --git a/packages/ui/lang/zh.json b/packages/ui/lang/zh.json index 2627632a58..125f774112 100644 --- a/packages/ui/lang/zh.json +++ b/packages/ui/lang/zh.json @@ -50,6 +50,7 @@ "English": "English", "Russian": "Русский", "Portuguese": "Português", + "PortugueseBrazil": "Português (Brasil)", "Spanish": "Español", "Chinese": "中文", "French": "Français", diff --git a/packages/ui/package.json b/packages/ui/package.json index a206e7d69a..cac40eb1ee 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -49,8 +49,6 @@ "date-fns": "^2.30.0", "date-fns-tz": "^2.0.0", "dompurify": "^3.1.6", - "emojibase": "^16.0.0", - "emojibase-regex": "^16.0.0", "fast-equals": "^5.2.2", "svelte": "^4.2.20", "plyr": "3.7.8", diff --git a/packages/ui/src/components/EmbeddedPDF.svelte b/packages/ui/src/components/EmbeddedPDF.svelte index 0c063bf172..db4c12cb6c 100644 --- a/packages/ui/src/components/EmbeddedPDF.svelte +++ b/packages/ui/src/components/EmbeddedPDF.svelte @@ -14,29 +14,11 @@ --> -{#if iframeSrc} -