Compare commits

...

62 Commits

Author SHA1 Message Date
dependabot[bot]
6f8762f46e
build(deps): bump Alfresco/alfresco-build-tools (#10908)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.21.1 to 8.22.1.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](95f68dc050...b3070acf73)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.22.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-02 16:04:49 +01:00
MichalKinas
379fbbb508
[MNT-25043] Skip adding json as content type for FormData body requests (#10903) 2025-06-02 15:49:58 +02:00
Alfresco Build
342f165b41
New Crowdin translations by GitHub Action (#10887)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-06-02 12:46:53 +01:00
dependabot[bot]
ecba77dfa9
bump @angular-eslint/template-parser from 17.0.1 to 19.6.0 (#10907)
Bumps [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) from 17.0.1 to 19.6.0.
- [Release notes](https://github.com/angular-eslint/angular-eslint/releases)
- [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/template-parser/CHANGELOG.md)
- [Commits](https://github.com/angular-eslint/angular-eslint/commits/v19.6.0/packages/template-parser)

---
updated-dependencies:
- dependency-name: "@angular-eslint/template-parser"
  dependency-version: 19.6.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-02 12:13:32 +01:00
dependabot[bot]
a5b55c5861
bump postcss from 8.4.41 to 8.5.4 (#10906)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.41 to 8.5.4.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.41...8.5.4)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.4
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-02 12:10:59 +01:00
dependabot[bot]
2347ca68c4
build(deps): bump Alfresco/alfresco-build-tools from 8.21.1 to 8.22.1 (#10909)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.21.1 to 8.22.1.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](95f68dc050...b3070acf73)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.22.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-02 12:10:40 +01:00
AleksanderSklorz
cdb65bf204
[ACS-9377] remove the join button for records management library for user s without permissions for that library (#10901)
* [ACS-9377] Added function for file plans to get file plan roles

* [ACS-9377] Types and documentation

* [ACS-9377] Unit tests

* [ACS-9377] Fixed sonar cloud issue
2025-06-02 10:15:19 +02:00
Enrico Hilgendorf
8cb7a1719f
AAE-34656 Hide all counters in Workspace which do not provide value (#10896)
* Hide all counters in Workspace which do not provide value

* Fixed a copy & paste issue
2025-05-30 07:44:00 +02:00
dominikiwanekhyland
1971980216
[ACS-9619] Issues with trimming file's name in viewer (#10858) 2025-05-29 15:22:58 +02:00
Amedeo Lepore
467a19f3ec
AAE-34959 Fix withCredentials can not be set by app.config.json (#10897)
* AAE-34959 Run security options loader when alfresco-api-v2-loader.serfvice is initialized because on content app the app-config.loader is run after AlfrescoApiLoaderService.init and the callback function that should setDefaultSecurityOption is not executed

* AAE-34959 Provide SecurityOptionsLoaderService in root to fix No Provider error
2025-05-29 10:24:17 +02:00
Mykyta Maliarchuk
d1e48f9c33
[ACS-9247] Extend library column context (#10894)
* [ACS-9247] extend library column context

* [ACS-9247] cr fix
2025-05-29 10:11:07 +02:00
Soumyajit Chakraborty
79163cbae0
AAE-34482 Fixing label and alignment issues in forms (#10898)
* AAE-34482 fixing label and alignment issues in forms

* AAE-34482 fixing margin issues

* AAE-34482 adding span inside mat-label

* AAE-34482 fixing units

* AAE-34482 removing unwanted unit

* AAE-34482 fixing unit

* AAE-34482 fixing native element class in unit tests
2025-05-28 23:20:17 +05:30
dependabot[bot]
5bbb5c5716
build(deps-dev): bump stylelint from 16.8.2 to 16.19.1 (#10874)
Bumps [stylelint](https://github.com/stylelint/stylelint) from 16.8.2 to 16.19.1.
- [Release notes](https://github.com/stylelint/stylelint/releases)
- [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stylelint/stylelint/compare/16.8.2...16.19.1)

---
updated-dependencies:
- dependency-name: stylelint
  dependency-version: 16.19.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eugenio Romano <eromano@users.noreply.github.com>
2025-05-26 14:02:58 -04:00
dependabot[bot]
0018cd7f8f
build(deps): bump formidable from 3.5.2 to 3.5.4 (#10895)
Bumps [formidable](https://github.com/node-formidable/formidable) from 3.5.2 to 3.5.4.
- [Release notes](https://github.com/node-formidable/formidable/releases)
- [Changelog](https://github.com/node-formidable/formidable/blob/master/CHANGELOG.md)
- [Commits](https://github.com/node-formidable/formidable/commits)

---
updated-dependencies:
- dependency-name: formidable
  dependency-version: 3.5.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 15:35:14 +02:00
Michael Couné
06921783bf
AAE-33907 Adds input for 'Open next task' checkbox (#10823)
* [AAE-33907] Adds input for 'Open next task' checkbox

* Adds input for 'Open next task' checkbox

Adds an input to the task screen component to determine whether the "Open next task" checkbox is checked by default.

Also, adds an output that emits an event when the state of the "Open next task" checkbox changes.

* [AAE-33907] added condition for isNextTaskCheckboxChecked

* [AAE-33907] added showNextTaskCheckbox property and moved condition

* Adds next task checkbox functionality.

* Adds support for next task navigation

* Enhances screen cloud component testing

* Makes openNextTask optional for complete task

* removed tests

* Cleans up unnecessary blank lines in spec file

* fixed unit test
2025-05-23 11:50:38 +02:00
Enrico Hilgendorf
1462560e6e
AAE-35057 Change position of adf-cloud-form-content-card-fullscreen to relative (#10888) 2025-05-23 07:00:18 +02:00
dependabot[bot]
ecbfae5648
build(deps): bump @babel/runtime and @angular-devkit/build-angular (#10885)
Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) to 7.26.10 and updates ancestor dependency [@angular-devkit/build-angular](https://github.com/angular/angular-cli). These dependencies need to be updated together.


Updates `@babel/runtime` from 7.25.0 to 7.26.10
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-runtime)

Updates `@angular-devkit/build-angular` from 18.2.14 to 18.2.19
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/18.2.14...18.2.19)

---
updated-dependencies:
- dependency-name: "@babel/runtime"
  dependency-version: 7.26.10
  dependency-type: indirect
- dependency-name: "@angular-devkit/build-angular"
  dependency-version: 18.2.19
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-22 12:43:00 +01:00
Tomasz Nastaly
413fce8cb7
AAE-34641 Refactor viewer-render loading (#10868) 2025-05-22 10:07:26 +02:00
Bartosz Sekula
f2fa458fe5
AAE-33909 Ensure onProcessFinish event triggers when invoked from onFormLoaded event (#10867)
* AAE-33909 Ensure onProcessFinish event triggers when invoked from onFormLoaded event

* clean code

* fix unit tests

* update outcomes buttons

* update
2025-05-21 16:01:05 +02:00
Swetha Balasubramaniam
e37ef279e4
AAE-32986 Add custom-form-widget process to simpleapp (#10881)
* Update simpleapp.zip

* Update

* Update date-fns-utils.spec.ts

* Changes
2025-05-21 08:20:58 +01:00
AleksanderSklorz
2cc3ba4d6b
[ACS-7706] create tags return promise tag paging instead of tag entry (#10869)
* [ACS-7706] Corrected returned type for createTags function

* [ACS-7706] Updated documentation for createTags and fixed unit tests
2025-05-21 08:08:49 +02:00
tomasz hanaj
3fea334468
[AAE-34479] added class for custom outcome button (#10882)
Co-authored-by: Eugenio Romano <eromano@users.noreply.github.com>
2025-05-20 21:52:43 -04:00
dependabot[bot]
541ae4a266
build(deps): bump zgosalvez/github-actions-ensure-sha-pinned-actions (#10876)
Bumps [zgosalvez/github-actions-ensure-sha-pinned-actions](https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions) from 3.0.24 to 3.0.25.
- [Release notes](https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions/releases)
- [Commits](2d6823da40...fc87bb5b5a)

---
updated-dependencies:
- dependency-name: zgosalvez/github-actions-ensure-sha-pinned-actions
  dependency-version: 3.0.25
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 21:51:14 -04:00
dependabot[bot]
8633641235
build(deps): bump github/codeql-action from 3.28.17 to 3.28.18 (#10877)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.17 to 3.28.18.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](60168efe1c...ff0a06e83c)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.28.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 21:50:57 -04:00
dependabot[bot]
206d83e84a
build(deps): bump Alfresco/alfresco-build-tools from 8.20.0 to 8.21.1 (#10878)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.20.0 to 8.21.1.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](8cd8c3798c...95f68dc050)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.21.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 21:50:38 -04:00
dependabot[bot]
4a1e4544f4
build(deps): bump Alfresco/alfresco-build-tools (#10879)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.20.0 to 8.21.1.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](8cd8c3798c...95f68dc050)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.21.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 21:50:18 -04:00
Alfresco Build
f70ba30b46
New Crowdin translations by GitHub Action (#10884)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-05-20 21:49:59 -04:00
Tomasz Gnyp
b3900b96ed
AAE-34972 Test PR for adf link (#10861) 2025-05-20 15:57:02 +02:00
Soumyajit Chakraborty
2fd960bf5c
AAE-34885 Form with tabs has incomplete frame in preview (#10880)
* AAE-34885 Form tabs preview frame size and width fix

* AAE-34885 fixing form preview in modeling app

* AAE-34485 workspace-app form scroll fix

* AAE-34485 fixing margin issues on the modeling app form preview

* fixing width issues on preview

---------

Co-authored-by: Eugenio Romano <eromano@users.noreply.github.com>
2025-05-19 15:40:06 +05:30
Bartosz Sekula
8b47434c37
AAE-33052 Deprecate custom theme (#10870) 2025-05-16 15:28:08 +01:00
Denys Vuika
fe53c5d5a8
MNT-25095 Update the documentation (#10864)
* update the documentation

* update the documentation

---------

Co-authored-by: Eugenio Romano <eromano@users.noreply.github.com>
2025-05-15 19:11:29 -04:00
dependabot[bot]
deefa948cd
build(deps): bump undici in /.github/actions/print-affected-libs (#10866)
Bumps [undici](https://github.com/nodejs/undici) from 5.28.5 to 5.29.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v5.28.5...v5.29.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 5.29.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eugenio Romano <eromano@users.noreply.github.com>
2025-05-15 19:06:54 -04:00
Amedeo Lepore
051b82684f
AAE-34959 Allow disabling withCredentials for the identity providers that disallow credentials (#10859) 2025-05-15 21:27:22 +02:00
Eugenio Romano
b4eee9d631
AAE-34675 Fix default selection required (#10860)
* fix default selection required

* rename test

* dropdown form field return object

* dropdown form field return object

* dropdown form field return object

* fix test

* Update lib/core/src/lib/form/components/widgets/core/form-field.model.spec.ts

Co-authored-by: Ehsan Rezaei <ehsan.rezaei@hyland.com>

* Update lib/core/src/lib/form/components/widgets/core/form-field.model.ts

Co-authored-by: Ehsan Rezaei <ehsan.rezaei@hyland.com>

---------

Co-authored-by: Ehsan Rezaei <ehsan.rezaei@hyland.com>
2025-05-15 21:26:17 +02:00
dependabot[bot]
9c6a1901c6
build(deps-dev): bump http-proxy-middleware from 2.0.7 to 2.0.9 (#10863)
Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.7 to 2.0.9.
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.9/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.7...v2.0.9)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-version: 2.0.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-15 16:51:53 +01:00
Ruggero Corsaletti
3ad13d3e38
AAE-34992 Improve crowdin update strategy (#10865) 2025-05-15 16:51:47 +01:00
Wojciech Duda
efd6e5b1b0
AAE-34826 Incorrect scale for rendered pdf documents (#10857)
* AAE-34826 Fix missing class for pdf viewer

* AAE-34826 Remove double initial scale

* AAE-34826 Fix missing scale

* AAE-34826 Remove unnecessary condition

* AAE-34826 Remove surplus id

* AAE-34826 converted scale to string

---------

Co-authored-by: Ehsan Rezaei <ehsan.rezaei@hyland.com>
2025-05-15 12:50:36 +01:00
Vito Albano
6257510056
Ng18 migration (#10683)
* [MIGRATION] - Angular 18

* [ci:force][MIGRATION] - fixed Apollo new import

* [MIGRATION] - rebased to lastest

* [MIGRATION] - updated style to use still material 2 and postpone material migration

* [AAE-32974] - sync lock with version 8

* [AAE-33014] - Fixed m2 material issue

* Rebased to latest

* Rebased to latest

* [ACS-9159] Upgraded node version (#10782)

* [MIGRATION] - Added missing package

* [MIGRATION] - check if hte module mapping hide the warning

* [MIGRATION] - Readded wrongly removed package

* Upgrade TS

---------

Co-authored-by: AleksanderSklorz <115619721+AleksanderSklorz@users.noreply.github.com>
Co-authored-by: DominikIwanek <dominik.iwanek@hyland.com>
2025-05-15 12:33:07 +01:00
Ehsan Rezaei
28137d8a09
Revert "AAE-34641 Fix form loading when tab is changed (#10843)" (#10862)
This reverts commit 685bc387b585db77694355378309213cfde356e3.
2025-05-15 12:07:07 +02:00
Alfresco Build
81822a119d
New Crowdin translations by GitHub Action (#10854)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-05-14 16:48:50 -04:00
dependabot[bot]
43d0c00feb
build(deps): bump formidable from 3.5.1 to 3.5.4 in /lib/cli (#10833)
Bumps [formidable](https://github.com/node-formidable/formidable) from 3.5.1 to 3.5.4.
- [Release notes](https://github.com/node-formidable/formidable/releases)
- [Changelog](https://github.com/node-formidable/formidable/blob/master/CHANGELOG.md)
- [Commits](https://github.com/node-formidable/formidable/commits)

---
updated-dependencies:
- dependency-name: formidable
  dependency-version: 3.5.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Eugenio Romano <eromano@users.noreply.github.com>
2025-05-14 16:47:05 -04:00
dependabot[bot]
7c127888db
build(deps): bump Alfresco/alfresco-build-tools from 8.19.0 to 8.20.0 (#10839)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.19.0 to 8.20.0.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](09293790e3...8cd8c3798c)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-13 21:40:59 +02:00
Alfresco Build
d342643bd8
New Crowdin translations by GitHub Action (#10852)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-05-13 19:07:47 +02:00
Soumyajit Chakraborty
58a5732043
AAE-34888 fixing padding issues in form widgets (#10853) 2025-05-13 18:28:40 +05:30
Tomasz Nastaly
685bc387b5
AAE-34641 Fix form loading when tab is changed (#10843) 2025-05-13 12:55:45 +02:00
Alexander Puschkin
f0c90594ca
AAE-34731 Hide the open next task checkbox for the Start Process view (#10842)
* Fix the "open next task" checkbox is not showing anymore in the process start section

* Add test

* adjust description of the test

* revert the format changes

* prettier

* rebase changes fix

* prettier

* fix tests
2025-05-13 11:16:35 +02:00
dependabot[bot]
65f6e8f4de
build(deps-dev): bump vite from 5.4.18 to 5.4.19 (#10851)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.18 to 5.4.19.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.19/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.19/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 5.4.19
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-12 11:36:55 -04:00
Soumyajit Chakraborty
ec47d8eac8
AAE-34869 fixing padding issue (#10850) 2025-05-12 20:19:47 +05:30
dependabot[bot]
ca6e3969d8
build(deps): bump slackapi/slack-github-action from 2.0.0 to 2.1.0 (#10848)
Bumps [slackapi/slack-github-action](https://github.com/slackapi/slack-github-action) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/slackapi/slack-github-action/releases)
- [Commits](485a9d42d3...b0fa283ad8)

---
updated-dependencies:
- dependency-name: slackapi/slack-github-action
  dependency-version: 2.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-12 10:21:28 -04:00
dependabot[bot]
b4e8e4943a
build(deps): bump zgosalvez/github-actions-ensure-sha-pinned-actions (#10849)
Bumps [zgosalvez/github-actions-ensure-sha-pinned-actions](https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions) from 3.0.23 to 3.0.24.
- [Release notes](https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions/releases)
- [Commits](4830be28ce...2d6823da40)

---
updated-dependencies:
- dependency-name: zgosalvez/github-actions-ensure-sha-pinned-actions
  dependency-version: 3.0.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-12 10:20:40 -04:00
dependabot[bot]
71b8dae86d
build(deps): bump Alfresco/alfresco-build-tools (#10837)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.18.3 to 8.20.0.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](247f59bac1...8cd8c3798c)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-12 10:18:33 -04:00
dependabot[bot]
84e5a0fa4b
build(deps): bump github/codeql-action from 3.28.16 to 3.28.17 (#10838)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.16 to 3.28.17.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](28deaeda66...60168efe1c)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.28.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-12 10:18:11 -04:00
Ewelina Dikta
f2845a36c6
[AAE-29434] Adding process with process variables to simpleapp (#10845) 2025-05-12 09:03:50 +02:00
Soumyajit Chakraborty
acf9e3e11c
Improvement/aae 30909 enhance user experience with tab navigation within the form (#10844)
* AAE-30909 tab-header fixed at the top

* AAE-30909 fixing widh and scrolling issues in workspace and studio-hxp
2025-05-08 19:23:30 +05:30
Soumyajit Chakraborty
adeb82c137
AAE-34335 fix for styling discrepancies in form-widgets (#10841) 2025-05-06 18:33:28 +05:30
Wojciech Duda
48defa2b5f
AAE-34594 Remove redundant scroll bar from form renderer (#10840) 2025-05-06 10:19:09 +01:00
Robert Duda
9ee0e5ee3e
AAE-19688 Persist feature flag overrides in session storage (#10832)
* AAE-19688 Persist feature flag overrides in session storage

* unit tests
2025-04-30 16:59:50 +02:00
dependabot[bot]
61a1fb64bf
build(deps): bump Alfresco/alfresco-build-tools from 8.18.1 to 8.18.2 (#10805)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.18.1 to 8.18.2.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](45bb8b6647...e524cd3bb3)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.18.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-30 14:38:26 +01:00
Fabian Kindgen
0f1200b5a2
AAE-34514 Disable next task checkbox for forms (#10824) 2025-04-29 09:48:27 +02:00
dependabot[bot]
2c438760ea
build(deps): bump Alfresco/alfresco-build-tools from 8.18.1 to 8.18.3 (#10827)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.18.1 to 8.18.3.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](45bb8b6647...247f59bac1)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.18.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-28 13:46:30 -04:00
dependabot[bot]
56adef9d92
build(deps): bump github/codeql-action from 3.28.15 to 3.28.16 (#10826)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.15 to 3.28.16.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](45775bd823...28deaeda66)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.28.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-28 13:45:58 -04:00
dependabot[bot]
60a8cdb8b8
build(deps): bump Alfresco/alfresco-build-tools (#10828)
Bumps [Alfresco/alfresco-build-tools](https://github.com/alfresco/alfresco-build-tools) from 8.18.2 to 8.18.3.
- [Release notes](https://github.com/alfresco/alfresco-build-tools/releases)
- [Commits](e524cd3bb3...247f59bac1)

---
updated-dependencies:
- dependency-name: Alfresco/alfresco-build-tools
  dependency-version: 8.18.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-28 13:45:37 -04:00
147 changed files with 10915 additions and 6893 deletions

View File

@ -267,9 +267,9 @@
}
},
"node_modules/undici": {
"version": "5.28.5",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz",
"integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==",
"version": "5.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
"license": "MIT",
"dependencies": {
"@fastify/busboy": "^2.0.0"
@ -495,9 +495,9 @@
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
},
"undici": {
"version": "5.28.5",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.5.tgz",
"integrity": "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==",
"version": "5.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
"requires": {
"@fastify/busboy": "^2.0.0"
}

View File

@ -26,7 +26,7 @@ runs:
cache-dependency-path: package-lock.json
- name: get latest tag sha
id: tag-sha
uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@e524cd3bb34350e247b0348cc8e4a6e0b4fd6c42 # v8.18.2
uses: Alfresco/alfresco-build-tools/.github/actions/git-latest-tag@f37c44cc085236178ebb8834eb634b0b4b550a40 # v8.23.0
# CACHE
- name: Node Modules cache
id: node-modules-cache

View File

@ -30,7 +30,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
# Override language selection by uncommenting this and choosing your languages
with:
languages: javascript
@ -39,7 +39,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -53,4 +53,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18

View File

@ -64,7 +64,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Ensure SHA pinned actions
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@4830be28ce81da52ec70d65c552a7403821d98d4 # v3.0.23
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@fc87bb5b5a97953d987372e74478de634726b3e5 # v3.0.25
- name: Check package-lock.json version
run: |
@ -84,10 +84,10 @@ jobs:
fetch-depth: 0
- name: Get branch name
uses: Alfresco/alfresco-build-tools/.github/actions/get-branch-name@45bb8b664779b691f8a21d9fd49e360916726c11 # v8.18.1
uses: Alfresco/alfresco-build-tools/.github/actions/get-branch-name@b3070acf733fdcfd2698065e99017d4969256e5b # v8.22.1
- name: Save commit message
uses: Alfresco/alfresco-build-tools/.github/actions/get-commit-message@45bb8b664779b691f8a21d9fd49e360916726c11 # v8.18.1
uses: Alfresco/alfresco-build-tools/.github/actions/get-commit-message@b3070acf733fdcfd2698065e99017d4969256e5b # v8.22.1
- name: ci:force flag parser
shell: bash
@ -277,7 +277,7 @@ jobs:
uses: ./.github/actions/slack-group-area
with:
affected: ${{ steps.e2e-result.outputs.result }}
- uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d # v2.0.0
- uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0
name: Nofify QA failure
if: ${{ github.event_name == 'schedule' && contains(needs.*.result, 'failure') }}
env:

View File

@ -229,7 +229,7 @@ jobs:
needs: [release-storybook, release-npm, npm-check-bundle]
steps:
- uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d # v2.0.0
- uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0
name: Nofify FE eng-guild-front-end workflow failed
if: ${{ contains(toJson(needs.*.result), 'failure') }}
env:

2
.nvmrc
View File

@ -1 +1 @@
20.18.1
22.14.0

View File

@ -8,6 +8,6 @@
"source": "/**/**/i18n/en.json",
"translation": "/%original_path%/%two_letters_code%.%file_extension%",
"export_only_approved": "true",
"update_option": "update_as_unapproved"
"update_option": "update_without_changes"
}
]

View File

@ -23,10 +23,10 @@ Manages tags in Content Services.
- _nodeId:_ `string` - Id of node to which tags should be assigned.
- _tags:_ `TagBody[]` - List of tags to create and assign or just assign if they already exist.
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagPaging`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagPaging.md)`|`[`TagEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagEntry.md)`>` - Just linked tags to node or single tag if linked only one tag.
- **createTags**(tags: `TagBody[]`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagEntry.md)`[]>`<br/>
- **createTags**(tags: `TagBody[]`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagEntry`](../../../lib/js-api/src/api/content-rest-api/docs/TagsApi.md#TagEntry) `|` [`TagPaging`](../../../lib/js-api/src/api/content-rest-api/docs/TagsApi.md#TagPaging)`>`<br/>
Creates tags.
- _tags:_ `TagBody[]` - list of tags to create.
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagEntry`](https://github.com/Alfresco/alfresco-js-api/blob/master/src/alfresco-core-rest-api/docs/TagEntry.md)`[]>` - Created tags.
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TagEntry`](../../../lib/js-api/src/api/content-rest-api/docs/TagsApi.md#TagEntry) `|` [`TagPaging`](../../../lib/js-api/src/api/content-rest-api/docs/TagsApi.md#TagPaging)`>` - Created tags.
- **deleteTag**(tagId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<void>`<br/>
Deletes a tag with tagId. This will cause the tag to be removed from all nodes. You must have admin rights to delete a tag.
- _tagId:_ `string` - of the tag to be deleted

View File

@ -4,16 +4,18 @@ Added: v4.1.0
---
## Form Extensibility for APA Form Widget
This page describes how you can customize ADF forms to your own specification.
## Contents
There are two ways to customize the form
- [Replace default form widgets with custom components](#replace-default-form-widgets-with-apa-form-widgets)
- [Replace custom form widget with custom components](#replace-custom-form-widgets-with-custom-components)
## Replace default form widgets with APA form widgets
This is an example of replacing the standard `Text` [widget](../../lib/testing/src/lib/core/pages/form/widgets/widget.ts) with a custom component for all APA forms rendered within the `<adf-form>` component.
This is an example of replacing the standard `Text` with a custom component for all APA forms rendered within the `<adf-form>` component.
1. Create a simple form with some `Text` widgets:
@ -24,8 +26,10 @@ This is an example of replacing the standard `Text` [widget](../../lib/testing/s
```ts
import { Component } from '@angular/core';
import { WidgetComponent } from '@alfresco/adf-core';
@Component({
selector: 'custom-editor',
standalone: true,
template: `
<div style="color: red">Look, I'm a APA custom editor!</div>
`
@ -33,40 +37,14 @@ This is an example of replacing the standard `Text` [widget](../../lib/testing/s
export class CustomEditorComponent extends WidgetComponent {}
```
2. Add it to the application module or any custom module that is imported into the application one:
2. Import the [`FormRenderingService`](../core/services/form-rendering.service.md) in the feature module, or application module (recommended: `ProcessServicesExtensionModule`), and override the default mapping:
```ts
import { NgModule } from '@angular/core';
import { CustomEditorComponent } from './custom-editor.component';
@NgModule({
declarations: [ CustomEditorComponent ],
exports: [ CustomEditorComponent ]
})
export class CustomEditorsModule {}
```
import { FormRenderingService } from '@alfresco/adf-core';
3. Every custom widget component should be added into the the collections `declarations` and `exports`. If you decided to store custom widgets in a separate dedicated module (and optionally as a separate re-distributable library) don't forget to import it into the main application:
```ts
@NgModule({
imports: [
// ...
CustomEditorsModule
// ...
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {}
```
4. Import the [`FormRenderingService`](../core/services/form-rendering.service.md) into any of your Views and override the default mapping, for example:
```ts
import { Component } from '@angular/core';
import { CustomEditorComponent } from './custom-editor.component';
@Component({...})
export class MyView {
@NgModule({...})
export class ProcessServicesExtensionModule {
constructor(formRenderingService: FormRenderingService) {
this.formRenderingService.register({
'text': () => CustomEditorComponent
@ -75,10 +53,12 @@ This is an example of replacing the standard `Text` [widget](../../lib/testing/s
}
```
5. At runtime the form should look similar to the following:
> [!IMPORTANT]
> The widget should be registered outside the custom widget component, otherwise the widget will not be registered correctly.
![custom text widget](../docassets/images/apa-simple-override-form.png)
At runtime the form should look similar to the following:
![custom text widget](../docassets/images/apa-simple-override-form.png)
## Replace custom form widgets with custom components
@ -105,54 +85,30 @@ When displayed in a task, the field will look similar to the following:
To render the missing content:
1. Create an Angular component:
1. Create a standalone Angular component:
```ts
import { Component } from '@angular/core';
import { WidgetComponent } from '@alfresco/adf-core';
@Component({
selector: 'app-demo-widget',
standalone: true,
template: `<div style="color: green">ADF version of custom form widget</div>`
})
export class DemoWidgetComponent extends WidgetComponent {}
```
2. Place it inside the custom module:
2. Import the [`FormRenderingService`](../core/services/form-rendering.service.md) in the feature module, or application module (recommended: `ProcessServicesExtensionModule`), and override the default mapping:
```ts
import { NgModule } from '@angular/core';
import { DemoWidgetComponent } from './demo-widget.component';
@NgModule({
declarations: [ DemoWidgetComponent ],
exports: [ DemoWidgetComponent ]
})
export class CustomWidgetsModule {}
```
import { FormRenderingService } from '@alfresco/adf-core';
3. Import it into your Application Module:
```ts
@NgModule({
imports: [
// ...
CustomWidgetsModule
// ...
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule {}
```
4. Import the [`FormRenderingService`](../core/services/form-rendering.service.md) in any of your Views and provide the new mapping:
```ts
import { Component } from '@angular/core';
import { DemoWidgetComponent } from './demo-widget.component';
@Component({...})
export class MyView {
@NgModule({/*...*/})
export class ProcessServicesExtensionModule {
constructor(formRenderingService: FormRenderingService) {
this.formRenderingService.register({
formRenderingService.register({
'custom-editor': () => DemoWidgetComponent
});
}
@ -163,6 +119,9 @@ At runtime you should now see your custom Angular component rendered in place of
![adf form widget runtime](../docassets/images/apa-resolved-widget.png)
> [!IMPORTANT]
> The widget should be registered outside the custom widget component, otherwise the widget will not be registered correctly.
## See Also
- [Extensibility](./extensibility.md)

View File

@ -9,7 +9,7 @@
"version": "8.0.0",
"license": "Apache-2.0",
"dependencies": {
"@alfresco/js-api": ">=8.0.0-alpha.7-0",
"@alfresco/js-api": ">=8.0.0-alpha.7",
"commander": "^6.2.1",
"ejs": "^3.1.9",
"license-checker": "^25.0.1",
@ -39,6 +39,27 @@
"tslib": "^2.6.1"
}
},
"node_modules/@noble/hashes": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@paralleldrive/cuid2": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz",
"integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "^1.1.5"
}
},
"node_modules/@types/ejs": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.2.tgz",
@ -435,14 +456,18 @@
}
},
"node_modules/formidable": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz",
"integrity": "sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==",
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz",
"integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==",
"license": "MIT",
"dependencies": {
"@paralleldrive/cuid2": "^2.2.2",
"dezalgo": "^1.0.4",
"hexoid": "^1.0.0",
"once": "^1.4.0"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"url": "https://ko-fi.com/tunnckoCore/commissions"
}
@ -576,14 +601,6 @@
"node": ">= 0.4"
}
},
"node_modules/hexoid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
"integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==",
"engines": {
"node": ">=8"
}
},
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",

View File

@ -19,6 +19,7 @@ import { AlfrescoApiConfig } from '@alfresco/js-api';
import { Injectable } from '@angular/core';
import { AppConfigService, AppConfigValues, StorageService } from '@alfresco/adf-core';
import { AlfrescoApiService } from '../services/alfresco-api.service';
import { SecurityOptionsLoaderService } from '../security-options-loader/security-options-loader.service';
/**
* Create a factory to resolve an api service instance
@ -37,11 +38,12 @@ export class AlfrescoApiLoaderService {
constructor(
private readonly appConfig: AppConfigService,
private readonly apiService: AlfrescoApiService,
private readonly securityOptionsLoaderService: SecurityOptionsLoaderService,
private storageService: StorageService
) {}
async init(): Promise<any> {
await this.appConfig.load();
await this.appConfig.load(this.securityOptionsLoaderService.load);
return this.initAngularAlfrescoApi();
}

View File

@ -92,4 +92,19 @@ describe('LibraryRoleColumnComponent', () => {
fixture.detectChanges();
expect(value).toBe('LIBRARY.ROLE.NONE');
});
it('should take role from obj when node entry role is not provided', () => {
component.context = {
row: {
node: { entry: {} },
obj: { role: 'SiteManager' }
}
};
let value = '';
component.displayText$.subscribe((val) => (value = val));
fixture.detectChanges();
expect(value).toBe('LIBRARY.ROLE.MANAGER');
});
});

View File

@ -17,7 +17,7 @@
import { ChangeDetectionStrategy, Component, DestroyRef, inject, Input, OnInit, ViewEncapsulation } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { Site, SiteEntry } from '@alfresco/js-api';
import { Site } from '@alfresco/js-api';
import { ShareDataRow } from '../../data/share-data-row.model';
import { NodesApiService } from '../../../common/services/nodes-api.service';
import { CommonModule } from '@angular/common';
@ -64,9 +64,7 @@ export class LibraryRoleColumnComponent implements OnInit {
}
protected updateValue() {
const node: SiteEntry = this.context.row.node;
if (node?.entry) {
const role: string = node.entry.role;
const role = this.context.row.node?.entry.role ?? this.context.row.obj.role;
switch (role) {
case Site.RoleEnum.SiteManager:
this.displayText$.next('LIBRARY.ROLE.MANAGER');
@ -85,5 +83,4 @@ export class LibraryRoleColumnComponent implements OnInit {
break;
}
}
}
}

View File

@ -16,9 +16,54 @@
*/
import { LibraryStatusColumnComponent } from './library-status-column.component';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ContentTestingModule } from '../../../testing/content.testing.module';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { Site } from '@alfresco/js-api';
describe('LibraryStatusColumnComponent', () => {
let fixture: ComponentFixture<LibraryStatusColumnComponent>;
let component: LibraryStatusColumnComponent;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ContentTestingModule, LibraryStatusColumnComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
fixture = TestBed.createComponent(LibraryStatusColumnComponent);
component = fixture.componentInstance;
});
it('should be defined', () => {
expect(LibraryStatusColumnComponent).toBeDefined();
});
it('should take default visibility from node entry', () => {
component.context = {
row: {
node: { entry: { visibility: Site.VisibilityEnum.PUBLIC } }
}
};
let value = '';
component.displayText$.subscribe((val) => (value = val));
fixture.detectChanges();
expect(value).toBe('LIBRARY.VISIBILITY.PUBLIC');
});
it('should take visibility from obj when node entry visibility is not provided', () => {
component.context = {
row: {
node: { entry: {} },
obj: { visibility: Site.VisibilityEnum.PUBLIC }
}
};
let value = '';
component.displayText$.subscribe((val) => (value = val));
fixture.detectChanges();
expect(value).toBe('LIBRARY.VISIBILITY.PUBLIC');
});
});

View File

@ -18,7 +18,7 @@
import { Component, DestroyRef, inject, Input, OnInit } from '@angular/core';
import { NodesApiService } from '../../../common/services/nodes-api.service';
import { BehaviorSubject } from 'rxjs';
import { Site, SiteEntry } from '@alfresco/js-api';
import { Site } from '@alfresco/js-api';
import { ShareDataRow } from '../../data/share-data-row.model';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
@ -62,9 +62,7 @@ export class LibraryStatusColumnComponent implements OnInit {
}
protected updateValue() {
const node: SiteEntry = this.context.row.node;
if (node?.entry) {
const visibility: string = node.entry.visibility;
const visibility = this.context.row.node?.entry.visibility ?? this.context.row.obj.visibility;
switch (visibility) {
case Site.VisibilityEnum.PUBLIC:
@ -81,5 +79,4 @@ export class LibraryStatusColumnComponent implements OnInit {
break;
}
}
}
}

View File

@ -711,10 +711,10 @@
},
"NODE_FAVORITE_DIRECTIVE": {
"MESSAGES": {
"NODE_ADDED": "Added {{ name }} to favorites",
"NODES_ADDED": "Added {{ number }} items to favorites",
"NODE_REMOVED": "Removed {{ name }} from favorites",
"NODES_REMOVED": "Removed {{ number }} items from favorites"
"NODE_ADDED": "Añadido {{ name }} a favoritos",
"NODES_ADDED": "Añadidos {{ number }} elementos a favoritos",
"NODE_REMOVED": "Eliminado {{ name }} de favoritos",
"NODES_REMOVED": "Eliminado {{ number }} elementos de favoritos"
}
}
}

View File

@ -711,10 +711,10 @@
},
"NODE_FAVORITE_DIRECTIVE": {
"MESSAGES": {
"NODE_ADDED": "Added {{ name }} to favorites",
"NODES_ADDED": "Added {{ number }} items to favorites",
"NODE_REMOVED": "Removed {{ name }} from favorites",
"NODES_REMOVED": "Removed {{ number }} items from favorites"
"NODE_ADDED": "Ajout de {{ name }} aux favoris",
"NODES_ADDED": "Ajout des éléments {{ number }} aux favoris",
"NODE_REMOVED": "Suppression de {{ name }} des favoris",
"NODES_REMOVED": "Les éléments {{ number }} supprimés des favoris"
}
}
}

View File

@ -711,10 +711,10 @@
},
"NODE_FAVORITE_DIRECTIVE": {
"MESSAGES": {
"NODE_ADDED": "Added {{ name }} to favorites",
"NODES_ADDED": "Added {{ number }} items to favorites",
"NODE_REMOVED": "Removed {{ name }} from favorites",
"NODES_REMOVED": "Removed {{ number }} items from favorites"
"NODE_ADDED": "Aggiunto {{ name }} ai preferiti",
"NODES_ADDED": "Aggiunti {{ number }} elementi ai preferiti",
"NODE_REMOVED": "Rimosso {{ name }} dai preferiti",
"NODES_REMOVED": "Rimosso {{ number }} elementi dai preferiti"
}
}
}

View File

@ -711,10 +711,10 @@
},
"NODE_FAVORITE_DIRECTIVE": {
"MESSAGES": {
"NODE_ADDED": "Added {{ name }} to favorites",
"NODES_ADDED": "Added {{ number }} items to favorites",
"NODE_REMOVED": "Removed {{ name }} from favorites",
"NODES_REMOVED": "Removed {{ number }} items from favorites"
"NODE_ADDED": "Dodano {{ name }} do ulubionych",
"NODES_ADDED": "Dodano {{ number }} elementy do ulubionych",
"NODE_REMOVED": "Usunięto {{ name }} z ulubionych",
"NODES_REMOVED": "Usunięto {{ number }} elementy z ulubionych"
}
}
}

View File

@ -711,10 +711,10 @@
},
"NODE_FAVORITE_DIRECTIVE": {
"MESSAGES": {
"NODE_ADDED": "Added {{ name }} to favorites",
"NODES_ADDED": "Added {{ number }} items to favorites",
"NODE_REMOVED": "Removed {{ name }} from favorites",
"NODES_REMOVED": "Removed {{ number }} items from favorites"
"NODE_ADDED": "Adicionado {{ name }} aos favoritos",
"NODES_ADDED": "Adicionados {{ number }} itens aos favoritos",
"NODE_REMOVED": "Removido {{ name }} dos favoritos",
"NODES_REMOVED": "Removido {{ number }} itens dos favoritos"
}
}
}

View File

@ -69,6 +69,7 @@ export abstract class InfiniteScrollDatasource<T> extends DataSource<T> {
reset(): void {
this.isLoading$.next(true);
this.dataStream.next([]);
this.getNextBatch({ skipCount: 0, maxItems: this.batchSize })
.pipe(take(1))
.subscribe((firstBatch) => {

View File

@ -0,0 +1,73 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { SecurityOptionsLoaderService } from './security-options-loader.service';
import { AppConfigService, AppConfigValues } from '@alfresco/adf-core';
import { AdfHttpClient } from '@alfresco/adf-core/api';
describe('SecurityOptionsLoaderService', () => {
let service: SecurityOptionsLoaderService;
let appConfigServiceSpy: jasmine.SpyObj<AppConfigService>;
let adfHttpClientSpy: jasmine.SpyObj<AdfHttpClient>;
beforeEach(() => {
appConfigServiceSpy = jasmine.createSpyObj('AppConfigService', ['get']);
adfHttpClientSpy = jasmine.createSpyObj('AdfHttpClient', ['setDefaultSecurityOption']);
service = new SecurityOptionsLoaderService(appConfigServiceSpy, adfHttpClientSpy);
});
it('should set withCredentials when value is true', () => {
appConfigServiceSpy.get.and.callFake((key: string): any => {
if (key === AppConfigValues.AUTH_WITH_CREDENTIALS) {
return true;
}
});
service.load();
expect(adfHttpClientSpy.setDefaultSecurityOption).toHaveBeenCalledWith({ withCredentials: true });
});
it('should set withCredentials when value is false', () => {
appConfigServiceSpy.get.and.callFake((key: string): any => {
if (key === AppConfigValues.AUTH_WITH_CREDENTIALS) {
return false;
}
});
service.load();
expect(adfHttpClientSpy.setDefaultSecurityOption).toHaveBeenCalledWith({ withCredentials: false });
});
it('should not call setDefaultSecurityOption when value is undefined', () => {
appConfigServiceSpy.get.and.returnValue(undefined);
service.load();
expect(adfHttpClientSpy.setDefaultSecurityOption).not.toHaveBeenCalled();
});
it('should not call setDefaultSecurityOption when value is null', () => {
appConfigServiceSpy.get.and.returnValue(null);
service.load();
expect(adfHttpClientSpy.setDefaultSecurityOption).not.toHaveBeenCalled();
});
});

View File

@ -0,0 +1,34 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { AppConfigService, AppConfigValues } from '@alfresco/adf-core';
import { AdfHttpClient } from '@alfresco/adf-core/api';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class SecurityOptionsLoaderService {
constructor(private appConfigService: AppConfigService, private adfHttpClient: AdfHttpClient) {}
load = () => {
const withCredentials = this.appConfigService.get<boolean>(AppConfigValues.AUTH_WITH_CREDENTIALS);
if (withCredentials !== undefined && withCredentials !== null) {
this.adfHttpClient.setDefaultSecurityOption({ withCredentials });
}
};
}

View File

@ -89,7 +89,7 @@ describe('TagService', () => {
describe('createTags', () => {
it('should call createTags on tagsApi', () => {
spyOn(service.tagsApi, 'createTags').and.returnValue(Promise.resolve([]));
spyOn(service.tagsApi, 'createTags').and.returnValue(Promise.resolve({}));
const tag1 = new TagBody();
tag1.tag = 'Some tag 1';
const tag2 = new TagBody();
@ -101,19 +101,17 @@ describe('TagService', () => {
});
it('should emit refresh when tags creation is success', async () => {
const tags: TagEntry[] = [
{
const tag: TagEntry = {
entry: {
id: 'Some id 1',
tag: 'Some tag 1'
}
}
];
};
spyOn(service.refresh, 'emit');
spyOn(service.tagsApi, 'createTags').and.returnValue(Promise.resolve(tags));
spyOn(service.tagsApi, 'createTags').and.returnValue(Promise.resolve(tag));
await service.createTags([]).toPromise();
expect(service.refresh.emit).toHaveBeenCalledWith(tags);
expect(service.refresh.emit).toHaveBeenCalledWith(tag);
});
});

View File

@ -99,7 +99,7 @@ export class TagService {
* @param tags list of tags to create.
* @returns Created tags.
*/
createTags(tags: TagBody[]): Observable<TagEntry[]> {
createTags(tags: TagBody[]): Observable<TagEntry | TagPaging> {
return from(this.tagsApi.createTags(tags)).pipe(tap((tagEntries) => this.refresh.emit(tagEntries)));
}

View File

@ -45,7 +45,7 @@ export class AuthenticationInterceptor implements HttpInterceptor {
if (req.context.get(SHOULD_ADD_AUTH_TOKEN)) {
return this.authService.addTokenToHeader(req.url, req.headers).pipe(
mergeMap((headersWithBearer) => {
const headerWithContentType = this.appendJsonContentType(headersWithBearer);
const headerWithContentType = this.appendJsonContentType(headersWithBearer, req.body);
const kcReq = req.clone({ headers: headerWithContentType });
return next.handle(kcReq).pipe(catchError((error) => observableThrowError(error)));
})
@ -55,7 +55,7 @@ export class AuthenticationInterceptor implements HttpInterceptor {
return next.handle(req).pipe(catchError((error) => observableThrowError(error)));
}
private appendJsonContentType(headers: HttpHeaders): HttpHeaders {
private appendJsonContentType(headers: HttpHeaders, reqBody: any): HttpHeaders {
// prevent adding any content type, to properly handle formData with boundary browser generated value,
// as adding any Content-Type its going to break the upload functionality
@ -63,7 +63,7 @@ export class AuthenticationInterceptor implements HttpInterceptor {
return headers.delete('Content-Type');
}
if (!headers.get('Content-Type')) {
if (!headers.get('Content-Type') && !(reqBody instanceof FormData)) {
return headers.set('Content-Type', 'application/json;charset=UTF-8');
}

View File

@ -2,15 +2,15 @@
@use '@angular/material' as mat;
@mixin adf-breadcrumb-theme($theme) {
$config: mat.get-color-config($theme);
$config: mat.m2-get-color-config($theme);
$foreground-palette: map.get($config, foreground);
$primary-palette: map.get($config, primary);
$text-color: mat.get-color-from-palette($foreground-palette, text);
$primary: mat.get-color-from-palette($primary-palette, text);
$text-color: mat.m2-get-color-from-palette($foreground-palette, text);
$primary: mat.m2-get-color-from-palette($primary-palette, text);
adf-breadcrumb {
.adf-breadcrumb__show-all-button-icon--rotate {
color: mat.get-color-from-palette($primary-palette, 500);
color: mat.m2-get-color-from-palette($primary-palette, 500);
}
.adf-breadcrumb__item-wrapper {

View File

@ -0,0 +1,3 @@
# DEPRECATION WARNING
The custom theme is deprecated and will be removed in the future. To generate a custom theme, please refer to the Angular Material [Custom Theme section](https://v18.material.angular.dev/guide/theming#custom-theme).

View File

@ -2,22 +2,19 @@
@use '@angular/material' as mat;
@import './theme/theme-data';
$custom-theme: mat.define-light-theme(
$custom-theme: mat.m2-define-light-theme(
(
color: (
primary: map.get($palettes, primary),
accent: map.get($palettes, accent),
warn: map.get($palettes, warning),
warn: map.get($palettes, warning)
),
typography: $app-typography,
typography: $app-typography
)
);
@if $background-color {
$custom-theme: get-custom-background-color(
$background-color,
$custom-theme
);
$custom-theme: get-custom-background-color($background-color, $custom-theme);
}
@if $text-color {

View File

@ -1,31 +1,27 @@
@use '@angular/material' as mat;
@import './default-colors.scss';
@import './custom-palette-creator.scss';
@import './default-colors';
@import './custom-palette-creator';
@function get-mat-palettes($primary-color, $accent-color) {
$mat-primary-palette: null;
@if ($primary-color) {
$custom-theme-primary-palette: create-color-palette($primary-color, 'primary');
$mat-primary-palette: mat.define-palette($custom-theme-primary-palette, 500);
$mat-primary-palette: mat.m2-define-palette($custom-theme-primary-palette, 500);
} @else {
$mat-primary-palette: mat.define-palette($default-primary, A100);
$mat-primary-palette: mat.m2-define-palette($default-primary, A100);
}
$mat-accent-palette: null;
@if ($accent-color) {
$custom-theme-accent-palette: create-color-palette($accent-color, 'accent');
$mat-accent-palette: mat.define-palette($custom-theme-accent-palette, 500);
$mat-accent-palette: mat.m2-define-palette($custom-theme-accent-palette, 500);
} @else {
$mat-accent-palette: mat.define-palette($default-accent);
$mat-accent-palette: mat.m2-define-palette($default-accent);
}
$mat-warn-palette: mat.define-palette($default-warn, A100);
$mat-warn-palette: mat.m2-define-palette($default-warn, A100);
@return (
primary: $mat-primary-palette,
accent: $mat-accent-palette,
warning: $mat-warn-palette,
);
@return (primary: $mat-primary-palette, accent: $mat-accent-palette, warning: $mat-warn-palette);
}

View File

@ -3,37 +3,37 @@
@import '../variables/font-family';
@function get-mat-typography($base-font-size, $font-family) {
$custom-typography: mat.define-typography-config(
$custom-typography: mat.m2-define-typography-config(
$font-family: 'Muli, Roboto, "Helvetica Neue", sans-serif',
$headline-1: mat.define-typography-level(112px, 112px, 300),
$headline-2: mat.define-typography-level(56px, 56px, 400),
$headline-3: mat.define-typography-level(45px, 48px, 400),
$headline-4: mat.define-typography-level(34px, 40px, 400),
$headline-5: mat.define-typography-level(24px, 32px, 400),
$headline-6: mat.define-typography-level(20px, 32px, 500),
$subtitle-1: mat.define-typography-level(16px, 28px, 400),
$body-1: mat.define-typography-level(15px, 24px, 400),
$subtitle-2: mat.define-typography-level(14px, 24px, 500),
$body-2: mat.define-typography-level(14px, 20px, 400),
$caption: mat.define-typography-level(12px, 20px, 400),
$button: mat.define-typography-level(14px, 14px, 500),
$headline-1: mat.m2-define-typography-level(112px, 112px, 300),
$headline-2: mat.m2-define-typography-level(56px, 56px, 400),
$headline-3: mat.m2-define-typography-level(45px, 48px, 400),
$headline-4: mat.m2-define-typography-level(34px, 40px, 400),
$headline-5: mat.m2-define-typography-level(24px, 32px, 400),
$headline-6: mat.m2-define-typography-level(20px, 32px, 500),
$subtitle-1: mat.m2-define-typography-level(16px, 28px, 400),
$body-1: mat.m2-define-typography-level(15px, 24px, 400),
$subtitle-2: mat.m2-define-typography-level(14px, 24px, 500),
$body-2: mat.m2-define-typography-level(14px, 20px, 400),
$caption: mat.m2-define-typography-level(12px, 20px, 400),
$button: mat.m2-define-typography-level(14px, 14px, 500),
// Line-height must be unit-less fraction of the font-size.
);
@if $base-font-size {
$custom-typography: mat.define-typography-config(
$headline-1: mat.define-typography-level(8rem, 8rem, 300),
$headline-2: mat.define-typography-level(4rem, 4rem, 400),
$headline-3: mat.define-typography-level(3.21rem, 3.21rem, 400),
$headline-4: mat.define-typography-level(2.42rem, 2.85rem, 400),
$headline-5: mat.define-typography-level(1.71rem, 2.28rem, 400),
$headline-6: mat.define-typography-level(1.42rem, 2.28rem, 500),
$subtitle-1: mat.define-typography-level(1.14rem, 2rem, 400),
$body-1: mat.define-typography-level(1.07rem, 1.71rem, 400),
$subtitle-2: mat.define-typography-level(1rem, 1.71rem, 500),
$body-2: mat.define-typography-level(1rem, 1.42rem, 400),
$caption: mat.define-typography-level(0.86rem, 1.42rem, 400),
$button: mat.define-typography-level(1rem, 1rem, 500),
$custom-typography: mat.m2-define-typography-config(
$headline-1: mat.m2-define-typography-level(8rem, 8rem, 300),
$headline-2: mat.m2-define-typography-level(4rem, 4rem, 400),
$headline-3: mat.m2-define-typography-level(3.21rem, 3.21rem, 400),
$headline-4: mat.m2-define-typography-level(2.42rem, 2.85rem, 400),
$headline-5: mat.m2-define-typography-level(1.71rem, 2.28rem, 400),
$headline-6: mat.m2-define-typography-level(1.42rem, 2.28rem, 500),
$subtitle-1: mat.m2-define-typography-level(1.14rem, 2rem, 400),
$body-1: mat.m2-define-typography-level(1.07rem, 1.71rem, 400),
$subtitle-2: mat.m2-define-typography-level(1rem, 1.71rem, 500),
$body-2: mat.m2-define-typography-level(1rem, 1.42rem, 400),
$caption: mat.m2-define-typography-level(0.86rem, 1.42rem, 400),
$button: mat.m2-define-typography-level(1rem, 1rem, 500),
$font-family: $default-font-family
);
}

View File

@ -17,37 +17,37 @@
import { TestBed } from '@angular/core/testing';
import { DebugFeaturesService } from './debug-features.service';
import { StorageService } from '../../../../src/lib/common/services/storage.service';
import { OverridableFeaturesServiceToken, WritableFeaturesServiceToken } from '../interfaces/features.interface';
import { OverridableFeaturesServiceToken, WritableFeaturesServiceConfigToken, WritableFeaturesServiceToken } from '../interfaces/features.interface';
import { DummyFeaturesService } from './dummy-features.service';
import { StorageFeaturesService } from './storage-features.service';
import { take } from 'rxjs/operators';
describe('DebugFeaturesService', () => {
let service: DebugFeaturesService;
const mockStorage = {
getItem: () =>
JSON.stringify({
feature1: {
current: true
},
feature2: {
current: false,
fictive: true
}
}),
setItem: () => {}
};
let mockStorageKey: string;
let mockStorage;
beforeEach(() => {
mockStorageKey = 'storage-key-test';
mockStorage = { [mockStorageKey]: true };
TestBed.configureTestingModule({
providers: [
DebugFeaturesService,
{ provide: StorageService, useValue: mockStorage },
{
provide: WritableFeaturesServiceConfigToken,
useValue: { storageKey: mockStorageKey }
},
{ provide: WritableFeaturesServiceToken, useClass: StorageFeaturesService },
{ provide: OverridableFeaturesServiceToken, useClass: DummyFeaturesService }
]
});
spyOn(sessionStorage, 'getItem').and.callFake((key) => JSON.stringify(mockStorage[key]));
spyOn(sessionStorage, 'setItem').and.callFake((key, value) => {
mockStorage[key] = value;
});
service = TestBed.inject(DebugFeaturesService);
});

View File

@ -16,8 +16,8 @@
*/
import { Inject, Injectable, Optional } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { skip, switchMap } from 'rxjs/operators';
import { BehaviorSubject, Observable, combineLatest } from 'rxjs';
import { filter, switchMap } from 'rxjs/operators';
import {
IDebugFeaturesService,
IFeaturesService,
@ -29,12 +29,12 @@ import {
FlagSet,
IWritableFeaturesService
} from '../interfaces/features.interface';
import { StorageService } from '@alfresco/adf-core';
@Injectable()
export class DebugFeaturesService implements IDebugFeaturesService {
private isInDebugMode: BehaviorSubject<boolean>;
private isInDebugMode$: Observable<boolean>;
private readonly isInDebugModeSubject = new BehaviorSubject<boolean>(false);
private readonly isInDebugMode$ = this.isInDebugModeSubject.asObservable();
private readonly initSubject = new BehaviorSubject<boolean>(false);
get storageKey(): string {
return `${this.config?.storageKey || 'feature-flags'}-override`;
@ -43,14 +43,15 @@ export class DebugFeaturesService implements IDebugFeaturesService {
constructor(
@Inject(OverridableFeaturesServiceToken) private overriddenFeaturesService: IFeaturesService,
@Inject(WritableFeaturesServiceToken) private writableFeaturesService: IFeaturesService & IWritableFeaturesService,
private storageService: StorageService,
@Optional() @Inject(WritableFeaturesServiceConfigToken) private config?: WritableFeaturesServiceConfig
) {
this.isInDebugMode = new BehaviorSubject<boolean>(JSON.parse(this.storageService.getItem(this.storageKey) || 'false'));
this.isInDebugMode$ = this.isInDebugMode.asObservable();
this.init();
this.isInDebugMode.pipe(skip(1)).subscribe((debugMode) => {
this.storageService.setItem(this.storageKey, JSON.stringify(debugMode));
combineLatest({
debugMode: this.isInDebugModeSubject,
init: this.waitForInitializationToFinish()
}).subscribe(({ debugMode }) => {
sessionStorage.setItem(this.storageKey, JSON.stringify(debugMode));
});
}
@ -87,10 +88,20 @@ export class DebugFeaturesService implements IDebugFeaturesService {
}
enable(on: boolean): void {
this.isInDebugMode.next(on);
this.isInDebugModeSubject.next(on);
}
isEnabled$(): Observable<boolean> {
return this.isInDebugMode$;
}
private init() {
const storedOverride = JSON.parse(sessionStorage.getItem(this.storageKey) || 'false');
this.isInDebugModeSubject.next(storedOverride);
this.initSubject.next(true);
}
private waitForInitializationToFinish(): Observable<boolean> {
return this.initSubject.pipe(filter((initialized) => !!initialized));
}
}

View File

@ -17,17 +17,20 @@
import { TestBed } from '@angular/core/testing';
import { StorageFeaturesService } from './storage-features.service';
import { StorageService } from '../../../../src/public-api';
import { FlagSet, WritableFeaturesServiceConfigToken } from '../interfaces/features.interface';
import { skip, take } from 'rxjs/operators';
describe('StorageFeaturesService', () => {
let storageFeaturesService: StorageFeaturesService;
describe('if flags are present in LocalStorage', () => {
const mockStorage = {
getItem: () =>
JSON.stringify({
describe('if flags are present in sessionStorage', () => {
let mockStorageKey: string;
let mockStorage;
beforeEach(() => {
mockStorageKey = 'storage-key-test';
mockStorage = {
[mockStorageKey]: {
feature1: {
current: true
},
@ -35,23 +38,23 @@ describe('StorageFeaturesService', () => {
current: false,
fictive: true
}
}),
setItem: () => {}
}
};
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: StorageService, useValue: mockStorage },
{
provide: WritableFeaturesServiceConfigToken,
useValue: {
storageKey: 'storage-key-test'
}
useValue: { storageKey: mockStorageKey }
}
]
});
spyOn(sessionStorage, 'getItem').and.callFake((key) => JSON.stringify(mockStorage[key]));
spyOn(sessionStorage, 'setItem').and.callFake((key, value) => {
mockStorage[key] = value;
});
storageFeaturesService = TestBed.inject(StorageFeaturesService);
storageFeaturesService.init();
});

View File

@ -16,8 +16,8 @@
*/
import { Inject, Injectable, Optional } from '@angular/core';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { map, skip } from 'rxjs/operators';
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import {
FlagChangeset,
IFeaturesService,
@ -28,21 +28,21 @@ import {
WritableFeaturesServiceConfig
} from '../interfaces/features.interface';
import { FlagSetParser } from './flagset.parser';
import { StorageService } from '@alfresco/adf-core';
@Injectable({ providedIn: 'root' })
export class StorageFeaturesService implements IFeaturesService, IWritableFeaturesService {
private currentFlagState: WritableFlagChangeset = {};
private flags = new BehaviorSubject<WritableFlagChangeset>({});
private flags$ = this.flags.asObservable();
private readonly flags = new BehaviorSubject<WritableFlagChangeset>({});
private readonly flags$ = this.flags.asObservable();
private readonly initSubject = new BehaviorSubject<boolean>(false);
constructor(
private storageService: StorageService,
@Optional() @Inject(WritableFeaturesServiceConfigToken) private config?: WritableFeaturesServiceConfig
) {
this.flags.pipe(skip(1)).subscribe((flags) => {
constructor(@Optional() @Inject(WritableFeaturesServiceConfigToken) private config?: WritableFeaturesServiceConfig) {
combineLatest({
flags: this.flags,
init: this.waitForInitializationToFinish()
}).subscribe(({ flags }) => {
this.currentFlagState = flags;
this.storageService.setItem(this.storageKey, JSON.stringify(FlagSetParser.serialize(flags)));
sessionStorage.setItem(this.storageKey, JSON.stringify(FlagSetParser.serialize(flags)));
});
}
@ -51,9 +51,10 @@ export class StorageFeaturesService implements IFeaturesService, IWritableFeatur
}
init(): Observable<WritableFlagChangeset> {
const storedFlags = JSON.parse(this.storageService.getItem(this.storageKey) || '{}');
const storedFlags = JSON.parse(sessionStorage.getItem(this.storageKey) || '{}');
const initialFlagChangeSet = FlagSetParser.deserialize(storedFlags);
this.flags.next(initialFlagChangeSet);
this.initSubject.next(true);
return of(initialFlagChangeSet);
}
@ -133,4 +134,8 @@ export class StorageFeaturesService implements IFeaturesService, IWritableFeatur
this.flags.next(mergedFlags);
}
private waitForInitializationToFinish(): Observable<boolean> {
return this.initSubject.pipe(filter((initialized) => !!initialized));
}
}

View File

@ -0,0 +1,123 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { AdfHttpClient } from '@alfresco/adf-core/api';
import { StorageService } from '../common';
import { StoragePrefixFactory } from './app-config-storage-prefix.factory';
import { loadAppConfig } from './app-config.loader';
import { AppConfigService, AppConfigValues } from './app-config.service';
import { fakeAsync, TestBed, tick } from '@angular/core/testing';
import { HttpClientModule } from '@angular/common/http';
import { of } from 'rxjs';
describe('loadAppConfig', () => {
let appConfigServiceSpy: jasmine.SpyObj<AppConfigService>;
let storageServiceSpy: jasmine.SpyObj<StorageService>;
let adfHttpClientSpy: jasmine.SpyObj<AdfHttpClient>;
let storagePrefixFactorySpy: jasmine.SpyObj<StoragePrefixFactory>;
let appConfigGetSpy: jasmine.Spy;
let appConfigLoadSpy: jasmine.Spy;
let factoryFunction: () => void;
beforeEach(() => {
adfHttpClientSpy = jasmine.createSpyObj('AdfHttpClient', ['setDefaultSecurityOption']);
storagePrefixFactorySpy = jasmine.createSpyObj('StoragePrefixFactory', ['getPrefix']);
TestBed.configureTestingModule({
imports: [HttpClientModule],
providers: [
{ provide: AppConfigService },
{ provide: StorageService },
{
provide: AdfHttpClient,
useValue: adfHttpClientSpy
},
{ provide: StoragePrefixFactory, useValue: storagePrefixFactorySpy }
]
});
appConfigServiceSpy = TestBed.inject(AppConfigService) as jasmine.SpyObj<AppConfigService>;
appConfigGetSpy = spyOn(appConfigServiceSpy, 'get');
appConfigLoadSpy = spyOn(appConfigServiceSpy, 'load');
appConfigLoadSpy.and.callFake((callback: () => void) => {
callback();
});
storageServiceSpy = TestBed.inject(StorageService) as jasmine.SpyObj<StorageService>;
spyOnProperty(storageServiceSpy, 'prefix', 'get').and.callThrough();
storagePrefixFactorySpy.getPrefix.and.returnValue({ subscribe: () => {} } as any);
factoryFunction = loadAppConfig(appConfigServiceSpy, storageServiceSpy, adfHttpClientSpy, storagePrefixFactorySpy);
});
it('should disable CSRF based on app config', () => {
appConfigGetSpy.and.callFake((key: string): any => {
if (key === AppConfigValues.DISABLECSRF) {
return true;
}
});
factoryFunction();
expect(appConfigServiceSpy.get).toHaveBeenCalledWith('disableCSRF', true);
expect(adfHttpClientSpy.disableCsrf).toBeTrue();
});
it('should set default security option when auth.withCredentials is defined', () => {
appConfigServiceSpy.get.and.callFake((key: string): any => {
if (key === AppConfigValues.AUTH_WITH_CREDENTIALS) {
return true;
}
});
factoryFunction();
expect(adfHttpClientSpy.setDefaultSecurityOption).toHaveBeenCalledWith({ withCredentials: true });
});
it('should set storage prefix from app config', () => {
appConfigServiceSpy.get.and.callFake((key: string, _default): any => {
if (key === AppConfigValues.STORAGE_PREFIX) {
return 'test-prefix';
}
});
factoryFunction();
expect(appConfigServiceSpy.get).toHaveBeenCalledWith(AppConfigValues.STORAGE_PREFIX, '');
expect(storageServiceSpy.prefix).toEqual('test-prefix_');
});
it('should update storage prefix from storagePrefixFactory', fakeAsync(() => {
storagePrefixFactorySpy.getPrefix.and.returnValue(of('new-amazing-prefix'));
factoryFunction();
tick();
expect(storagePrefixFactorySpy.getPrefix).toHaveBeenCalled();
expect(storageServiceSpy.prefix).toEqual('new-amazing-prefix_');
}));
it('should call appConfigService.load with the init function', () => {
factoryFunction();
expect(appConfigLoadSpy).toHaveBeenCalledWith(jasmine.any(Function));
});
});

View File

@ -37,6 +37,11 @@ export function loadAppConfig(
) {
const init = () => {
adfHttpClient.disableCsrf = appConfigService.get<boolean>(AppConfigValues.DISABLECSRF, true);
const withCredentials = appConfigService.get<boolean>(AppConfigValues.AUTH_WITH_CREDENTIALS);
if (withCredentials !== undefined && withCredentials !== null) {
adfHttpClient.setDefaultSecurityOption({ withCredentials });
}
storageService.prefix = appConfigService.get<string>(AppConfigValues.STORAGE_PREFIX, '');
storagePrefixFactory.getPrefix().subscribe((property) => {

View File

@ -18,6 +18,7 @@
import { Directive, EventEmitter, Input, Output } from '@angular/core';
import { ThemePalette } from '@angular/material/core';
import { FormFieldModel, FormFieldValidator, FormModel, FormOutcomeEvent, FormOutcomeModel } from './widgets';
import { isOutcomeButtonVisible } from './helpers/buttons-visibility';
@Directive({
standalone: true
@ -103,10 +104,6 @@ export abstract class FormBaseComponent {
*/
formStyle: string = '';
get hasVisibleOutcomes(): boolean {
return this.form?.outcomes?.some((outcome) => this.isOutcomeButtonVisible(outcome, this.form.readOnly));
}
get form(): FormModel {
return this._form;
}
@ -169,22 +166,7 @@ export abstract class FormBaseComponent {
}
isOutcomeButtonVisible(outcome: FormOutcomeModel, isFormReadOnly: boolean): boolean {
if (outcome?.name) {
if (outcome.name === FormOutcomeModel.COMPLETE_ACTION) {
return this.showCompleteButton;
}
if (isFormReadOnly) {
return outcome.isSelected;
}
if (outcome.name === FormOutcomeModel.SAVE_ACTION) {
return this.showSaveButton;
}
if (outcome.name === FormOutcomeModel.START_PROCESS_ACTION) {
return false;
}
return true;
}
return false;
return isOutcomeButtonVisible(outcome, { isFormReadOnly, showCompleteButton: this.showCompleteButton, showSaveButton: this.showSaveButton });
}
/**

View File

@ -73,6 +73,7 @@ export class FormFieldComponent implements OnInit, OnDestroy {
if (w.adf === undefined) {
w.adf = {};
}
const originalField = this.getField();
if (originalField) {
const customTemplate = this.field.form.customFieldTemplates[originalField.type];

View File

@ -4,7 +4,11 @@
<div *ngIf="hasTabs()" class="alfresco-tabs-widget">
<mat-tab-group>
<mat-tab *ngFor="let tab of visibleTabs()" [label]="tab.title | translate ">
<ng-template matTabContent>
<div class="adf-form-tab-content">
<ng-template *ngTemplateOutlet="render; context: { fieldToRender: tab.fields }" />
</div>
</ng-template>
</mat-tab>
</mat-tab-group>
</div>
@ -38,7 +42,8 @@
<section class="adf-grid-list-column-view" *ngIf="currentRootElement?.isExpanded">
<div class="adf-grid-list-single-column"
*ngFor="let column of currentRootElement?.columns"
[style.width.%]="getColumnWidth(currentRootElement)">
[style.width.%]="getColumnWidth(currentRootElement)"
>
<ng-container *ngFor="let field of column?.fields">
<ng-container *ngIf="field.type === 'section'; else formField">
<adf-form-section [field]="field"/>
@ -59,9 +64,11 @@
</div>
</ng-template>
</div>
<div *ngIf="currentRootElement.type === 'dynamic-table'" class="adf-container-widget">
<adf-form-field [field]="currentRootElement" />
</div>
<div class="adf-container-widget"
*ngIf="currentRootElement.type === 'readonly' && currentRootElement.field.params.field.type === 'dynamic-table'">
<adf-form-field [field]="currentRootElement.field"/>

View File

@ -13,6 +13,43 @@
height: 100%;
}
.alfresco-tabs-widget {
width: 100%;
.adf-form-tab-content {
margin-top: 1em;
}
.adf-form-tab-group {
width: 100%;
}
#{ms.$mat-tab-body} {
margin-bottom: 8em;
}
#{ms.$mat-tab-header} {
z-index: 10;
margin: 0;
background-color: white;
position: absolute;
width: 96%;
/* stylelint-disable-next-line declaration-no-important */
margin-left: 0 !important;
/* stylelint-disable-next-line declaration-no-important */
margin-right: 10px !important;
top: 0;
}
#{ms.$mat-tab-body-wrapper} {
padding-top: 5%;
}
}
.mat-mdc-card-content:first-child {
padding-top: 1em;
}
.adf-container-widget {
.adf-grid-list {
display: grid;
@ -24,6 +61,7 @@
display: flex;
margin-right: -1%;
width: 100%;
&-item {
width: 100%;
@ -115,12 +153,18 @@
overflow: hidden;
}
& #{ms.$mat-tab-header} {
position: fixed;
z-index: 1000;
}
& #{ms.$mat-card-header-text} {
margin: 0;
}
& #{ms.$mat-tab-body-content} {
overflow: hidden;
padding-top: 0;
}
& #{mat-tab-label-text} {

View File

@ -0,0 +1,45 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { FormOutcomeModel } from '../widgets';
interface IsOutcomeButtonVisibleProps {
isFormReadOnly: boolean;
showCompleteButton: boolean;
showSaveButton: boolean;
}
export const isOutcomeButtonVisible = (outcome: FormOutcomeModel, props: IsOutcomeButtonVisibleProps): boolean => {
const { isFormReadOnly, showCompleteButton, showSaveButton } = props;
if (outcome?.name) {
if (outcome.name === FormOutcomeModel.COMPLETE_ACTION) {
return showCompleteButton;
}
if (isFormReadOnly) {
return outcome.isSelected;
}
if (outcome.name === FormOutcomeModel.SAVE_ACTION) {
return showSaveButton;
}
if (outcome.name === FormOutcomeModel.START_PROCESS_ACTION) {
return false;
}
return true;
}
return false;
};

View File

@ -10,9 +10,9 @@
>
</div>
<div>
<mat-form-field class="adf-amount-widget__input" [hideRequiredMarker]="true">
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id"
>{{field.name | translate }}<span class="adf-asterisk" [style.visibility]="isRequired() ? 'visible' : 'hidden'">*</span></label
<mat-form-field class="adf-amount-widget__input" [hideRequiredMarker]="true" [floatLabel]="placeholder ? 'always' : 'auto'">
<mat-label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id"
>{{field.name | translate }}<span class="adf-asterisk" [style.visibility]="isRequired() ? 'visible' : 'hidden'">*</span></mat-label
>
<span matTextPrefix class="adf-amount-widget__prefix-spacing">{{ currency }}&nbsp;</span>
<input

View File

@ -24,6 +24,10 @@
align-self: flex-end;
}
.adf-input {
margin-top: 3px;
}
&:not(#{ms.$mat-focused}):not(#{ms.$mat-form-field-invalid}) {
.adf-amount-widget__prefix-spacing {
color: var(--adf-theme-foreground-secondary-text-color);

View File

@ -192,7 +192,7 @@ describe('AmountWidgetComponent - rendering', () => {
expect(inputField).toBeTruthy();
expect(await field.getPrefixText()).toBe('$');
const widgetLabel = testingUtils.getByCSS('label.adf-label').nativeElement;
const widgetLabel = testingUtils.getByCSS('.adf-label').nativeElement;
expect(widgetLabel.textContent.trim()).toBe('Test Amount*');
expect(widget.field.isValid).toBe(false);
@ -228,7 +228,7 @@ describe('AmountWidgetComponent - rendering', () => {
fixture.detectChanges();
await fixture.whenStable();
const widgetLabel = testingUtils.getByCSS('label.adf-label').nativeElement;
const widgetLabel = testingUtils.getByCSS('.adf-label').nativeElement;
expect(widgetLabel.textContent.trim()).toBe('Test Amount*');
const field = await testingUtils.getMatFormField();

View File

@ -146,7 +146,7 @@ describe('FormFieldModel', () => {
});
expect(field.options).toEqual([{ id: 'id_one', name: 'One' }]);
expect(field.value).toEqual('id_one');
expect(field.value).toEqual({ id: 'id_one', name: 'One' });
});
it('should add value (selected options) to field options if NOT present (multiple selection)', () => {
@ -176,7 +176,7 @@ describe('FormFieldModel', () => {
expect(field.hasEmptyValue).toBe(true);
expect(field.emptyOption).toEqual({ id: 'empty', name: 'Chose one...' });
expect(field.value).toEqual('empty');
expect(field.value).toEqual({ id: 'empty', name: 'Chose one...' });
});
it('should set hasEmptyValue to true if "empty" option is present in options', () => {
@ -272,7 +272,8 @@ describe('FormFieldModel', () => {
options: [],
value: { id: 'delayed-option-id', name: 'Delayed option' }
});
expect(field.value).toBe('delayed-option-id');
expect(field.value).toEqual({ id: 'delayed-option-id', name: 'Delayed option' });
});
});
});
@ -770,7 +771,7 @@ describe('FormFieldModel', () => {
];
});
it('should update form with selected option and options from which we chose', () => {
it('should update form with selected option and options from which we chose when is a string', () => {
field.value = 'restOpt2';
field.updateForm();
@ -1023,7 +1024,7 @@ describe('FormFieldModel', () => {
expect(field.options).toEqual(staticOptions);
});
it('should selected option appear in form values', () => {
it('should selected option appear in form values string', () => {
const field = getFieldConfig('manual', staticOptions, 'opt2');
field.updateForm();
@ -1031,6 +1032,15 @@ describe('FormFieldModel', () => {
expect(field.value).toEqual('opt2');
expect(field.form.values['dropdown_field']).toEqual({ id: 'opt2', name: 'Option 2' });
});
it('should selected option appear in form values obj', () => {
const field = getFieldConfig('manual', staticOptions, { id: 'opt3', name: 'opt3' });
field.updateForm();
expect(field.value).toEqual({ id: 'opt3', name: 'opt3' });
expect(field.form.values['dropdown_field']).toEqual({ id: 'opt3', name: 'opt3' });
});
});
describe('radio buttons field', () => {

View File

@ -331,13 +331,13 @@ export class FormFieldModel extends FormWidgetModel {
const isEmptyValue = !value || [this.emptyOption.id, this.emptyOption.name].includes(value);
if (isEmptyValue) {
return this.emptyOption.id;
return this.emptyOption;
}
}
if (this.isValidOption(value)) {
this.addOption({ id: value.id, name: value.name });
return value.id;
return value;
}
if (this.hasMultipleValues) {
@ -436,6 +436,17 @@ export class FormFieldModel extends FormWidgetModel {
this.form.values[this.id] = matchingOption || null;
}
if (typeof this.value === 'object') {
if (this.value.id === 'empty' || this.value.id === '') {
this.form.values[this.id] = null;
break;
}
const matchingOption: FormFieldOption = this.options.find((opt) => opt.id === this.value.id);
this.form.values[this.id] = matchingOption;
}
break;
}
case FormFieldTypes.RADIO_BUTTONS: {

View File

@ -358,7 +358,7 @@ export class FormModel implements ProcessFormModel {
this.handleSectionField(field, formFieldModel);
} else if (this.isContainerField(field)) {
this.handleContainerField(field, formFieldModel);
} else {
} else if (this.isFormField(field)) {
this.handleSingleField(field, formFieldModel);
}
});
@ -368,6 +368,10 @@ export class FormModel implements ProcessFormModel {
return field instanceof ContainerModel;
}
private isFormField(field: ContainerModel | FormFieldModel): field is FormFieldModel {
return field instanceof FormFieldModel;
}
private isSectionField(field: ContainerModel | FormFieldModel): field is FormFieldModel {
return field.type === FormFieldTypes.SECTION;
}

View File

@ -11,9 +11,9 @@
<mat-form-field class="adf-date-time-widget"
[class.adf-left-label-input-datepicker]="field.leftLabels"
[hideRequiredMarker]="true">
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">
<mat-label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">
{{ field.name | translate }} ({{ field.dateDisplayFormat }})<span class="adf-asterisk" [style.visibility]="isRequired() ? 'visible' : 'hidden'">*</span>
</label>
</mat-label>
<input matInput
[matDatetimepicker]="datetimePicker"
[id]="field.id"

View File

@ -10,9 +10,9 @@
<div>
<mat-form-field [hideRequiredMarker]="true">
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">
<mat-label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">
{{ field.name | translate }}<span class="adf-asterisk" [style.visibility]="isRequired() ? 'visible' : 'hidden'">*</span>
</label>
</mat-label>
<input matInput
class="adf-input"

View File

@ -2,9 +2,9 @@
[class.adf-invalid]="!field.isValid && isTouched()"
[class.adf-readonly]="field.readOnly">
<mat-form-field floatPlaceholder="never" [hideRequiredMarker]="true">
<label class="adf-label" [attr.for]="field.id">
<mat-label class="adf-label" [attr.for]="field.id">
{{ field.name | translate }}<span class="adf-asterisk" [style.visibility]="isRequired() ? 'visible' : 'hidden'">*</span>
</label>
</mat-label>
<textarea matInput
class="adf-input"
[cdkTextareaAutosize]="true"

View File

@ -10,9 +10,9 @@
</div>
<div>
<mat-form-field [hideRequiredMarker]="true">
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">
<mat-label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">
{{ field.name | translate }}<span class="adf-asterisk" [style.visibility]="isRequired() ? 'visible' : 'hidden'">*</span>
</label>
</mat-label>
<input matInput
class="adf-input"
type="text"

View File

@ -9,9 +9,9 @@
</div>
<div>
<mat-form-field [hideRequiredMarker]="true">
<label *ngIf="!field.leftLabels" class="adf-label" [attr.for]="field.id">
<mat-label *ngIf="!field.leftLabels" class="adf-label" [attr.for]="field.id">
{{ field.name | translate }}<span class="adf-asterisk" [style.visibility]="isRequired() ? 'visible' : 'hidden'">*</span>
</label>
</mat-label>
<input matInput
class="adf-input"
type="text"

View File

@ -23,6 +23,7 @@ export * from './components/form-renderer.component';
export * from './components/widgets';
export * from './components/middlewares/middleware';
export * from './components/middlewares/decimal-middleware.service';
export * from './components/helpers/buttons-visibility';
export * from './services/form-rendering.service';
export * from './services/form.service';

View File

@ -18,7 +18,7 @@
"COMPLETE": "Abschließen",
"CANCEL": "Abbrechen",
"CLAIM": "Beanspruchen",
"UNCLAIM": "Anspruch aufheben",
"UNCLAIM": "FREIGEBEN",
"START PROCESS": "Prozess starten",
"DATA_LOADING": "Daten werden geladen",
"CLOSE": "Schließen",

View File

@ -51,7 +51,7 @@
"REST_API_FAILED": "No se puede acceder al servidor '{{ hostname }}'",
"VARIABLE_DROPDOWN_OPTIONS_FAILED": "Ha habido un problema al cargar elementos desplegables. Contacte con el administrador.",
"DATA_TABLE_LOAD_FAILED": "Ha habido un problema al cargar elementos de tabla. Contacte con el administrador.",
"DATA_TABLE_EMPTY_CONTENT": "No data found",
"DATA_TABLE_EMPTY_CONTENT": "No se encontraron datos",
"EXTERNAL_PROPERTY_LOAD_FAILED": "Ha habido un problema al cargar una propiedad externa. Contacte con el administrador.",
"FILE_NAME": "Nombre del fichero",
"TITLE": "Título",

View File

@ -51,7 +51,7 @@
"REST_API_FAILED": "Le serveur '{{ hostname }}' n'est pas accessible",
"VARIABLE_DROPDOWN_OPTIONS_FAILED": "Un problème est survenu lors du chargement des éléments de la liste déroulante. Veuillez contacter un administrateur.",
"DATA_TABLE_LOAD_FAILED": "Un problème est survenu lors du chargement des éléments du tableau. Veuillez contacter un administrateur.",
"DATA_TABLE_EMPTY_CONTENT": "No data found",
"DATA_TABLE_EMPTY_CONTENT": "Données introuvables",
"EXTERNAL_PROPERTY_LOAD_FAILED": "Un problème est survenu lors du chargement de la propriété externe. Veuillez contacter un administrateur.",
"FILE_NAME": "Nom de fichier",
"TITLE": "Titre",

View File

@ -51,7 +51,7 @@
"REST_API_FAILED": "Il server '{{ hostname }}' non è raggiungibile",
"VARIABLE_DROPDOWN_OPTIONS_FAILED": "Si è verificato un problema durante il caricamento degli elementi a discesa. Si prega di contattare lamministratore.",
"DATA_TABLE_LOAD_FAILED": "Si è verificato un problema durante il caricamento degli elementi della tabella. Si prega di contattare lamministratore.",
"DATA_TABLE_EMPTY_CONTENT": "No data found",
"DATA_TABLE_EMPTY_CONTENT": "Nessun dato trovato",
"EXTERNAL_PROPERTY_LOAD_FAILED": "Si è verificato un problema durante il caricamento delle proprietà esterne. Si prega di contattare lamministratore.",
"FILE_NAME": "Nome file",
"TITLE": "Titolo",

View File

@ -51,7 +51,7 @@
"REST_API_FAILED": "Serwer '{{ hostname }}' jest nieosiągalny",
"VARIABLE_DROPDOWN_OPTIONS_FAILED": "Wystąpił problem podczas ładowania elementów rozwijania. Skontaktuj się z administratorem.",
"DATA_TABLE_LOAD_FAILED": "Wystąpił problem podczas ładowania elementów tabeli. Skontaktuj się z administratorem.",
"DATA_TABLE_EMPTY_CONTENT": "No data found",
"DATA_TABLE_EMPTY_CONTENT": "Brak danych",
"EXTERNAL_PROPERTY_LOAD_FAILED": "Wystąpił problem podczas ładowania właściwości zewnętrznej. Skontaktuj się z administratorem.",
"FILE_NAME": "Nazwa pliku",
"TITLE": "Tytuł",

View File

@ -51,7 +51,7 @@
"REST_API_FAILED": "O servidor `{{ hostname }}` não pode ser acedido",
"VARIABLE_DROPDOWN_OPTIONS_FAILED": "Problema ao carregar elementos suspensos. Entre em contacto com o administrador.",
"DATA_TABLE_LOAD_FAILED": "Problema ao carregar os elementos da tabela. Entre em contacto com o administrador.",
"DATA_TABLE_EMPTY_CONTENT": "No data found",
"DATA_TABLE_EMPTY_CONTENT": "Não foram encontrados dados",
"EXTERNAL_PROPERTY_LOAD_FAILED": "Houve um problema ao carregar a propriedade externa. Entre em contacto com o administrador.",
"FILE_NAME": "Nome do ficheiro",
"TITLE": "Título",

View File

@ -26,70 +26,70 @@
// map SCSS variables to expose as CSS variables
$defaults: (
// theme colors
--theme-primary-color: mat.get-color-from-palette($primary),
--theme-primary-color-default-contrast: mat.get-color-from-palette($primary, default-contrast),
--theme-header-text-color: mat.get-color-from-palette($primary, default-contrast),
--adf-theme-primary-50: mat.get-color-from-palette($primary, 50),
--adf-theme-primary-100: mat.get-color-from-palette($primary, 100),
--adf-theme-primary-300: mat.get-color-from-palette($primary, 300),
--adf-theme-primary-900: mat.get-color-from-palette($primary, 900),
--theme-warn-color: mat.get-color-from-palette($warn),
--theme-warn-color-a700: mat.get-color-from-palette($warn, A700),
--theme-warn-color-default-contrast: mat.get-color-from-palette($warn, default-contrast),
--theme-accent-color: mat.get-color-from-palette($accent),
--theme-accent-color-a200: mat.get-color-from-palette($accent, A200),
--theme-accent-color-default-contrast: mat.get-color-from-palette($accent, default-contrast),
--theme-accent-500: mat.get-color-from-palette($accent, 500),
--adf-theme-foreground-base-color: mat.get-color-from-palette($foreground, base),
--adf-theme-foreground-base-color-065: mat.get-color-from-palette($foreground, base, 0.65),
--adf-theme-foreground-base-color-045: mat.get-color-from-palette($foreground, base, 0.45),
--adf-theme-foreground-disabled-text-color: mat.get-color-from-palette($foreground, disabled-text),
--adf-theme-foreground-divider-color: mat.get-color-from-palette($foreground, divider),
--adf-theme-foreground-icon-color: mat.get-color-from-palette($foreground, icon),
--adf-theme-foreground-icon-color-054: mat.get-color-from-palette($foreground, icon, 0.54),
--adf-theme-foreground-secondary-text-color: mat.get-color-from-palette($foreground, secondary-text),
--adf-theme-foreground-text-color: mat.get-color-from-palette($foreground, text),
--adf-theme-foreground-text-color-087: mat.get-color-from-palette($foreground, text, 0.87),
--adf-theme-foreground-text-color-075: mat.get-color-from-palette($foreground, text, 0.75),
--adf-theme-foreground-text-color-064: mat.get-color-from-palette($foreground, text, 0.64),
--adf-theme-foreground-text-color-054: mat.get-color-from-palette($foreground, text, 0.54),
--adf-theme-foreground-text-color-040: mat.get-color-from-palette($foreground, text, 0.4),
--adf-theme-foreground-text-color-027: mat.get-color-from-palette($foreground, text, 0.27),
--adf-theme-foreground-text-color-025: mat.get-color-from-palette($foreground, text, 0.25),
--adf-theme-foreground-text-color-014: mat.get-color-from-palette($foreground, text, 0.14),
--adf-theme-foreground-text-color-007: mat.get-color-from-palette($foreground, text, 0.07),
--adf-theme-background-card-color: mat.get-color-from-palette($background, card),
--adf-theme-background-card-color-087: mat.get-color-from-palette($background, card, 0.87),
--theme-background-color: mat.get-color-from-palette($background, background),
--adf-theme-background-dialog-color: mat.get-color-from-palette($background, dialog),
--adf-theme-background-hover-color: mat.get-color-from-palette($background, hover),
--adf-theme-background-selected-button-color: mat.get-color-from-palette($background, selected-button),
--adf-theme-background-status-bar-color: mat.get-color-from-palette($background, status-bar),
--adf-theme-background-unselected-chip-color: mat.get-color-from-palette($background, unselected-chip),
--theme-primary-color: mat.m2-get-color-from-palette($primary),
--theme-primary-color-default-contrast: mat.m2-get-color-from-palette($primary, default-contrast),
--theme-header-text-color: mat.m2-get-color-from-palette($primary, default-contrast),
--adf-theme-primary-50: mat.m2-get-color-from-palette($primary, 50),
--adf-theme-primary-100: mat.m2-get-color-from-palette($primary, 100),
--adf-theme-primary-300: mat.m2-get-color-from-palette($primary, 300),
--adf-theme-primary-900: mat.m2-get-color-from-palette($primary, 900),
--theme-warn-color: mat.m2-get-color-from-palette($warn),
--theme-warn-color-a700: mat.m2-get-color-from-palette($warn, A700),
--theme-warn-color-default-contrast: mat.m2-get-color-from-palette($warn, default-contrast),
--theme-accent-color: mat.m2-get-color-from-palette($accent),
--theme-accent-color-a200: mat.m2-get-color-from-palette($accent, A200),
--theme-accent-color-default-contrast: mat.m2-get-color-from-palette($accent, default-contrast),
--theme-accent-500: mat.m2-get-color-from-palette($accent, 500),
--adf-theme-foreground-base-color: mat.m2-get-color-from-palette($foreground, base),
--adf-theme-foreground-base-color-065: mat.m2-get-color-from-palette($foreground, base, 0.65),
--adf-theme-foreground-base-color-045: mat.m2-get-color-from-palette($foreground, base, 0.45),
--adf-theme-foreground-disabled-text-color: mat.m2-get-color-from-palette($foreground, disabled-text),
--adf-theme-foreground-divider-color: mat.m2-get-color-from-palette($foreground, divider),
--adf-theme-foreground-icon-color: mat.m2-get-color-from-palette($foreground, icon),
--adf-theme-foreground-icon-color-054: mat.m2-get-color-from-palette($foreground, icon, 0.54),
--adf-theme-foreground-secondary-text-color: mat.m2-get-color-from-palette($foreground, secondary-text),
--adf-theme-foreground-text-color: mat.m2-get-color-from-palette($foreground, text),
--adf-theme-foreground-text-color-087: mat.m2-get-color-from-palette($foreground, text, 0.87),
--adf-theme-foreground-text-color-075: mat.m2-get-color-from-palette($foreground, text, 0.75),
--adf-theme-foreground-text-color-064: mat.m2-get-color-from-palette($foreground, text, 0.64),
--adf-theme-foreground-text-color-054: mat.m2-get-color-from-palette($foreground, text, 0.54),
--adf-theme-foreground-text-color-040: mat.m2-get-color-from-palette($foreground, text, 0.4),
--adf-theme-foreground-text-color-027: mat.m2-get-color-from-palette($foreground, text, 0.27),
--adf-theme-foreground-text-color-025: mat.m2-get-color-from-palette($foreground, text, 0.25),
--adf-theme-foreground-text-color-014: mat.m2-get-color-from-palette($foreground, text, 0.14),
--adf-theme-foreground-text-color-007: mat.m2-get-color-from-palette($foreground, text, 0.07),
--adf-theme-background-card-color: mat.m2-get-color-from-palette($background, card),
--adf-theme-background-card-color-087: mat.m2-get-color-from-palette($background, card, 0.87),
--theme-background-color: mat.m2-get-color-from-palette($background, background),
--adf-theme-background-dialog-color: mat.m2-get-color-from-palette($background, dialog),
--adf-theme-background-hover-color: mat.m2-get-color-from-palette($background, hover),
--adf-theme-background-selected-button-color: mat.m2-get-color-from-palette($background, selected-button),
--adf-theme-background-status-bar-color: mat.m2-get-color-from-palette($background, status-bar),
--adf-theme-background-unselected-chip-color: mat.m2-get-color-from-palette($background, unselected-chip),
// typography
--theme-font-family: mat.font-family($typography),
--theme-font-family: mat.m2-font-family($typography),
--theme-font-weight: normal,
--theme-body-1-font-size: mat.font-size($typography, body-2),
--theme-body-2-font-size: mat.font-size($typography, subtitle-2),
--theme-body-1-line-height: mat.line-height($typography, body-2),
--theme-display-1-font-size: mat.font-size($typography, headline-4),
--theme-display-3-font-size: mat.font-size($typography, headline-2),
--theme-display-4-font-size: mat.font-size($typography, headline-1),
--theme-caption-font-size: mat.font-size($typography, caption),
--theme-title-font-size: mat.font-size($typography, headline-6),
--theme-subheading-1-font-size: mat.font-size($typography, body-1),
--theme-subheading-2-font-size: mat.font-size($typography, subtitle-1),
--theme-button-font-size: mat.font-size($typography, button),
--theme-headline-font-size: mat.font-size($typography, headline-5),
--theme-headline-line-height: mat.line-height($typography, headline-5),
--theme-body-1-font-size: mat.m2-font-size($typography, body-2),
--theme-body-2-font-size: mat.m2-font-size($typography, subtitle-2),
--theme-body-1-line-height: mat.m2-line-height($typography, body-2),
--theme-display-1-font-size: mat.m2-font-size($typography, headline-4),
--theme-display-3-font-size: mat.m2-font-size($typography, headline-2),
--theme-display-4-font-size: mat.m2-font-size($typography, headline-1),
--theme-caption-font-size: mat.m2-font-size($typography, caption),
--theme-title-font-size: mat.m2-font-size($typography, headline-6),
--theme-subheading-1-font-size: mat.m2-font-size($typography, body-1),
--theme-subheading-2-font-size: mat.m2-font-size($typography, subtitle-1),
--theme-button-font-size: mat.m2-font-size($typography, button),
--theme-headline-font-size: mat.m2-font-size($typography, headline-5),
--theme-headline-line-height: mat.m2-line-height($typography, headline-5),
--theme-adf-icon-1-font-size: map-get($custom-css-variables, 'theme-adf-icon-1-font-size'),
--theme-adf-picture-1-font-size: map-get($custom-css-variables, 'theme-adf-picture-1-font-size'),
--theme-adf-task-footer-font-size: map-get($custom-css-variables, 'theme-adf-task-footer-font-size'),
--theme-adf-task-title-font-size: map-get($custom-css-variables, 'theme-adf-task-title-font-size'),
// specific colors
--adf-theme-mat-grey-color-a200: mat.get-color-from-palette(mat.$grey-palette, A200),
--adf-theme-mat-grey-color-a400: mat.get-color-from-palette(mat.$grey-palette, A400),
--adf-theme-mat-grey-color-50: mat.get-color-from-palette(mat.$grey-palette, 50),
--adf-theme-mat-grey-color-a200: mat.m2-get-color-from-palette(mat.$m2-grey-palette, 'A200'),
--adf-theme-mat-grey-color-a400: mat.m2-get-color-from-palette(mat.$m2-grey-palette, 'A400'),
--adf-theme-mat-grey-color-50: mat.m2-get-color-from-palette(mat.$m2-grey-palette, 50),
// spacing
--adf-theme-spacing: map-get($custom-css-variables, 'theme-adf-spacing'),
// components

View File

@ -6,8 +6,11 @@ $mat-tab-label-active: '.mdc-tab--active';
$mat-tab-label-container: '.mat-mdc-tab-label-container';
$mat-tab-label-text: '.mdc-tab__text-label';
$mat-tab-body: '.mat-mdc-tab-body';
$mat-tab-header: '.mat-mdc-tab-header';
$mat-tab-body-content: '.mat-mdc-tab-body-content';
$mat-tab-ink-bar: '.mdc-tab-indicator';
$mat-tab-body-wrapper: '.mat-mdc-tab-body-wrapper';
$mat-tab-body-content: '.mat-mdc-card-content';
$mat-chip: '.mat-mdc-chip';
$mat-chip-list: '.mat-mdc-chip-list';
$mat-checkbox: '.mat-mdc-checkbox';

View File

@ -1,18 +1,18 @@
@use '@angular/material' as mat;
$alfresco-typography: mat.define-typography-config(
$alfresco-typography: mat.m2-define-typography-config(
$font-family: 'Roboto, "Helvetica Neue", sans-serif',
$headline-1: mat.define-typography-level(112px, 112px, 300),
$headline-2: mat.define-typography-level(56px, 56px, 400),
$headline-3: mat.define-typography-level(45px, 48px, 400),
$headline-4: mat.define-typography-level(34px, 40px, 400),
$headline-5: mat.define-typography-level(24px, 32px, 400),
$headline-6: mat.define-typography-level(20px, 32px, 500),
$subtitle-1: mat.define-typography-level(16px, 28px, 400),
$body-1: mat.define-typography-level(15px, 24px, 400),
$subtitle-2: mat.define-typography-level(14px, 24px, 500),
$body-2: mat.define-typography-level(14px, 20px, 400),
$caption: mat.define-typography-level(12px, 20px, 400),
$button: mat.define-typography-level(14px, 14px, 500),
$headline-1: mat.m2-define-typography-level(112px, 112px, 300),
$headline-2: mat.m2-define-typography-level(56px, 56px, 400),
$headline-3: mat.m2-define-typography-level(45px, 48px, 400),
$headline-4: mat.m2-define-typography-level(34px, 40px, 400),
$headline-5: mat.m2-define-typography-level(24px, 32px, 400),
$headline-6: mat.m2-define-typography-level(20px, 32px, 500),
$subtitle-1: mat.m2-define-typography-level(16px, 28px, 400),
$body-1: mat.m2-define-typography-level(15px, 24px, 400),
$subtitle-2: mat.m2-define-typography-level(14px, 24px, 500),
$body-2: mat.m2-define-typography-level(14px, 20px, 400),
$caption: mat.m2-define-typography-level(12px, 20px, 400),
$button: mat.m2-define-typography-level(14px, 14px, 500),
// Line-height must be unit-less fraction of the font-size.
);

View File

@ -27,7 +27,7 @@
class="adf-viewer-pdf-viewer"
(window:resize)="onResize()">
<div [id]="randomPdfId + '-viewer-viewerPdf'"
class="adf-pdfViewer"
class="adf-pdfViewer pdfViewer"
role="document"
tabindex="0"
aria-expanded="true">

View File

@ -114,7 +114,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
displayPage: number;
totalPages: number;
loadingPercent: number;
pdfViewer: any;
pdfViewer: PDFViewer;
pdfJsWorkerUrl: string;
pdfJsWorkerInstance: Worker;
currentScaleMode: PdfScaleMode = 'init';
@ -132,7 +132,12 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
documentOverflow = false;
get currentScaleText(): string {
return this.pdfViewer?.currentScaleValue ? Math.round(this.pdfViewer.currentScaleValue * 100) + '%' : '';
const currentScaleValueStr = this.pdfViewer?.currentScaleValue;
const scaleNumber = Number(currentScaleValueStr);
const currentScaleText = scaleNumber ? `${Math.round(scaleNumber * 100)}%` : '';
return currentScaleText;
}
private pdfjsLib = inject(PDFJS_MODULE);
@ -452,10 +457,9 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
*/
setScaleUpdatePages(newScale: number) {
if (this.pdfViewer) {
if (!this.isSameScale(this.pdfViewer.currentScaleValue, newScale)) {
this.pdfViewer.currentScaleValue = newScale;
if (!this.isSameScale(this.pdfViewer.currentScaleValue, newScale.toString())) {
this.pdfViewer.currentScaleValue = newScale.toString();
}
this.pdfViewer.update();
}
this.setDocumentOverflow();
@ -468,7 +472,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
* @param newScale - new scale page
* @returns `true` if the scale is the same, otherwise `false`
*/
isSameScale(oldScale: number, newScale: number): boolean {
isSameScale(oldScale: string, newScale: string): boolean {
return newScale === oldScale;
}

View File

@ -1,95 +1,105 @@
<div *ngIf="(viewerType === 'media' || viewerType === 'pdf' || viewerType === 'image') ? isLoading || !isContentReady : isLoading"
class="adf-viewer-render-main-loader">
<div *ngIf="isLoading$ | async" class="adf-viewer-render-main-loader">
<div class="adf-viewer-render-layout-content adf-viewer__fullscreen-container">
<div class="adf-viewer-render-content-container">
<div class="adf-viewer-render__loading-screen ">
<div class="adf-viewer-render__loading-screen">
<h2>{{ 'ADF_VIEWER.LOADING' | translate }}</h2>
<div>
<mat-spinner class="adf-viewer-render__loading-screen__spinner"/>
<mat-spinner class="adf-viewer-render__loading-screen__spinner" />
</div>
</div>
</div>
</div>
</div>
<div *ngIf="!isLoading"
class="adf-viewer-render-main">
<ng-container *ngIf="urlFile || blobFile">
<div [hidden]="isLoading$ | async" class="adf-viewer-render-main">
<div class="adf-viewer-render-layout-content adf-viewer__fullscreen-container">
<div class="adf-viewer-render-content-container" [ngSwitch]="viewerType">
<ng-container *ngSwitchCase="'external'">
<adf-preview-extension *ngIf="!!externalViewer"
<adf-preview-extension
*ngIf="!!externalViewer"
[id]="externalViewer.component"
[url]="urlFile"
[extension]="externalViewer.fileExtension"
[nodeId]="nodeId"
[attr.data-automation-id]="externalViewer.component" />
[attr.data-automation-id]="externalViewer.component"
/>
</ng-container>
<ng-container *ngSwitchCase="'pdf'">
<adf-pdf-viewer [thumbnailsTemplate]="thumbnailsTemplate"
<adf-pdf-viewer
[thumbnailsTemplate]="thumbnailsTemplate"
[allowThumbnails]="allowThumbnails"
[blobFile]="blobFile"
[urlFile]="urlFile"
[fileName]="internalFileName"
[cacheType]="cacheTypeForContent"
(pagesLoaded)="isContentReady = true"
(pagesLoaded)="markAsLoaded()"
(close)="onClose()"
(error)="onUnsupportedFile()" />
(error)="onUnsupportedFile()"
/>
</ng-container>
<ng-container *ngSwitchCase="'image'">
<adf-img-viewer [urlFile]="urlFile"
<adf-img-viewer
[urlFile]="urlFile"
[readOnly]="readOnly"
[fileName]="internalFileName"
[allowedEditActions]="allowedEditActions"
[blobFile]="blobFile"
(error)="onUnsupportedFile()"
(submit)="onSubmitFile($event)"
(imageLoaded)="isContentReady = true"
(imageLoaded)="markAsLoaded()"
(isSaving)="isSaving.emit($event)"
/>
</ng-container>
<ng-container *ngSwitchCase="'media'">
<adf-media-player id="adf-mdedia-player"
<adf-media-player
id="adf-mdedia-player"
[urlFile]="urlFile"
[tracks]="tracks"
[mimeType]="mimeType"
[blobFile]="blobFile"
[fileName]="internalFileName"
(error)="onUnsupportedFile()"
(canPlay)="isContentReady = true"/>
(canPlay)="markAsLoaded()"
/>
</ng-container>
<ng-container *ngSwitchCase="'text'">
<adf-txt-viewer [urlFile]="urlFile"
[blobFile]="blobFile" />
<adf-txt-viewer [urlFile]="urlFile" [blobFile]="blobFile" />
</ng-container>
<ng-container *ngSwitchCase="'custom'">
<ng-container *ngFor="let ext of viewerExtensions">
<adf-preview-extension *ngIf="checkExtensions(ext.fileExtension)"
<adf-preview-extension
*ngIf="checkExtensions(ext.fileExtension)"
[id]="ext.component"
[url]="urlFile"
[extension]="extension"
[nodeId]="nodeId"
[attr.data-automation-id]="ext.component" />
[attr.data-automation-id]="ext.component"
/>
</ng-container>
<ng-container *ngFor="let extensionTemplate of extensionTemplates">
<span *ngIf="extensionTemplate.isVisible" class="adf-viewer-render-custom-content">
<ng-template [ngTemplateOutlet]="extensionTemplate.template"
[ngTemplateOutletContext]="{ urlFile: urlFile, extension: extension }" />
<ng-template
[ngTemplateOutlet]="extensionTemplate.template"
[ngTemplateOutletContext]="{ urlFile: urlFile, extension: extension }"
/>
</span>
</ng-container>
</ng-container>
<ng-container *ngSwitchDefault>
<adf-viewer-unknown-format [customError]="customError"/>
<adf-viewer-unknown-format [customError]="customError" />
</ng-container>
</div>
</div>
</div>
</div>
</ng-container>
<ng-container *ngIf="viewerTemplateExtensions">
<ng-template [ngTemplateOutlet]="viewerTemplateExtensions" [ngTemplateOutletInjector]="injector" />
</ng-container>

View File

@ -24,7 +24,7 @@ import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { NoopTranslateModule, UnitTestingUtils } from '../../../testing';
import { RenderingQueueServices } from '../../services/rendering-queue.services';
import { ViewerRenderComponent } from './viewer-render.component';
import { ImgViewerComponent, MediaPlayerComponent, ViewerExtensionDirective } from '@alfresco/adf-core';
import { ImgViewerComponent, MediaPlayerComponent, PdfViewerComponent, ViewerExtensionDirective } from '@alfresco/adf-core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@Component({
@ -483,14 +483,16 @@ describe('ViewerComponent', () => {
describe('Spinner', () => {
const getMainLoader = (): DebugElement => testingUtils.getByCSS('.adf-viewer-render-main-loader');
it('should show spinner when isLoading is true', () => {
component.isLoading = true;
it('should not show spinner by default', (done) => {
component.isLoading$.subscribe((isLoading) => {
fixture.detectChanges();
expect(getMainLoader()).not.toBeNull();
expect(isLoading).toBeFalse();
expect(getMainLoader()).toBeNull();
done();
});
});
it('should show spinner until content is ready when viewerType is media', () => {
component.isLoading = false;
it('should display spinner when viewerType is media', () => {
component.urlFile = 'some-file.mp4';
component.ngOnChanges();
@ -506,24 +508,21 @@ describe('ViewerComponent', () => {
expect(component.viewerType).toBe('media');
});
// eslint-disable-next-line ban/ban
xit('should show spinner until content is ready when viewerType is pdf', () => {
component.isLoading = false;
it('should display spinner when viewerType is pdf', () => {
component.urlFile = 'some-url.pdf';
expect(getMainLoader()).toBeNull();
component.ngOnChanges();
fixture.detectChanges();
expect(getMainLoader()).not.toBeNull();
const imgViewer = testingUtils.getByDirective(PdfViewerComponent);
imgViewer.triggerEventHandler('pagesLoaded', null);
fixture.detectChanges();
expect(getMainLoader()).toBeNull();
expect(component.viewerType).toBe('pdf');
});
it('should show spinner until content is ready when viewerType is image', () => {
component.isLoading = false;
it('should display spinner when viewerType is image', () => {
component.urlFile = 'some-url.png';
component.ngOnChanges();
@ -537,16 +536,5 @@ describe('ViewerComponent', () => {
expect(getMainLoader()).toBeNull();
expect(component.viewerType).toBe('image');
});
it('should not show spinner when isLoading = false and isContentReady = false for other viewer types', () => {
component.isLoading = false;
component.urlFile = 'some-url.txt';
component.ngOnChanges();
fixture.detectChanges();
expect(getMainLoader()).toBeNull();
expect(component.isContentReady).toBeFalse();
});
});
});

View File

@ -16,7 +16,7 @@
*/
import { AppExtensionService, ExtensionsModule, ViewerExtensionRef } from '@alfresco/adf-extensions';
import { NgForOf, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet } from '@angular/common';
import { AsyncPipe, NgForOf, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet } from '@angular/common';
import { Component, EventEmitter, Injector, Input, OnChanges, OnInit, Output, TemplateRef, ViewEncapsulation } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@ -28,6 +28,9 @@ import { MediaPlayerComponent } from '../media-player/media-player.component';
import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component';
import { TxtViewerComponent } from '../txt-viewer/txt-viewer.component';
import { UnknownFormatComponent } from '../unknown-format/unknown-format.component';
import { BehaviorSubject } from 'rxjs';
type ViewerType = 'media' | 'image' | 'pdf' | 'external' | 'text' | 'custom' | 'unknown';
@Component({
selector: 'adf-viewer-render',
@ -50,7 +53,8 @@ import { UnknownFormatComponent } from '../unknown-format/unknown-format.compone
UnknownFormatComponent,
ExtensionsModule,
NgForOf,
NgSwitchDefault
NgSwitchDefault,
AsyncPipe
],
providers: [ViewUtilService]
})
@ -86,10 +90,6 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
@Input()
fileName: string;
/** Override loading status */
@Input()
isLoading = false;
/** Enable when where is possible the editing functionalities */
@Input()
readOnly = true;
@ -141,8 +141,8 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
extensionsSupportedByTemplates: string[] = [];
extension: string;
internalFileName: string;
viewerType: string = 'unknown';
isContentReady = false;
viewerType: ViewerType = 'unknown';
readonly isLoading$ = new BehaviorSubject(false);
/**
* Returns a list of the active Viewer content extensions.
@ -182,12 +182,10 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
ngOnInit() {
this.cacheTypeForContent = 'no-cache';
this.setDefaultLoadingState();
}
ngOnChanges() {
this.isContentReady = false;
this.isLoading = !this.blobFile && !this.urlFile;
if (this.blobFile) {
this.setUpBlobData();
} else if (this.urlFile) {
@ -195,9 +193,13 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
}
}
markAsLoaded() {
this.isLoading$.next(false);
}
private setUpBlobData() {
this.internalFileName = this.fileName;
this.viewerType = this.viewUtilService.getViewerTypeByMimeType(this.blobFile.type);
this.viewerType = this.viewUtilService.getViewerTypeByMimeType(this.blobFile.type) as ViewerType;
this.extensionChange.emit(this.blobFile.type);
this.scrollTop();
@ -206,7 +208,7 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
private setUpUrlFile() {
this.internalFileName = this.fileName ? this.fileName : this.viewUtilService.getFilenameFromUrl(this.urlFile);
this.extension = this.viewUtilService.getFileExtension(this.internalFileName);
this.viewerType = this.viewUtilService.getViewerType(this.extension, this.mimeType, this.extensionsSupportedByTemplates);
this.viewerType = this.viewUtilService.getViewerType(this.extension, this.mimeType, this.extensionsSupportedByTemplates) as ViewerType;
this.extensionChange.emit(this.extension);
this.scrollTop();
@ -235,4 +237,14 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
onClose() {
this.close.next(true);
}
private canBePreviewed(): boolean {
return this.viewerType === 'media' || this.viewerType === 'pdf' || this.viewerType === 'image';
}
private setDefaultLoadingState() {
if (this.canBePreviewed()) {
this.isLoading$.next(true);
}
}
}

View File

@ -49,8 +49,7 @@
<div class="adf-viewer__display-name"
id="adf-viewer-display-name"
[title]="fileName">
<span class="adf-viewer__display-name-without-extension">{{ fileNameWithoutExtension }}</span>
<span class="adf-viewer__display-name-extension">{{ fileExtension }}</span>
<span>{{ displayName }}</span>
</div>
<button *ngIf="allowNavigate && canNavigateNext"
data-automation-id="adf-toolbar-next-file"

View File

@ -55,23 +55,10 @@
&__display-name {
font-size: var(--theme-subheading-2-font-size);
opacity: 0.87;
line-height: 1.5;
letter-spacing: -0.4px;
font-weight: normal;
font-style: normal;
font-stretch: normal;
vertical-align: middle;
color: var(--adf-theme-foreground-text-color);
&-without-extension {
display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
max-width: 400px;
white-space: nowrap;
vertical-align: bottom;
}
}
&-container {

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component } from '@angular/core';
import { Component, SimpleChanges } from '@angular/core';
import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
@ -143,36 +143,61 @@ describe('ViewerComponent', () => {
});
});
describe('File Name Test', () => {
const getFileNameWithoutExtension = (): string =>
testingUtils.getByCSS('.adf-viewer__display-name-without-extension').nativeElement.textContent;
const getExtension = (): string => testingUtils.getByCSS('.adf-viewer__display-name-extension').nativeElement.textContent;
describe('File Name Display Tests', () => {
describe('displayFileName method', () => {
it('should return full filename when total length is 80 characters or less', () => {
const fileShortName = 'shortname.txt';
component.fileName = fileShortName;
fixture.detectChanges();
expect(component.getDisplayFileName()).toBe(fileShortName);
expect(getFileName()).toBe(fileShortName);
});
it('should truncate filename when total length exceeds 80 characters', () => {
const longName =
'verylongfilenamethatexceedsmaximumlengthallowedverylongfilenamethatexceedsmaximumlengthallowed.verylongextensionnamethatistoolongverylongextensionnamethatistoolong';
component.fileName = longName;
fixture.detectChanges();
const result = component.getDisplayFileName();
expect(result).toContain('.....');
expect(result.length).toBe(50);
});
it('should handle empty filename', () => {
component.fileName = '';
fixture.detectChanges();
expect(component.getDisplayFileName()).toBe('');
expect(getFileName()).toBe('');
});
});
describe('fileName setter integration', () => {
it('should fileName be set by urlFile input if the fileName is not provided as Input', () => {
component.fileName = '';
spyOn(viewUtilService, 'getFilenameFromUrl').and.returnValue('fakeFileName.jpeg');
const mockSimpleChanges: any = { urlFile: { currentValue: 'https://fakefile.url/fakeFileName.jpeg' } };
const mockSimpleChanges = { urlFile: { currentValue: 'https://fakefile.url/fakeFileName.jpeg' } } as unknown as SimpleChanges;
component.ngOnChanges(mockSimpleChanges);
fixture.detectChanges();
expect(getFileName()).toEqual('fakeFileName.jpeg');
expect(getFileNameWithoutExtension()).toBe('fakeFileName.');
expect(getExtension()).toBe('jpeg');
});
it('should set fileName providing fileName input', () => {
component.fileName = 'testFileName.jpg';
spyOn(viewUtilService, 'getFilenameFromUrl').and.returnValue('fakeFileName.jpeg');
const mockSimpleChanges: any = { urlFile: { currentValue: 'https://fakefile.url/fakeFileName.jpeg' } };
const mockSimpleChanges = { urlFile: { currentValue: 'https://fakefile.url/fakeFileName.jpeg' } } as unknown as SimpleChanges;
component.ngOnChanges(mockSimpleChanges);
fixture.detectChanges();
fixture.detectChanges();
expect(getFileName()).toEqual('testFileName.jpg');
expect(getFileNameWithoutExtension()).toBe('testFileName.');
expect(getExtension()).toBe('jpg');
});
});
});

View File

@ -297,6 +297,7 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
private _fileNameWithoutExtension: string;
private _fileExtension: string;
public displayName: string;
public downloadPromptTimer: number;
public downloadPromptReminderTimer: number;
public mimeTypeIconUrl: string;
@ -309,6 +310,7 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
this._fileName = fileName;
this._fileExtension = this.viewUtilsService.getFileExtension(this.fileName);
this._fileNameWithoutExtension = this.fileName?.replace(new RegExp(`${this.fileExtension}$`), '') || '';
this.displayName = this.getDisplayFileName();
}
get fileName(): string {
@ -462,6 +464,24 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
this.clearDownloadPromptTimeouts();
}
getDisplayFileName(): string {
const fullName = (this.fileNameWithoutExtension || '') + (this.fileExtension || '');
const maxLength = 50;
if (fullName.length <= maxLength) {
return fullName;
}
const amountOfTruncateDots = 5;
const availableSpace = maxLength - amountOfTruncateDots;
const endLength = 8;
const startLength = availableSpace - endLength;
const start = fullName.substring(0, startLength);
const end = fullName.substring(fullName.length - endLength);
return start + '.....' + end;
}
private configureAndInitDownloadPrompt() {
this.configureDownloadPromptProperties();
if (this.enableDownloadPrompt) {

View File

@ -5,6 +5,9 @@ export default {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../coverage/libs/js-api',
moduleMNameMapper: {
'^pdfjs-dist$': 'pdfjs-dist/legacy/build/pdf'
},
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',

View File

@ -209,9 +209,9 @@ export class TagsApi extends BaseApi {
/**
* Create specified by **tags** list of tags.
* @param tags List of tags to create.
* @returns Promise<TagEntry[]>
* @returns Promise<TagEntry | TagPaging>
*/
createTags(tags: TagBody[]): Promise<TagEntry[]> {
createTags(tags: TagBody[]): Promise<TagEntry | TagPaging> {
throwIfNotDefined(tags, 'tags');
return this.post({

View File

@ -240,7 +240,7 @@ Create specified by **tags** list of tags.
|----------|-----------------------|-------------------------|
| **tags** | [TagBody[]](#TagBody) | List of tags to create. |
**Return type**: [TagEntry[]](#TagEntry)
**Return type**: [TagEntry](#TagEntry) | [TagPaging](#TagPaging)
**Example**

View File

@ -24,6 +24,7 @@ import { BaseApi } from './base.api';
import { buildCollectionParam } from '../../../alfrescoApiClient';
import { throwIfNotDefined } from '../../../assert';
import { RecordsIncludeQuery, RecordsPagingQuery, RecordsSourceQuery } from './types';
import { FilePlanRolePaging, FilePlanRoleParameters } from '../model';
/**
* FilePlansApi service.
@ -158,4 +159,26 @@ export class FilePlansApi extends BaseApi {
returnType: FilePlanEntry
});
}
/**
* Gets a list of roles for the specified file plan.
* @param filePlanId The identifier of a file plan. You can also use the -filePlan- alias.
* @param parameters Optional parameters
* @returns Promise<FilePlanEntry>
*/
getFilePlanRoles(filePlanId: string, parameters?: FilePlanRoleParameters): Promise<FilePlanRolePaging> {
throwIfNotDefined(filePlanId, 'filePlanId');
return this.get({
path: '/file-plans/{filePlanId}/roles',
pathParams: {
filePlanId
},
queryParams: {
where: parameters?.where?.capabilityNames
? `(capabilityName in (${parameters.where.capabilityNames.map((value) => "'" + value + "'").join(', ')}))`
: undefined
}
});
}
}

View File

@ -0,0 +1,12 @@
# FilePlanRole
## Properties
| Name | Type |
|--------------------|-----------------------------------------------------------|
| **displayLabel** | string |
| **groupShortName** | string |
| **name** | string |
| **roleGroupName** | string |
| **capabilities** | [**FilePlanRoleCapability[]**](FilePlanRoleCapability.md) |

View File

@ -0,0 +1,11 @@
# FilePLanRoleCapability
## Properties
| Name | Type |
|-----------|-------------------------------------------------------------------|
| **group** | [**FilePlanRoleCapabilityGroup**](FilePlanRoleCapabilityGroup.md) |
| **index** | number |
| **name** | string |
| **title** | string |

View File

@ -0,0 +1,9 @@
# FilePlanRoleCapabilityGroup
## Properties
| Name | Type |
|-----------|--------|
| **id** | string |
| **title** | string |

View File

@ -0,0 +1,8 @@
# FilePlanRoleEntry
## Properties
| Name | Type |
|-----------|-------------------------------------|
| **entry** | [**FilePlanRole**](FilePlanRole.md) |

View File

@ -0,0 +1,8 @@
# FilePLanRolePaging
## Properties
| Name | Type |
|----------|---------------------------------------------------------|
| **list** | [**FilePlanRolePagingList**](FilePlanRolePagingList.md) |

View File

@ -0,0 +1,9 @@
# FilePLanRolePagingList
## Properties
| Name | Type |
|----------------|-------------------------------------------------------------|
| **entries** | [**FilePlanRoleEntry[]**](FilePlanRoleEntry.md) |
| **pagination** | [**Pagination**](../../content-rest-api/docs/Pagination.md) |

View File

@ -0,0 +1,8 @@
# FilePlanRoleParameters
## Properties
| Name | Type |
|-----------|-------------------------------------------------------------------|
| **where** | [**FilePlanRoleParametersWhere**](FilePlanRoleParametersWhere.md) |

View File

@ -0,0 +1,8 @@
# FilePlanRoleParametersWhere
## Properties
| Name | Type |
|---------------------|----------|
| **capabilityNames** | string[] |

View File

@ -2,13 +2,13 @@
All URIs are relative to *https://localhost/alfresco/api/-default-/public/gs/versions/1*
Method | HTTP request | Description
------------- | ------------- | -------------
[**createFilePlanCategories**](FilePlansApi.md#createFilePlanCategories) | **POST** /file-plans/{filePlanId}/categories | Create record categories for a file plan
[**getFilePlan**](FilePlansApi.md#getFilePlan) | **GET** /file-plans/{filePlanId} | Get a file plan
[**getFilePlanCategories**](FilePlansApi.md#getFilePlanCategories) | **GET** /file-plans/{filePlanId}/categories | List file plans's children
[**updateFilePlan**](FilePlansApi.md#updateFilePlan) | **PUT** /file-plans/{filePlanId} | Update a file plan
| Method | HTTP request | Description |
|--------------------------------------------------------------------------|----------------------------------------------|---------------------------------------------------|
| [**createFilePlanCategories**](FilePlansApi.md#createFilePlanCategories) | **POST** /file-plans/{filePlanId}/categories | Create record categories for a file plan |
| [**getFilePlan**](FilePlansApi.md#getFilePlan) | **GET** /file-plans/{filePlanId} | Get a file plan |
| [**getFilePlanCategories**](FilePlansApi.md#getFilePlanCategories) | **GET** /file-plans/{filePlanId}/categories | List file plans's children |
| [**updateFilePlan**](FilePlansApi.md#updateFilePlan) | **PUT** /file-plans/{filePlanId} | Update a file plan |
| [**getFilePlanRoles**](FilePlansApi.md#getFilePlanRoles) | **GET** /file-plans/{filePlanId}/roles | Gets a list of roles for the specified file plan. |
<a name="createFilePlanCategories"></a>
# **createFilePlanCategories**
@ -402,3 +402,39 @@ parameter are returned in addition to those specified in the **fields** paramete
[**FilePlanEntry**](FilePlanEntry.md)
<a name="getFilePlanRoles"></a>
# **getFilePlanRoles**
> FilePlanEntry getFilePlanRoles(filePlanId, parameters)
Gets a list of roles for the specified file plan.
### Example
```javascript
import FilePlansApi from 'FilePlansApi';
import { AlfrescoApi } from '@alfresco/js-api';
this.alfrescoApi = new AlfrescoApi();
this.alfrescoApi.setConfig({
hostEcm: 'http://127.0.0.1:8080'
});
let fileplansApi = new FilePlansApi(this.alfrescoApi);
const filePlanId = 'some id';
fileplansApi.updateFilePlan(filePlanId, {
where: {
capabilityNames: ['ViewRecords']
}
}).then((data) => console.log('API called successfully. Returned data: ' + data))
.catch((error) => console.log(error));
```
### Parameters
| Name | Type | Description |
|----------------|---------------------------------------------------------|-----------------------------------------------------------------------|
| **filePlanId** | **string** | The identifier of a file plan. You can also use the -filePlan- alias. |
| **parameters** | [**FilePlanRoleParameters**](FilePlanRoleParameters.md) | Optional parameters. |
### Return type
[**FilePlanRolePaging**](FilePlanRolePaging.md)

View File

@ -0,0 +1,33 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { FilePlanRoleCapability } from './filePlanRoleCapability';
export class FilePlanRole {
displayLabel: string;
groupShortName: string;
name: string;
roleGroupName: string;
capabilities: FilePlanRoleCapability[];
constructor(input?: Partial<FilePlanRole>) {
if (input) {
Object.assign(this, input);
this.capabilities = input.capabilities?.map((capability) => new FilePlanRoleCapability(capability));
}
}
}

View File

@ -0,0 +1,32 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { FilePlanRoleCapabilityGroup } from './filePlanRoleCapabilityGroup';
export class FilePlanRoleCapability {
group: FilePlanRoleCapabilityGroup;
index: number;
name: string;
title: string;
constructor(input?: Partial<FilePlanRoleCapability>) {
if (input) {
Object.assign(this, input);
this.group = input.group ? new FilePlanRoleCapabilityGroup(input.group) : undefined;
}
}
}

View File

@ -0,0 +1,27 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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.
*/
export class FilePlanRoleCapabilityGroup {
id: string;
title: string;
constructor(input?: Partial<FilePlanRoleCapabilityGroup>) {
if (input) {
Object.assign(this, input);
}
}
}

View File

@ -0,0 +1,29 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { FilePlanRole } from './filePlanRole';
export class FilePlanRoleEntry {
entry: FilePlanRole;
constructor(input?: Partial<FilePlanRoleEntry>) {
if (input) {
Object.assign(this, input);
this.entry = input.entry ? new FilePlanRole(input.entry) : undefined;
}
}
}

View File

@ -0,0 +1,29 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { FilePlanRolePagingList } from './filePlanRolePagingList';
export class FilePlanRolePaging {
list: FilePlanRolePagingList;
constructor(input: Partial<FilePlanRolePaging>) {
if (input) {
Object.assign(this, input);
this.list = input.list ? new FilePlanRolePagingList(input.list) : undefined;
}
}
}

View File

@ -0,0 +1,32 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { Pagination } from '../../content-rest-api';
import { FilePlanRoleEntry } from './filePlanRoleEntry';
export class FilePlanRolePagingList {
entries: FilePlanRoleEntry[];
pagination: Pagination;
constructor(input: Partial<FilePlanRolePagingList>) {
if (input) {
Object.assign(this, input);
this.pagination = input.pagination ? new Pagination(input.pagination) : undefined;
this.entries = input?.entries.map((entry) => new FilePlanRoleEntry(entry));
}
}
}

View File

@ -0,0 +1,22 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { FilePlanRoleParametersWhere } from './filePlanRoleParametersWhere';
export interface FilePlanRoleParameters {
where?: FilePlanRoleParametersWhere;
}

View File

@ -0,0 +1,20 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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.
*/
export interface FilePlanRoleParametersWhere {
capabilityNames?: string[];
}

View File

@ -19,6 +19,14 @@ export * from './filePlan';
export * from './filePlanBodyUpdate';
export * from './filePlanComponentBodyUpdate';
export * from './filePlanEntry';
export * from './filePlanRole';
export * from './filePlanRoleCapability';
export * from './filePlanRoleCapabilityGroup';
export * from './filePlanRoleEntry';
export * from './filePlanRolePaging';
export * from './filePlanRolePagingList';
export * from './filePlanRoleParameters';
export * from './filePlanRoleParametersWhere';
export * from './rMNodeBodyCreate';
export * from './rMNodeBodyCreateWithRelativePath';
export * from './rMSite';

View File

@ -16,7 +16,7 @@
*/
import assert from 'assert';
import { AlfrescoApi, TagBody, TagEntry, TagsApi } from '../../src';
import { AlfrescoApi, TagBody, TagEntry, TagPaging, TagsApi } from '../../src';
import { EcmAuthMock, TagMock } from '../mockObjects';
describe('Tags', () => {
@ -105,10 +105,10 @@ describe('Tags', () => {
describe('createTags', () => {
it('should return created tags', (done) => {
tagMock.createTags201Response();
tagsApi.createTags([new TagBody(), new TagBody()]).then((tags) => {
assert.equal(tags.length, 2);
assert.equal(tags[0].entry.tag, 'tag-test-1');
assert.equal(tags[1].entry.tag, 'tag-test-2');
tagsApi.createTags([new TagBody(), new TagBody()]).then((tags: TagPaging) => {
assert.equal(tags.list.entries.length, 2);
assert.equal(tags.list.entries[0].entry.tag, 'tag-test-1');
assert.equal(tags.list.entries[1].entry.tag, 'tag-test-2');
done();
});
});

View File

@ -0,0 +1,129 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { EcmAuthMock, FilePlansMock } from '../mockObjects';
import { AlfrescoApi, FilePlanRolePaging, FilePlansApi } from '../../src';
describe('FilePlansApi', () => {
let filePlansApiMock: FilePlansMock;
let filePlansApi: FilePlansApi;
beforeEach(async () => {
const hostEcm = 'https://127.0.0.1:8080';
const authResponseMock = new EcmAuthMock(hostEcm);
authResponseMock.get201Response();
filePlansApiMock = new FilePlansMock(hostEcm);
const alfrescoApi = new AlfrescoApi({
hostEcm
});
filePlansApi = new FilePlansApi(alfrescoApi);
await alfrescoApi.login('admin', 'admin');
});
describe('getFilePlanRoles', () => {
let expectedRolePaging: FilePlanRolePaging;
beforeEach(() => {
expectedRolePaging = {
list: {
pagination: {
count: 2,
hasMoreItems: false,
totalItems: 2,
skipCount: 0,
maxItems: 100
},
entries: [
{
entry: {
displayLabel: 'Role One',
groupShortName: 'group short name 1',
name: 'role1',
roleGroupName: 'role group name 1',
capabilities: [
{
index: 0,
name: 'capability1',
title: 'Capability One',
group: {
id: 'group1',
title: 'Group One'
}
},
{
index: 1,
name: 'capability2',
title: 'Capability Two',
group: {
id: 'group2',
title: 'Group Two'
}
}
]
}
},
{
entry: {
displayLabel: 'Role Two',
groupShortName: 'group short name 2',
name: 'role2',
roleGroupName: 'role group name 2',
capabilities: [
{
index: 0,
name: 'capability3',
title: 'Capability Three',
group: {
id: 'group3',
title: 'Group Three'
}
}
]
}
}
]
}
};
});
it('should get file plan roles', (done) => {
const filePlanId = 'filePlanId123';
filePlansApiMock.get200FilePlanRoles(filePlanId);
filePlansApi.getFilePlanRoles(filePlanId).then((rolePaging) => {
expect(rolePaging).toEqual(expectedRolePaging);
done();
});
});
it('should get file plan roles with filtering by capability names', (done) => {
const filePlanId = 'filePlanId123';
filePlansApiMock.get200FilePlanRolesWithFilteringByCapabilityNames(filePlanId);
filePlansApi
.getFilePlanRoles(filePlanId, {
where: {
capabilityNames: ['capability1', 'capability2']
}
})
.then((rolePaging) => {
expect(rolePaging).toEqual(expectedRolePaging);
done();
});
});
});
});

View File

@ -65,7 +65,7 @@ export class TagMock extends BaseMock {
createTags201Response(): void {
nock(this.host, { encodedQueryParams: true })
.post('/alfresco/api/-default-/public/alfresco/versions/1/tags')
.reply(201, [this.mockTagEntry(), this.mockTagEntry('tag-test-2', 'd79bdbd0-9f55-45bb-9521-811e15bf48f6')]);
.reply(201, this.getPaginatedListOfTags());
}
get201ResponseForAssigningTagsToNode(body: TagBody[]): void {

View File

@ -0,0 +1,101 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache 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
*
* http://www.apache.org/licenses/LICENSE-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 { BaseMock } from '../base.mock';
import nock from 'nock';
import { FilePlanRolePaging } from '@alfresco/js-api';
export class FilePlansMock extends BaseMock {
get200FilePlanRoles(filePlanId: string): void {
nock(this.host, { encodedQueryParams: true })
.get(`/alfresco/api/-default-/public/gs/versions/1/file-plans/${filePlanId}/roles`)
.query({})
.reply(200, this.mockFilePlanRolePaging());
}
get200FilePlanRolesWithFilteringByCapabilityNames(filePlanId: string): void {
nock(this.host, { encodedQueryParams: true })
.get(`/alfresco/api/-default-/public/gs/versions/1/file-plans/${filePlanId}/roles`)
.query({
where: "(capabilityName in ('capability1', 'capability2'))"
})
.reply(200, this.mockFilePlanRolePaging());
}
private mockFilePlanRolePaging(): FilePlanRolePaging {
return {
list: {
pagination: {
count: 2,
hasMoreItems: false,
totalItems: 2,
skipCount: 0,
maxItems: 100
},
entries: [
{
entry: {
displayLabel: 'Role One',
groupShortName: 'group short name 1',
name: 'role1',
roleGroupName: 'role group name 1',
capabilities: [
{
index: 0,
name: 'capability1',
title: 'Capability One',
group: {
id: 'group1',
title: 'Group One'
}
},
{
index: 1,
name: 'capability2',
title: 'Capability Two',
group: {
id: 'group2',
title: 'Group Two'
}
}
]
}
},
{
entry: {
displayLabel: 'Role Two',
groupShortName: 'group short name 2',
name: 'role2',
roleGroupName: 'role group name 2',
capabilities: [
{
index: 0,
name: 'capability3',
title: 'Capability Three',
group: {
id: 'group3',
title: 'Group Three'
}
}
]
}
}
]
}
};
}
}

View File

@ -34,6 +34,7 @@ export * from './content-services/version.mock';
export * from './content-services/webscript.mock';
export * from './goverance-services/authority-clearance.mock';
export * from './goverance-services/file-plans.mock';
export * from './goverance-services/gs-sites.mock';
export * from './goverance-services/node-security-marks.mock';
export * from './goverance-services/security-groups.mock';

Some files were not shown because too many files have changed in this diff Show More