Compare commits

...

1786 Commits

Author SHA1 Message Date
Tim Donohue
dde0e40805 Update version tag for 8.0-rc1 release 2024-04-05 17:06:50 -05:00
Tim Donohue
d0a8042a89 Merge pull request #2067 from atmire/disable-inline-css
Add configuration option to disable inlined CSS in SSR HTML
2024-04-05 13:14:22 -05:00
Yury Bondarenko
6203055c7a Merge remote-tracking branch 'origin/main' into disable-inline-css 2024-04-05 09:28:45 +02:00
Tim Donohue
1e339608dc Merge pull request #2894 from DSpace/dependabot/npm_and_yarn/axios-1.6.8
Bump axios from 0.21.4 to 1.6.8
2024-04-04 14:06:32 -05:00
Tim Donohue
cd79416776 Merge pull request #2872 from DSpace/dependabot/npm_and_yarn/webpack-dev-middleware-5.3.4
Bump webpack-dev-middleware from 5.3.3 to 5.3.4
2024-04-04 11:57:49 -05:00
dependabot[bot]
cf845af54e Bump axios from 0.21.4 to 1.6.8
Bumps [axios](https://github.com/axios/axios) from 0.21.4 to 1.6.8.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.21.4...v1.6.8)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-04 16:42:37 +00:00
Tim Donohue
863a04ed4d Merge pull request #2881 from DSpace/dependabot/npm_and_yarn/express-4.19.2
Bump express from 4.18.2 to 4.19.2
2024-04-04 11:31:08 -05:00
Tim Donohue
7ebda4a7ed Merge pull request #2887 from DSpace/rename-ds7-2-ds8
Renaming DSpace to DSpace 8
2024-04-04 10:45:51 -05:00
dependabot[bot]
41d4ed7603 Bump webpack-dev-middleware from 5.3.3 to 5.3.4
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-04 15:12:12 +00:00
dependabot[bot]
ae93c4fe11 Bump express from 4.18.2 to 4.19.2
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-04 15:12:03 +00:00
Tim Donohue
7ae6c7e67e Merge pull request #2871 from 4Science/DURACOM-234
Upgrade to angular 16
2024-04-04 10:11:08 -05:00
Paulo Graça
00798934d6 Renaming to DS8 2024-04-04 15:47:38 +01:00
Paulo Graça
dbf222248a Renaming DSpace to DSpace 8 2024-04-04 15:37:04 +01:00
Giuseppe Digilio
c327bf744b [DURACOM-234] Fix merge with main 2024-04-04 09:58:10 +02:00
Giuseppe Digilio
32f1d1096a Merge branch 'main' into DURACOM-234
# Conflicts:
#	src/app/core/data/request.service.ts
#	src/modules/app/browser-app.config.ts
#	src/modules/app/server-app.config.ts
2024-04-04 09:32:38 +02:00
Tim Donohue
86ddd45dc1 Merge pull request #2886 from tdonohue/csrf_fixes
Ensures CSRF token is initialized prior to first modifying (non-`GET`) request
2024-04-03 14:43:40 -05:00
Tim Donohue
bbab56a7da Update build script to use "docker compose" instead of "docker-compose". Fixes random failures starting e2e test backend via Docker 2024-04-03 14:00:42 -05:00
Giuseppe Digilio
35714b7a45 [DURACOM-234] rename guards in order to be complaint to function name convention 2024-04-03 18:57:11 +02:00
Giuseppe Digilio
f1686d5499 [DURACOM-234] rename resolvers in order to be complaint to function name convention 2024-04-03 18:48:14 +02:00
Tim Donohue
f7d31fd341 Fix tests. Add XSRFService to all specs which need it to be initialized 2024-04-03 11:48:13 -05:00
Tim Donohue
cc6b159a8a Update code to use GET request. Cleanup lint errors & add in basic TypeDocs 2024-04-03 10:25:43 -05:00
Art Lowel
617c7d8756 make a call to ensure a correct XSRF token before performing any non-GET requests 2024-04-03 09:46:45 -05:00
Giuseppe Digilio
aba04913ce [DURACOM-234] Replace Observable with BehaviorSubject 2024-04-02 18:54:39 +02:00
Giuseppe Digilio
660cf7e4dd [DURACOM-234] Remove assertions on isResponsePending in favour of remote data operators 2024-04-02 18:54:00 +02:00
Giuseppe Digilio
b49d883e0b [DURACOM-234] fix my-dspace.guard 2024-04-02 18:51:00 +02:00
Giuseppe Digilio
b8c26a71c3 [DURACOM-234] Fixes after migrating to functional guards 2024-04-02 18:45:57 +02:00
Giuseppe Digilio
026e16ffd9 [DURACOM-234] Fix tests 2024-04-02 15:28:19 +02:00
Giuseppe Digilio
32b816c1aa [DURACOM-234] Made use of mapToCanActivate for all the guards which are still Class 2024-04-02 13:13:25 +02:00
Giuseppe Digilio
19548fc6d4 [DURACOM-234] Move resolver dependency to relate functional one 2024-04-02 12:31:32 +02:00
Giuseppe Digilio
6a4ad7bdb4 [DURACOM-234] Fix resolver name 2024-04-02 12:28:49 +02:00
Giuseppe Digilio
29f5a17528 [DURACOM-234] Migrate to functional guards WIP 2024-03-29 18:28:32 +01:00
Giuseppe Digilio
5dbc6ce7a8 [DURACOM-237] Fix issue on drag end 2024-03-29 12:47:03 +01:00
Giuseppe Digilio
1f4aca800b [DURACOM-234] Migrate fo functional resolver 2024-03-28 23:34:37 +01:00
Giuseppe Digilio
549609e2ea [DURACOM-234] Fix Markdown Config 2024-03-28 16:49:14 +01:00
Giuseppe Digilio
a8c3af097d [DURACOM-234] Switch to standalone bootstrapping API 2024-03-26 20:52:26 +01:00
Giuseppe Digilio
0c3e9b1535 [DURACOM-234] Fix NG0600 error 2024-03-26 18:42:29 +01:00
Alan Orth
b3b3ef8203 Merge pull request #2877 from alexandrevryghem/fix-systemwidealert-form-validation_contribute-main
System-wide alert shouldn't be created when the message is empty
2024-03-26 10:11:45 +03:00
Alexandre Vryghem
03e49907c1 Merge branch 'fix-systemwidealert-form-validation_contribute-7.6' into fix-systemwidealert-form-validation_contribute-main 2024-03-23 16:04:25 +01:00
Alexandre Vryghem
767de4b5c8 Prevent system-wide alert to be activated with empty message 2024-03-23 15:12:48 +01:00
Yury Bondarenko
4a39f33006 Add to config.example.yml 2024-03-22 16:23:39 +01:00
Giuseppe Digilio
e0855a46cc [DURACOM-234] Remove unsupported node 16 2024-03-21 17:29:52 +01:00
Giuseppe Digilio
969b42fee1 [DURACOM-234] Fix tests 2024-03-21 16:34:42 +01:00
Giuseppe Digilio
abb284a2d6 [DURACOM-237] Fix tests 2024-03-21 16:33:32 +01:00
Giuseppe Digilio
5d24691bc2 Merged in DURACOM-240-mathjax (pull request #1488)
Replace mathjax pipe with directive
2024-03-21 12:17:44 +00:00
Giuseppe Digilio
b325701f9f [DURACOM-240] Fix test 2024-03-21 13:11:08 +01:00
Giuseppe Digilio
a652600c7f [DURACOM-234] Remove fdescribe 2024-03-21 13:10:33 +01:00
Giuseppe Digilio
e2bbb43395 Merge branch 'DURACOM-234' into DURACOM-240-mathjax
# Conflicts:
#	src/app/shared/utils/markdown.pipe.spec.ts
2024-03-21 12:57:31 +01:00
Giuseppe Digilio
152e724c75 [DURACOM-234] Fix tests 2024-03-21 12:56:45 +01:00
Giuseppe Digilio
c072d5ddfc Merge branch 'main' into DURACOM-234 2024-03-21 09:52:13 +01:00
Giuseppe Digilio
418f07bbf2 [DURACOM-240] Assure the MathJax script has been registered before rendering 2024-03-21 09:46:14 +01:00
Tim Donohue
41eccbbfe1 Merge pull request #2750 from 4Science/DURACOM-191
Migration to standalone components
2024-03-20 09:36:00 -05:00
Giuseppe Digilio
cfa2882a28 Merge branch 'DURACOM-234' into DURACOM-240-mathjax 2024-03-20 12:15:52 +01:00
Giuseppe Digilio
bef71a99b4 [DURACOM-234] Fix merge 2024-03-20 12:15:22 +01:00
Giuseppe Digilio
9b6b79c755 Merge branch 'DURACOM-234' into DURACOM-240-mathjax 2024-03-20 12:02:40 +01:00
Giuseppe Digilio
b214bb386a Merge branch 'DURACOM-191' into DURACOM-234
# Conflicts:
#	src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts
2024-03-20 12:01:42 +01:00
Giuseppe Digilio
6704a397bd [DURACOM-191] Provide routing option properly 2024-03-20 11:56:53 +01:00
Giuseppe Digilio
85292579af [DURACOM-191] Fix selected tab for the comcol-page-browse-by.component 2024-03-20 11:55:34 +01:00
Giuseppe Digilio
bcf8383daf [DURACOM-191] Fix issue with edit relationship page 2024-03-20 11:52:45 +01:00
Giuseppe Digilio
2826ff5536 [DURACOM-191] Fix missing import for ngIf and ngFor directive 2024-03-19 18:04:09 +01:00
Giuseppe Digilio
dfad3f3fad [DURACOM-191] Fix issue with admin workflow page 2024-03-18 21:03:37 +01:00
Giuseppe Digilio
ddd0131c8b [DURACOM-191] Fix submission-import-external-preview.component 2024-03-18 21:00:37 +01:00
Giuseppe Digilio
41c62e2478 [DURACOM-191] Fix batch import page 2024-03-18 17:57:01 +01:00
Giuseppe Digilio
5b6c174767 [DURACOM-191] Fix withdrawn-reinstate-modal 2024-03-18 17:48:08 +01:00
Giuseppe Digilio
4fdbf15ae0 Merge branch 'DURACOM-234' into DURACOM-240-mathjax 2024-03-18 16:11:12 +01:00
Giuseppe Digilio
66a99090a8 Merge branch 'DURACOM-191' into DURACOM-234
# Conflicts:
#	src/app/shared/resource-policies/resolvers/resource-policy-target.resolver.ts
2024-03-18 16:10:26 +01:00
Giuseppe Digilio
acfcf51e34 [DURACOM-191] Remove DataService decorator 2024-03-18 16:08:35 +01:00
Giuseppe Digilio
b28f99f6bf [DURACOM-191] Fix resource-policy-target.resolver which still used getDataServiceFor decorator 2024-03-18 16:03:43 +01:00
Giuseppe Digilio
8c55e89b3d [DURACOM-191] Fix eperson-group-list.component which still used getDataServiceFor decorator 2024-03-18 15:35:34 +01:00
Giuseppe Digilio
7472e6939b [DURACOM-191] Fix dso-edit-metadata.component which still used getDataServiceFor decorator 2024-03-18 13:45:42 +01:00
Giuseppe Digilio
58512b6eed [DURACOM-191] Fix test 2024-03-18 11:26:26 +01:00
Giuseppe Digilio
8409853be6 [DURACOM-191] Clean up suggestions-data.service implementation 2024-03-18 09:18:12 +01:00
Giuseppe Digilio
00479132ab [DURACOM-191] Add missing data service 2024-03-17 19:21:56 +01:00
Giuseppe Digilio
9e6fe526e1 [DURACOM-240] use BehaviorSubject 2024-03-17 19:07:09 +01:00
Andrea Barbasso
3e30f64e9c [DURACOM-240] add and fix tests 2024-03-17 18:32:38 +01:00
Andrea Barbasso
e1ba78c443 [DURACOM-240] refactor variable 2024-03-17 18:30:34 +01:00
Andrea Barbasso
e402ac482d [DURACOM-240] split service into SSR and CSR 2024-03-17 18:29:11 +01:00
Andrea Barbasso
68c9ef1051 [DURACOM-240] refactor markdownPipe to markdownDirective
missing tests and splitting the service into a browser one and a server one
2024-03-17 18:25:27 +01:00
Andrea Barbasso
825308e223 [DURACOM-240] working directive
need to change markdown pipe to markdown directive and put everything in there
2024-03-17 18:20:03 +01:00
Andrea Barbasso
c6b72f6c64 [DURACOM-240] pre-tests changes 2024-03-17 18:14:53 +01:00
Giuseppe Digilio
81b0d76f1b [DURACOM-240] Improve markdown pipe in order to check for empty values 2024-03-17 18:12:20 +01:00
Simone Ramundi
083a904043 Merged in DURACOM-237-align-to-angular-16 (pull request #1431)
DURACOM-237 align to angular 16

Approved-by: Giuseppe Digilio
2024-03-17 17:06:53 +00:00
Giuseppe Digilio
3e03b71479 [DURACOM-237] Add space before text 2024-03-17 18:06:06 +01:00
Giuseppe Digilio
1e95fdb300 [DURACOM-237] Fix lint 2024-03-17 17:50:14 +01:00
Giuseppe Digilio
61d5f49c13 Merge branch 'DURACOM-234' into DURACOM-237-align-to-angular-16
# Conflicts:
#	src/app/shared/form/chips/chips.component.ts
#	src/app/shared/form/form.module.ts
2024-03-17 17:44:35 +01:00
Giuseppe Digilio
0db7fffec6 Merge branch 'DURACOM-191' into DURACOM-234 2024-03-17 17:35:40 +01:00
Giuseppe Digilio
2e8817712a [DURACOM-191] Fix tests 2024-03-17 17:34:35 +01:00
Giuseppe Digilio
788e44a770 [DURACOM-191] Fix circular dependency 2024-03-17 15:48:05 +01:00
Giuseppe Digilio
f527810545 [DURACOM-191] Fix tests 2024-03-15 19:46:14 +01:00
Andrea Barbasso
aa4e4e6fce [DURACOM-234] fix build errors 2024-03-15 15:36:42 +01:00
Andrea Barbasso
4bc7df86b3 [DURACOM-234] remove useless import 2024-03-15 14:51:45 +01:00
Andrea Barbasso
c75c5f1199 [DURACOM-234] lint fix 2024-03-15 13:19:00 +01:00
Andrea Barbasso
0c3dd8d946 Merge branch 'DURACOM-191' into DURACOM-191-align-to-angular-16
# Conflicts:
#	package.json
#	src/app/access-control/epeople-registry/eperson-resolver.service.ts
#	src/app/admin/admin-registries/bitstream-formats/bitstream-formats.resolver.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts
#	src/app/app.module.ts
#	src/app/bitstream-page/bitstream-page.resolver.ts
#	src/app/bitstream-page/legacy-bitstream-url.resolver.ts
#	src/app/browse-by/browse-by-guard.ts
#	src/app/collection-page/collection-page.resolver.ts
#	src/app/collection-page/create-collection-page/create-collection-page.guard.ts
#	src/app/collection-page/edit-item-template-page/item-template-page.resolver.ts
#	src/app/community-page/community-page.resolver.ts
#	src/app/community-page/create-community-page/create-community-page.guard.ts
#	src/app/core/auth/auth-blocking.guard.ts
#	src/app/core/auth/authenticated.guard.ts
#	src/app/core/breadcrumbs/dso-breadcrumb.resolver.ts
#	src/app/core/breadcrumbs/i18n-breadcrumb.resolver.ts
#	src/app/core/breadcrumbs/quality-assurance-breadcrumb.resolver.ts
#	src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard.spec.ts
#	src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.spec.ts
#	src/app/core/data/feature-authorization/feature-authorization-guard/dso-page-some-feature.guard.ts
#	src/app/core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard.ts
#	src/app/core/end-user-agreement/abstract-end-user-agreement.guard.ts
#	src/app/core/feedback/feedback.guard.ts
#	src/app/core/reload/reload.guard.ts
#	src/app/core/server-check/server-check.guard.ts
#	src/app/core/submission/resolver/submission-object.resolver.ts
#	src/app/home-page/home-page.resolver.ts
#	src/app/init.service.ts
#	src/app/item-page/edit-item-page/edit-item-page.component.spec.ts
#	src/app/item-page/edit-item-page/edit-item-page.component.ts
#	src/app/item-page/item.resolver.ts
#	src/app/item-page/version-page/version.resolver.ts
#	src/app/lookup-by-id/lookup-guard.ts
#	src/app/menu.resolver.ts
#	src/app/my-dspace-page/my-dspace.guard.ts
#	src/app/process-page/process-breadcrumb.resolver.ts
#	src/app/process-page/process-page.resolver.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.resolver.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-source-page-component/quality-assurance-source-data.resolver.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-source-page-component/quality-assurance-source-page-resolver.service.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-topics-page/quality-assurance-topics-page-resolver.service.ts
#	src/app/register-email-form/registration.resolver.ts
#	src/app/register-page/registration.guard.ts
#	src/app/request-copy/request-copy.resolver.ts
#	src/app/search-page/configuration-search-page.guard.ts
#	src/app/shared/context-help.directive.ts
#	src/app/shared/dso-page/dso-edit-menu.resolver.ts
#	src/app/shared/form/chips/chips.component.spec.ts
#	src/app/shared/form/chips/chips.component.ts
#	src/app/shared/form/form.module.ts
#	src/app/shared/metadata-representation/metadata-representation-loader.component.ts
#	src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts
#	src/app/shared/resource-policies/resolvers/resource-policy-target.resolver.ts
#	src/app/shared/resource-policies/resolvers/resource-policy.resolver.ts
#	src/app/shared/theme-support/themed.component.ts
#	src/app/shared/utils/markdown.pipe.ts
#	src/app/submission/sections/upload/accessConditions/submission-section-upload-access-conditions.component.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts
#	src/app/workflowitems-edit-page/item-from-workflow.resolver.ts
#	src/app/workflowitems-edit-page/workflow-item-page.resolver.ts
#	src/app/workspaceitems-edit-page/item-from-workspace.resolver.ts
#	src/app/workspaceitems-edit-page/workspace-item-page.resolver.ts
#	src/config/app-config.interface.ts
#	src/main.server.ts
#	src/modules/app/browser-app.module.ts
#	src/modules/app/browser-init.service.ts
#	src/modules/app/server-app.module.ts
#	src/modules/app/server-init.service.ts
#	src/ngx-translate-loaders/translate-browser.loader.ts
#	src/ngx-translate-loaders/translate-server.loader.ts
#	src/themes/custom/lazy-theme.module.ts
#	src/themes/dspace/lazy-theme.module.ts
#	yarn.lock
2024-03-15 13:07:20 +01:00
Giuseppe Digilio
fb47c9698d Merge remote-tracking branch '4scienceGithub/DURACOM-191' into DURACOM-191 2024-03-15 11:31:20 +01:00
Andrea Barbasso
5fc2ed9213 [DURACOM-191] fix some tests 2024-03-15 10:32:39 +01:00
Giuseppe Digilio
f29e70d8a4 [DURACOM-191] Remove duplicated route path definition 2024-03-15 09:00:09 +01:00
Giuseppe Digilio
762fceecc2 [DURACOM-191] optimize application states providers 2024-03-14 19:54:03 +01:00
Giuseppe Digilio
524f20232d Merge remote-tracking branch '4scienceGithub/DURACOM-191' into DURACOM-191 2024-03-14 18:44:54 +01:00
Giuseppe Digilio
f53d829337 [DURACOM-191] remove providers already provided in root 2024-03-14 18:44:41 +01:00
Giuseppe Digilio
748febdb88 [DURACOM-191] Provide resolver in root 2024-03-14 18:06:35 +01:00
Andrea Barbasso
4faa4ab77d [DURACOM-191] fix metadata-import-page.component tests 2024-03-14 17:57:30 +01:00
Andrea Barbasso
1a14f3fb75 [DURACOM-191] fix some tests 2024-03-14 17:24:03 +01:00
Andrea Barbasso
87552ce5d6 Merge remote-tracking branch 'github/DURACOM-191' into DURACOM-191 2024-03-14 16:53:13 +01:00
Giuseppe Digilio
56fc0a7cfb Merge remote-tracking branch '4scienceGithub/DURACOM-191' into DURACOM-191 2024-03-14 16:52:14 +01:00
Giuseppe Digilio
d72c2848f2 [DURACOM-191] Remove app-routing.module 2024-03-14 16:52:09 +01:00
Giuseppe Digilio
f244ea7160 [DURACOM-191] Fix missing import 2024-03-14 16:51:38 +01:00
Andrea Barbasso
e73e95ebaf [DURACOM-191] fix some tests and remaining decorator to be removed 2024-03-14 16:51:05 +01:00
Giuseppe Digilio
653de28586 [DURACOM-191] Remove system-wide-alert.module 2024-03-14 16:30:56 +01:00
Andrea Barbasso
f0a0e38302 [DURACOM-191] fix some tests 2024-03-14 16:01:09 +01:00
Giuseppe Digilio
c6cdb99213 [DURACOM-191] Remove submission.module 2024-03-14 15:54:05 +01:00
Giuseppe Digilio
88bcbfc27d [DURACOM-191] Remove statistics-page.module and statistics.module 2024-03-14 15:48:37 +01:00
Giuseppe Digilio
e630b6cfed [DURACOM-191] Remove subscriptions-page.module, subscriptions.module.ts and upload.module.ts 2024-03-14 15:32:42 +01:00
Andrea Barbasso
c8ae352740 [DURACOM-191] fix some tests 2024-03-14 15:22:25 +01:00
Giuseppe Digilio
a364cd8d27 [DURACOM-191] Remove menu.module, resource-policies.module and search.module 2024-03-14 15:11:07 +01:00
Giuseppe Digilio
5d21510144 Merge remote-tracking branch '4scienceGithub/DURACOM-191' into DURACOM-191 2024-03-14 14:12:01 +01:00
Giuseppe Digilio
cb44f0a098 [DURACOM-191] Remove register-email-form.module, form.module, shared-browse-by.module and process-page-shared.module 2024-03-14 14:11:55 +01:00
Andrea Barbasso
981597cb19 [DURACOM-191] fix some tests 2024-03-14 12:55:09 +01:00
Andrea Barbasso
0b78b042c9 [DURACOM-191] move data services to map 2024-03-14 12:15:37 +01:00
Andrea Barbasso
268bdb4bd4 [DURACOM-191] fix some tests 2024-03-14 11:12:38 +01:00
Andrea Barbasso
6ef5d7ef46 [DURACOM-191] fix some tests 2024-03-14 11:07:03 +01:00
Andrea Barbasso
2473a27681 [DURACOM-191] fix qa-event-notification.component test 2024-03-14 10:57:42 +01:00
Andrea Barbasso
4e69eedaf8 [DURACOM-191] fix workspace-item-search-result-detail-element.component test 2024-03-14 10:48:24 +01:00
Andrea Barbasso
0d1bd063e6 [DURACOM-191] fix notify-info.service test 2024-03-14 10:39:45 +01:00
Andrea Barbasso
e4ea7d3ae2 [DURACOM-191] fix footer.component test 2024-03-14 10:29:25 +01:00
Andrea Barbasso
d2f64d968b [DURACOM-191] fix untyped-item.component test 2024-03-14 10:18:56 +01:00
Andrea Barbasso
0a0b9ea7eb [DURACOM-191] fix test 2024-03-14 10:05:55 +01:00
Andrea Barbasso
3d871b2745 [DURACOM-191] fix test 2024-03-14 09:45:44 +01:00
Andrea Barbasso
9d0db66b4f [DURACOM-191] fix test 2024-03-14 08:32:58 +01:00
Giuseppe Digilio
e153ca3e89 [DURACOM-191] Remove navbar.module 2024-03-13 19:55:03 +01:00
Giuseppe Digilio
054a3d56c5 [DURACOM-191] Remove edit-item-page.module and item-versions.module.ts 2024-03-13 19:27:08 +01:00
Giuseppe Digilio
a29c60376a [DURACOM-191] Remove item-page.module 2024-03-13 18:39:14 +01:00
Giuseppe Digilio
2840a4207d [DURACOM-191] Remove browse-by-page.module 2024-03-13 18:19:45 +01:00
Giuseppe Digilio
eb230333dd [DURACOM-191] add provided in root 2024-03-13 18:18:15 +01:00
Giuseppe Digilio
5ff7b8c58b [DURACOM-191] Remove my-dspace-page.module and fix missing imports 2024-03-13 18:17:55 +01:00
Andrea Barbasso
464d793a6d [DURACOM-191] fix tests 2024-03-13 17:07:24 +01:00
Giuseppe Digilio
11a2170898 [DURACOM-191] Remove google-recaptcha.module 2024-03-13 16:07:33 +01:00
Giuseppe Digilio
2cabfc7752 [DURACOM-191] Remove lookup-by-id.module 2024-03-13 16:06:23 +01:00
Giuseppe Digilio
5edef2d782 [DURACOM-191] Fix issue with migration to standalone components 2024-03-13 16:00:52 +01:00
Giuseppe Digilio
1e2fabbf31 [DURACOM-191] Fix lint 2024-03-13 14:57:54 +01:00
Giuseppe Digilio
08aef91a0a Merge remote-tracking branch '4scienceGithub/DURACOM-191' into DURACOM-191 2024-03-13 14:52:11 +01:00
Giuseppe Digilio
8c435be8c0 [DURACOM-191] Remove admin-reports.module 2024-03-13 14:51:51 +01:00
Giuseppe Digilio
e5ed336692 [DURACOM-191] Remove admin-ldn-services.module 2024-03-13 14:51:32 +01:00
Giuseppe Digilio
8b4eedef32 [DURACOM-191] Remove admin-notify-dashboard.module 2024-03-13 14:51:02 +01:00
Giuseppe Digilio
44c608c445 [DURACOM-191] Remove access-control.module 2024-03-13 12:28:27 +01:00
Giuseppe Digilio
e03f68d239 [DURACOM-191] Refactoring in order to remove nested subscriptions 2024-03-13 12:28:15 +01:00
Andrea Barbasso
98b15658b0 [DURACOM-191] fix some test errors 2024-03-13 12:02:01 +01:00
Andrea Barbasso
0ff19db071 [DURACOM-191] convert components to standalone 2024-03-13 10:59:26 +01:00
Andrea Barbasso
06f8aa028b [DURACOM-191] fix build errors 2024-03-13 09:19:38 +01:00
Andrea Barbasso
4fde89e10c [DURACOM-191] fix wrong imports 2024-03-13 07:48:13 +01:00
Andrea Barbasso
3029802ad4 [DURACOM-191] fix lint errors manually and automatically 2024-03-12 21:06:12 +01:00
Andrea Barbasso
ee265f855b [DURACOM-191] run optimize imports and lint fix 2024-03-12 20:10:23 +01:00
Andrea Barbasso
fe23ac6a82 Merge remote-tracking branch 'github/main' into DURACOM-191-20240311-main-merge
# Conflicts:
#	src/app/access-control/access-control-routing.module.ts
#	src/app/access-control/access-control.module.ts
#	src/app/access-control/bulk-access/browse/bulk-access-browse.component.spec.ts
#	src/app/access-control/bulk-access/browse/bulk-access-browse.component.ts
#	src/app/access-control/bulk-access/bulk-access.component.spec.ts
#	src/app/access-control/bulk-access/bulk-access.component.ts
#	src/app/access-control/bulk-access/settings/bulk-access-settings.component.spec.ts
#	src/app/access-control/bulk-access/settings/bulk-access-settings.component.ts
#	src/app/access-control/epeople-registry/epeople-registry.component.spec.ts
#	src/app/access-control/epeople-registry/epeople-registry.component.ts
#	src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts
#	src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts
#	src/app/access-control/group-registry/group-form/group-form.component.spec.ts
#	src/app/access-control/group-registry/group-form/group-form.component.ts
#	src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts
#	src/app/access-control/group-registry/group-form/members-list/members-list.component.ts
#	src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts
#	src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts
#	src/app/access-control/group-registry/groups-registry.component.spec.ts
#	src/app/access-control/group-registry/groups-registry.component.ts
#	src/app/admin/admin-curation-tasks/admin-curation-tasks.component.spec.ts
#	src/app/admin/admin-import-batch-page/batch-import-page.component.spec.ts
#	src/app/admin/admin-import-batch-page/batch-import-page.component.ts
#	src/app/admin/admin-import-metadata-page/metadata-import-page.component.spec.ts
#	src/app/admin/admin-import-metadata-page/metadata-import-page.component.ts
#	src/app/admin/admin-ldn-services/admin-ldn-services-routes.ts
#	src/app/admin/admin-ldn-services/admin-ldn-services.module.ts
#	src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.spec.ts
#	src/app/admin/admin-ldn-services/ldn-service-form/ldn-service-form.component.ts
#	src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.spec.ts
#	src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.ts
#	src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.spec.ts
#	src/app/admin/admin-notifications/admin-notifications-publication-claim-page/admin-notifications-publication-claim-page.component.ts
#	src/app/admin/admin-notifications/admin-notifications-routing.module.ts
#	src/app/admin/admin-notifications/admin-notifications.module.ts
#	src/app/admin/admin-registries/admin-registries-routing.module.ts
#	src/app/admin/admin-registries/admin-registries.module.ts
#	src/app/admin/admin-registries/bitstream-formats/add-bitstream-format/add-bitstream-format.component.spec.ts
#	src/app/admin/admin-registries/bitstream-formats/add-bitstream-format/add-bitstream-format.component.ts
#	src/app/admin/admin-registries/bitstream-formats/bitstream-formats-routing.module.ts
#	src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts
#	src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts
#	src/app/admin/admin-registries/bitstream-formats/bitstream-formats.module.ts
#	src/app/admin/admin-registries/bitstream-formats/edit-bitstream-format/edit-bitstream-format.component.spec.ts
#	src/app/admin/admin-registries/bitstream-formats/edit-bitstream-format/edit-bitstream-format.component.ts
#	src/app/admin/admin-registries/bitstream-formats/format-form/format-form.component.spec.ts
#	src/app/admin/admin-registries/bitstream-formats/format-form/format-form.component.ts
#	src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts
#	src/app/admin/admin-registries/metadata-registry/metadata-registry.component.ts
#	src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.spec.ts
#	src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts
#	src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts
#	src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts
#	src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts
#	src/app/admin/admin-registries/metadata-schema/metadata-schema.component.ts
#	src/app/admin/admin-reports/admin-reports-routing.module.ts
#	src/app/admin/admin-reports/admin-reports.module.ts
#	src/app/admin/admin-reports/filtered-collections/filtered-collections.component.spec.ts
#	src/app/admin/admin-reports/filtered-collections/filtered-collections.component.ts
#	src/app/admin/admin-reports/filtered-items/filtered-items.component.ts
#	src/app/admin/admin-reports/filters-section/filters-section.component.spec.ts
#	src/app/admin/admin-reports/filters-section/filters-section.component.ts
#	src/app/admin/admin-routing.module.ts
#	src/app/admin/admin-search-page/admin-search-page.component.spec.ts
#	src/app/admin/admin-search-page/admin-search-page.component.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/collection-search-result/collection-admin-search-result-grid-element.component.spec.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/collection-search-result/collection-admin-search-result-grid-element.component.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/community-search-result/community-admin-search-result-grid-element.component.spec.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/community-search-result/community-admin-search-result-grid-element.component.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/collection-search-result/collection-admin-search-result-list-element.component.spec.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/collection-search-result/collection-admin-search-result-list-element.component.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component.spec.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/community-search-result/community-admin-search-result-list-element.component.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.spec.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.ts
#	src/app/admin/admin-search-page/admin-search-results/item-admin-search-result-actions.component.spec.ts
#	src/app/admin/admin-search-page/admin-search-results/item-admin-search-result-actions.component.ts
#	src/app/admin/admin-search-page/admin-search.module.ts
#	src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.spec.ts
#	src/app/admin/admin-sidebar/admin-sidebar-section/admin-sidebar-section.component.ts
#	src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts
#	src/app/admin/admin-sidebar/admin-sidebar.component.ts
#	src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.spec.ts
#	src/app/admin/admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-page.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-page.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workflow-item/workflow-item-admin-workflow-actions.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workflow-item/workflow-item-admin-workflow-actions.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workspace-item/workspace-item-search-result-admin-workflow-list-element.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workspace-item/workspace-item-search-result-admin-workflow-list-element.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow.module.ts
#	src/app/admin/admin.module.ts
#	src/app/app-routing.module.ts
#	src/app/app.component.ts
#	src/app/app.module.ts
#	src/app/bitstream-page/bitstream-authorizations/bitstream-authorizations.component.spec.ts
#	src/app/bitstream-page/bitstream-authorizations/bitstream-authorizations.component.ts
#	src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.spec.ts
#	src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.ts
#	src/app/bitstream-page/bitstream-page-routing.module.ts
#	src/app/bitstream-page/bitstream-page.module.ts
#	src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.spec.ts
#	src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts
#	src/app/breadcrumbs/breadcrumbs.component.spec.ts
#	src/app/breadcrumbs/breadcrumbs.component.ts
#	src/app/browse-by/browse-by-date/browse-by-date.component.spec.ts
#	src/app/browse-by/browse-by-date/browse-by-date.component.ts
#	src/app/browse-by/browse-by-guard.spec.ts
#	src/app/browse-by/browse-by-metadata/browse-by-metadata.component.spec.ts
#	src/app/browse-by/browse-by-metadata/browse-by-metadata.component.ts
#	src/app/browse-by/browse-by-page.module.ts
#	src/app/browse-by/browse-by-routing.module.ts
#	src/app/browse-by/browse-by-switcher/browse-by-decorator.ts
#	src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts
#	src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts
#	src/app/browse-by/browse-by-switcher/dynamic-component-loader.directive.ts
#	src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.spec.ts
#	src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.ts
#	src/app/browse-by/browse-by-title/browse-by-title.component.spec.ts
#	src/app/browse-by/browse-by-title/browse-by-title.component.ts
#	src/app/browse-by/browse-by.module.ts
#	src/app/collection-page/collection-form/collection-form.component.ts
#	src/app/collection-page/collection-form/collection-form.module.ts
#	src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts
#	src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts
#	src/app/collection-page/collection-page-routing.module.ts
#	src/app/collection-page/collection-page.component.ts
#	src/app/collection-page/collection-page.module.ts
#	src/app/collection-page/create-collection-page/create-collection-page.component.spec.ts
#	src/app/collection-page/create-collection-page/create-collection-page.component.ts
#	src/app/collection-page/delete-collection-page/delete-collection-page.component.spec.ts
#	src/app/collection-page/delete-collection-page/delete-collection-page.component.ts
#	src/app/collection-page/edit-collection-page/collection-access-control/collection-access-control.component.spec.ts
#	src/app/collection-page/edit-collection-page/collection-authorizations/collection-authorizations.component.spec.ts
#	src/app/collection-page/edit-collection-page/collection-curate/collection-curate.component.spec.ts
#	src/app/collection-page/edit-collection-page/collection-metadata/collection-metadata.component.spec.ts
#	src/app/collection-page/edit-collection-page/collection-metadata/collection-metadata.component.ts
#	src/app/collection-page/edit-collection-page/collection-roles/collection-roles.component.spec.ts
#	src/app/collection-page/edit-collection-page/collection-source/collection-source-controls/collection-source-controls.component.spec.ts
#	src/app/collection-page/edit-collection-page/collection-source/collection-source-controls/collection-source-controls.component.ts
#	src/app/collection-page/edit-collection-page/collection-source/collection-source.component.spec.ts
#	src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts
#	src/app/collection-page/edit-collection-page/edit-collection-page.component.spec.ts
#	src/app/collection-page/edit-collection-page/edit-collection-page.component.ts
#	src/app/collection-page/edit-collection-page/edit-collection-page.module.ts
#	src/app/collection-page/edit-collection-page/edit-collection-page.routing.module.ts
#	src/app/collection-page/edit-item-template-page/edit-item-template-page.component.spec.ts
#	src/app/collection-page/edit-item-template-page/edit-item-template-page.component.ts
#	src/app/community-list-page/community-list-page.component.spec.ts
#	src/app/community-list-page/community-list-page.module.ts
#	src/app/community-list-page/community-list-page.routing.module.ts
#	src/app/community-list-page/community-list/community-list.component.spec.ts
#	src/app/community-list-page/community-list/community-list.component.ts
#	src/app/community-page/community-form/community-form.component.ts
#	src/app/community-page/community-form/community-form.module.ts
#	src/app/community-page/community-page-routing.module.ts
#	src/app/community-page/community-page.component.ts
#	src/app/community-page/community-page.module.ts
#	src/app/community-page/create-community-page/create-community-page.component.spec.ts
#	src/app/community-page/create-community-page/create-community-page.component.ts
#	src/app/community-page/delete-community-page/delete-community-page.component.spec.ts
#	src/app/community-page/delete-community-page/delete-community-page.component.ts
#	src/app/community-page/edit-community-page/community-access-control/community-access-control.component.spec.ts
#	src/app/community-page/edit-community-page/community-access-control/community-access-control.component.ts
#	src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.spec.ts
#	src/app/community-page/edit-community-page/community-curate/community-curate.component.spec.ts
#	src/app/community-page/edit-community-page/community-metadata/community-metadata.component.spec.ts
#	src/app/community-page/edit-community-page/community-roles/community-roles.component.spec.ts
#	src/app/community-page/edit-community-page/community-roles/community-roles.component.ts
#	src/app/community-page/edit-community-page/edit-community-page.component.spec.ts
#	src/app/community-page/edit-community-page/edit-community-page.component.ts
#	src/app/community-page/edit-community-page/edit-community-page.module.ts
#	src/app/community-page/edit-community-page/edit-community-page.routing.module.ts
#	src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.spec.ts
#	src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts
#	src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.ts
#	src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.spec.ts
#	src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts
#	src/app/core/auth/auth.service.spec.ts
#	src/app/core/browse/browse-definition-data.service.ts
#	src/app/core/cache/builders/link.service.spec.ts
#	src/app/core/cache/builders/link.service.ts
#	src/app/core/coar-notify/notify-info/notify-info.component.spec.ts
#	src/app/core/coar-notify/notify-info/notify-info.component.ts
#	src/app/core/coar-notify/notify-info/notify-info.service.ts
#	src/app/core/config/bulk-access-config-data.service.ts
#	src/app/core/config/submission-accesses-config-data.service.ts
#	src/app/core/config/submission-uploads-config-data.service.ts
#	src/app/core/core.module.ts
#	src/app/core/data/access-status-data.service.ts
#	src/app/core/data/bitstream-data.service.ts
#	src/app/core/data/bitstream-format-data.service.ts
#	src/app/core/data/bundle-data.service.ts
#	src/app/core/data/collection-data.service.ts
#	src/app/core/data/community-data.service.ts
#	src/app/core/data/configuration-data.service.ts
#	src/app/core/data/dspace-object-data.service.ts
#	src/app/core/data/feature-authorization/authorization-data.service.ts
#	src/app/core/data/feature-authorization/feature-authorization-guard/site-administrator.guard.ts
#	src/app/core/data/feature-authorization/feature-authorization-guard/site-register.guard.ts
#	src/app/core/data/feature-authorization/feature-data.service.ts
#	src/app/core/data/href-only-data.service.ts
#	src/app/core/data/identifier-data.service.ts
#	src/app/core/data/item-data.service.spec.ts
#	src/app/core/data/item-data.service.ts
#	src/app/core/data/metadata-field-data.service.ts
#	src/app/core/data/metadata-schema-data.service.ts
#	src/app/core/data/processes/process-data.service.ts
#	src/app/core/data/processes/script-data.service.ts
#	src/app/core/data/relationship-data.service.ts
#	src/app/core/data/relationship-type-data.service.ts
#	src/app/core/data/root-data.service.ts
#	src/app/core/data/site-data.service.ts
#	src/app/core/data/system-wide-alert-data.service.ts
#	src/app/core/data/version-data.service.ts
#	src/app/core/data/version-history-data.service.ts
#	src/app/core/data/workflow-action-data.service.ts
#	src/app/core/eperson/eperson-data.service.spec.ts
#	src/app/core/eperson/eperson-data.service.ts
#	src/app/core/eperson/group-data.service.spec.ts
#	src/app/core/eperson/group-data.service.ts
#	src/app/core/feedback/feedback-data.service.ts
#	src/app/core/google-recaptcha/google-recaptcha.module.ts
#	src/app/core/orcid/orcid-history-data.service.ts
#	src/app/core/orcid/orcid-queue-data.service.ts
#	src/app/core/profile/researcher-profile-data.service.ts
#	src/app/core/registry/registry.service.spec.ts
#	src/app/core/resource-policy/resource-policy-data.service.ts
#	src/app/core/shared/non-hierarchical-browse-definition.ts
#	src/app/core/shared/search/search.service.spec.ts
#	src/app/core/statistics/usage-report-data.service.ts
#	src/app/core/submission/submission-cc-license-data.service.ts
#	src/app/core/submission/submission-cc-license-url-data.service.ts
#	src/app/core/submission/vocabularies/vocabulary-entry-details.data.service.ts
#	src/app/core/submission/workflowitem-data.service.ts
#	src/app/core/submission/workspaceitem-data.service.ts
#	src/app/core/supervision-order/supervision-order-data.service.ts
#	src/app/core/tasks/claimed-task-data.service.ts
#	src/app/core/tasks/pool-task-data.service.ts
#	src/app/curation-form/curation-form.component.spec.ts
#	src/app/curation-form/curation-form.component.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-field-values/dso-edit-metadata-field-values.component.spec.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-field-values/dso-edit-metadata-field-values.component.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-headers/dso-edit-metadata-headers.component.spec.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-headers/dso-edit-metadata-headers.component.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value-headers/dso-edit-metadata-value-headers.component.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.spec.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts
#	src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.spec.ts
#	src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts
#	src/app/dso-shared/dso-shared.module.ts
#	src/app/entity-groups/journal-entities/item-grid-elements/journal-issue/journal-issue-grid-element.component.spec.ts
#	src/app/entity-groups/journal-entities/item-grid-elements/journal-issue/journal-issue-grid-element.component.ts
#	src/app/entity-groups/journal-entities/item-grid-elements/journal-volume/journal-volume-grid-element.component.spec.ts
#	src/app/entity-groups/journal-entities/item-grid-elements/journal-volume/journal-volume-grid-element.component.ts
#	src/app/entity-groups/journal-entities/item-grid-elements/journal/journal-grid-element.component.spec.ts
#	src/app/entity-groups/journal-entities/item-grid-elements/journal/journal-grid-element.component.ts
#	src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.ts
#	src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.ts
#	src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.ts
#	src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.spec.ts
#	src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.ts
#	src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.spec.ts
#	src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.ts
#	src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.spec.ts
#	src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.ts
#	src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.spec.ts
#	src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.ts
#	src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.spec.ts
#	src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.ts
#	src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.spec.ts
#	src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component.ts
#	src/app/entity-groups/journal-entities/item-list-elements/sidebar-search-list-elements/journal-issue/journal-issue-sidebar-search-list-element.component.ts
#	src/app/entity-groups/journal-entities/item-list-elements/sidebar-search-list-elements/journal-volume/journal-volume-sidebar-search-list-element.component.ts
#	src/app/entity-groups/journal-entities/item-list-elements/sidebar-search-list-elements/journal/journal-sidebar-search-list-element.component.ts
#	src/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.ts
#	src/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.ts
#	src/app/entity-groups/journal-entities/item-pages/journal/journal.component.spec.ts
#	src/app/entity-groups/journal-entities/item-pages/journal/journal.component.ts
#	src/app/entity-groups/journal-entities/journal-entities.module.ts
#	src/app/entity-groups/research-entities/item-grid-elements/org-unit/org-unit-grid-element.component.spec.ts
#	src/app/entity-groups/research-entities/item-grid-elements/org-unit/org-unit-grid-element.component.ts
#	src/app/entity-groups/research-entities/item-grid-elements/person/person-grid-element.component.spec.ts
#	src/app/entity-groups/research-entities/item-grid-elements/person/person-grid-element.component.ts
#	src/app/entity-groups/research-entities/item-grid-elements/project/project-grid-element.component.spec.ts
#	src/app/entity-groups/research-entities/item-grid-elements/project/project-grid-element.component.ts
#	src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.ts
#	src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.ts
#	src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.ts
#	src/app/entity-groups/research-entities/item-list-elements/org-unit/org-unit-list-element.component.spec.ts
#	src/app/entity-groups/research-entities/item-list-elements/org-unit/org-unit-list-element.component.ts
#	src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.spec.ts
#	src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.ts
#	src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.spec.ts
#	src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.ts
#	src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.spec.ts
#	src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.ts
#	src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.spec.ts
#	src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component.ts
#	src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.spec.ts
#	src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/project/project-search-result-list-element.component.ts
#	src/app/entity-groups/research-entities/item-list-elements/sidebar-search-list-elements/org-unit/org-unit-sidebar-search-list-element.component.ts
#	src/app/entity-groups/research-entities/item-list-elements/sidebar-search-list-elements/person/person-sidebar-search-list-element.component.ts
#	src/app/entity-groups/research-entities/item-list-elements/sidebar-search-list-elements/project/project-sidebar-search-list-element.component.ts
#	src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.ts
#	src/app/entity-groups/research-entities/item-pages/person/person.component.ts
#	src/app/entity-groups/research-entities/item-pages/project/project.component.ts
#	src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts
#	src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.ts
#	src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.spec.ts
#	src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.ts
#	src/app/entity-groups/research-entities/metadata-representations/project/project-item-metadata-list-element.component.spec.ts
#	src/app/entity-groups/research-entities/metadata-representations/project/project-item-metadata-list-element.component.ts
#	src/app/entity-groups/research-entities/research-entities.module.ts
#	src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.spec.ts
#	src/app/entity-groups/research-entities/submission/item-list-elements/external-source-entry/external-source-entry-list-submission-element.component.ts
#	src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.spec.ts
#	src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.ts
#	src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-suggestions/org-unit-input-suggestions.component.spec.ts
#	src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-suggestions/org-unit-input-suggestions.component.ts
#	src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.spec.ts
#	src/app/entity-groups/research-entities/submission/item-list-elements/person/person-search-result-list-submission-element.component.ts
#	src/app/entity-groups/research-entities/submission/item-list-elements/person/person-suggestions/person-input-suggestions.component.ts
#	src/app/entity-groups/research-entities/submission/name-variant-modal/name-variant-modal.component.spec.ts
#	src/app/entity-groups/research-entities/submission/name-variant-modal/name-variant-modal.component.ts
#	src/app/footer/footer.component.spec.ts
#	src/app/footer/footer.component.ts
#	src/app/forbidden/forbidden.component.ts
#	src/app/forgot-password/forgot-password-email/forgot-email.component.spec.ts
#	src/app/forgot-password/forgot-password-email/forgot-email.component.ts
#	src/app/forgot-password/forgot-password-email/themed-forgot-email.component.ts
#	src/app/forgot-password/forgot-password-form/forgot-password-form.component.spec.ts
#	src/app/forgot-password/forgot-password-form/forgot-password-form.component.ts
#	src/app/forgot-password/forgot-password-form/themed-forgot-password-form.component.ts
#	src/app/forgot-password/forgot-password-routing.module.ts
#	src/app/forgot-password/forgot-password.module.ts
#	src/app/header-nav-wrapper/header-navbar-wrapper.component.ts
#	src/app/header/context-help-toggle/context-help-toggle.component.spec.ts
#	src/app/header/context-help-toggle/context-help-toggle.component.ts
#	src/app/header/header.component.spec.ts
#	src/app/header/header.component.ts
#	src/app/health-page/health-info/health-info-component/health-info-component.component.spec.ts
#	src/app/health-page/health-info/health-info-component/health-info-component.component.ts
#	src/app/health-page/health-info/health-info.component.spec.ts
#	src/app/health-page/health-info/health-info.component.ts
#	src/app/health-page/health-page.component.spec.ts
#	src/app/health-page/health-page.component.ts
#	src/app/health-page/health-page.module.ts
#	src/app/health-page/health-page.routing.module.ts
#	src/app/health-page/health-panel/health-component/health-component.component.spec.ts
#	src/app/health-page/health-panel/health-component/health-component.component.ts
#	src/app/health-page/health-panel/health-panel.component.spec.ts
#	src/app/health-page/health-panel/health-panel.component.ts
#	src/app/health-page/health-panel/health-status/health-status.component.spec.ts
#	src/app/health-page/health-panel/health-status/health-status.component.ts
#	src/app/home-page/home-news/home-news.component.ts
#	src/app/home-page/home-page-routing.module.ts
#	src/app/home-page/home-page.component.ts
#	src/app/home-page/home-page.module.ts
#	src/app/home-page/recent-item-list/recent-item-list.component.spec.ts
#	src/app/home-page/recent-item-list/recent-item-list.component.ts
#	src/app/home-page/top-level-community-list/top-level-community-list.component.spec.ts
#	src/app/home-page/top-level-community-list/top-level-community-list.component.ts
#	src/app/import-external-page/import-external-page.component.spec.ts
#	src/app/import-external-page/import-external-page.component.ts
#	src/app/import-external-page/import-external-page.module.ts
#	src/app/import-external-page/import-external-routing.module.ts
#	src/app/info/end-user-agreement/end-user-agreement-content/end-user-agreement-content.component.spec.ts
#	src/app/info/end-user-agreement/end-user-agreement-content/end-user-agreement-content.component.ts
#	src/app/info/end-user-agreement/end-user-agreement.component.spec.ts
#	src/app/info/end-user-agreement/end-user-agreement.component.ts
#	src/app/info/feedback/feedback-form/feedback-form.component.spec.ts
#	src/app/info/feedback/feedback-form/feedback-form.component.ts
#	src/app/info/feedback/feedback.component.spec.ts
#	src/app/info/feedback/feedback.component.ts
#	src/app/info/info-routing.module.ts
#	src/app/info/info.module.ts
#	src/app/info/privacy/privacy-content/privacy-content.component.spec.ts
#	src/app/info/privacy/privacy-content/privacy-content.component.ts
#	src/app/info/privacy/privacy.component.spec.ts
#	src/app/info/privacy/privacy.component.ts
#	src/app/init.service.ts
#	src/app/item-page/alerts/item-alerts.component.spec.ts
#	src/app/item-page/alerts/item-alerts.component.ts
#	src/app/item-page/bitstreams/request-a-copy/bitstream-request-a-copy-page.component.spec.ts
#	src/app/item-page/bitstreams/request-a-copy/bitstream-request-a-copy-page.component.ts
#	src/app/item-page/bitstreams/upload/upload-bitstream.component.spec.ts
#	src/app/item-page/bitstreams/upload/upload-bitstream.component.ts
#	src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts
#	src/app/item-page/edit-item-page/edit-item-page.component.spec.ts
#	src/app/item-page/edit-item-page/edit-item-page.component.ts
#	src/app/item-page/edit-item-page/edit-item-page.module.ts
#	src/app/item-page/edit-item-page/edit-item-page.routing.module.ts
#	src/app/item-page/edit-item-page/item-access-control/item-access-control.component.spec.ts
#	src/app/item-page/edit-item-page/item-access-control/item-access-control.component.ts
#	src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts
#	src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts
#	src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts
#	src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.ts
#	src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.spec.ts
#	src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts
#	src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.spec.ts
#	src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.ts
#	src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-drag-handle/item-edit-bitstream-drag-handle.component.ts
#	src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.spec.ts
#	src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.ts
#	src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.spec.ts
#	src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.ts
#	src/app/item-page/edit-item-page/item-curate/item-curate.component.spec.ts
#	src/app/item-page/edit-item-page/item-curate/item-curate.component.ts
#	src/app/item-page/edit-item-page/item-delete/item-delete.component.spec.ts
#	src/app/item-page/edit-item-page/item-delete/item-delete.component.ts
#	src/app/item-page/edit-item-page/item-move/item-move.component.spec.ts
#	src/app/item-page/edit-item-page/item-move/item-move.component.ts
#	src/app/item-page/edit-item-page/item-operation/item-operation.component.spec.ts
#	src/app/item-page/edit-item-page/item-operation/item-operation.component.ts
#	src/app/item-page/edit-item-page/item-private/item-private.component.spec.ts
#	src/app/item-page/edit-item-page/item-private/item-private.component.ts
#	src/app/item-page/edit-item-page/item-public/item-public.component.spec.ts
#	src/app/item-page/edit-item-page/item-public/item-public.component.ts
#	src/app/item-page/edit-item-page/item-register-doi/item-register-doi.component.spec.ts
#	src/app/item-page/edit-item-page/item-register-doi/item-register-doi.component.ts
#	src/app/item-page/edit-item-page/item-reinstate/item-reinstate.component.spec.ts
#	src/app/item-page/edit-item-page/item-reinstate/item-reinstate.component.ts
#	src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.spec.ts
#	src/app/item-page/edit-item-page/item-relationships/edit-relationship-list/edit-relationship-list.component.ts
#	src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.spec.ts
#	src/app/item-page/edit-item-page/item-relationships/edit-relationship/edit-relationship.component.ts
#	src/app/item-page/edit-item-page/item-relationships/item-relationships.component.spec.ts
#	src/app/item-page/edit-item-page/item-relationships/item-relationships.component.ts
#	src/app/item-page/edit-item-page/item-status/item-status.component.ts
#	src/app/item-page/edit-item-page/item-version-history/item-version-history.component.spec.ts
#	src/app/item-page/edit-item-page/item-version-history/item-version-history.component.ts
#	src/app/item-page/edit-item-page/item-withdraw/item-withdraw.component.spec.ts
#	src/app/item-page/edit-item-page/item-withdraw/item-withdraw.component.ts
#	src/app/item-page/edit-item-page/modify-item-overview/modify-item-overview.component.ts
#	src/app/item-page/edit-item-page/simple-item-action/abstract-simple-item-action.component.spec.ts
#	src/app/item-page/edit-item-page/simple-item-action/abstract-simple-item-action.component.ts
#	src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.spec.ts
#	src/app/item-page/edit-item-page/virtual-metadata/virtual-metadata.component.ts
#	src/app/item-page/field-components/collections/collections.component.spec.ts
#	src/app/item-page/field-components/collections/collections.component.ts
#	src/app/item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts
#	src/app/item-page/field-components/metadata-uri-values/metadata-uri-values.component.ts
#	src/app/item-page/field-components/metadata-values/metadata-values.component.spec.ts
#	src/app/item-page/field-components/metadata-values/metadata-values.component.ts
#	src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts
#	src/app/item-page/full/field-components/file-section/full-file-section.component.ts
#	src/app/item-page/full/full-item-page.component.spec.ts
#	src/app/item-page/full/full-item-page.component.ts
#	src/app/item-page/item-page-routing.module.ts
#	src/app/item-page/item-page.module.ts
#	src/app/item-page/item-shared.module.ts
#	src/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component.ts
#	src/app/item-page/media-viewer/media-viewer.component.spec.ts
#	src/app/item-page/media-viewer/media-viewer.component.ts
#	src/app/item-page/mirador-viewer/mirador-viewer.component.spec.ts
#	src/app/item-page/mirador-viewer/mirador-viewer.component.ts
#	src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.spec.ts
#	src/app/item-page/orcid-page/orcid-auth/orcid-auth.component.ts
#	src/app/item-page/orcid-page/orcid-page.component.html
#	src/app/item-page/orcid-page/orcid-page.component.spec.ts
#	src/app/item-page/orcid-page/orcid-page.component.ts
#	src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.spec.ts
#	src/app/item-page/orcid-page/orcid-queue/orcid-queue.component.ts
#	src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.spec.ts
#	src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.ts
#	src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts
#	src/app/item-page/simple/field-components/file-section/file-section.component.ts
#	src/app/item-page/simple/field-components/file-section/themed-file-section.component.ts
#	src/app/item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.spec.ts
#	src/app/item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts
#	src/app/item-page/simple/field-components/specific-field/author/item-page-author-field.component.spec.ts
#	src/app/item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts
#	src/app/item-page/simple/field-components/specific-field/date/item-page-date-field.component.spec.ts
#	src/app/item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts
#	src/app/item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.spec.ts
#	src/app/item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts
#	src/app/item-page/simple/field-components/specific-field/img/item-page-img-field.component.spec.ts
#	src/app/item-page/simple/field-components/specific-field/img/item-page-img-field.component.ts
#	src/app/item-page/simple/field-components/specific-field/item-page-field.component.spec.ts
#	src/app/item-page/simple/field-components/specific-field/item-page-field.component.ts
#	src/app/item-page/simple/field-components/specific-field/title/item-page-title-field.component.spec.ts
#	src/app/item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts
#	src/app/item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.spec.ts
#	src/app/item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts
#	src/app/item-page/simple/item-page.component.spec.ts
#	src/app/item-page/simple/item-page.component.ts
#	src/app/item-page/simple/item-types/publication/publication.component.spec.ts
#	src/app/item-page/simple/item-types/shared/item.component.spec.ts
#	src/app/item-page/simple/item-types/shared/item.component.ts
#	src/app/item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts
#	src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts
#	src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.ts
#	src/app/item-page/simple/notify-requests-status/notify-requests-status-component/notify-requests-status.component.spec.ts
#	src/app/item-page/simple/notify-requests-status/notify-requests-status-component/notify-requests-status.component.ts
#	src/app/item-page/simple/qa-event-notification/qa-event-notification.component.spec.ts
#	src/app/item-page/simple/qa-event-notification/qa-event-notification.component.ts
#	src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts
#	src/app/item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts
#	src/app/item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.spec.ts
#	src/app/item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.ts
#	src/app/item-page/simple/related-items/related-items-component.ts
#	src/app/item-page/simple/related-items/related-items.component.spec.ts
#	src/app/item-page/version-page/version-page/version-page.component.ts
#	src/app/item-page/versions/item-versions-delete-modal/item-versions-delete-modal.component.spec.ts
#	src/app/item-page/versions/item-versions-delete-modal/item-versions-delete-modal.component.ts
#	src/app/item-page/versions/item-versions-summary-modal/item-versions-summary-modal.component.spec.ts
#	src/app/item-page/versions/item-versions-summary-modal/item-versions-summary-modal.component.ts
#	src/app/item-page/versions/item-versions.component.spec.ts
#	src/app/item-page/versions/item-versions.component.ts
#	src/app/item-page/versions/item-versions.module.ts
#	src/app/item-page/versions/notice/item-versions-notice.component.spec.ts
#	src/app/item-page/versions/notice/item-versions-notice.component.ts
#	src/app/login-page/login-page-routing.module.ts
#	src/app/login-page/login-page.component.spec.ts
#	src/app/login-page/login-page.component.ts
#	src/app/login-page/login-page.module.ts
#	src/app/login-page/themed-login-page.component.ts
#	src/app/logout-page/logout-page-routing.module.ts
#	src/app/logout-page/logout-page.component.spec.ts
#	src/app/logout-page/logout-page.component.ts
#	src/app/logout-page/logout-page.module.ts
#	src/app/logout-page/themed-logout-page.component.ts
#	src/app/lookup-by-id/lookup-by-id-routes.ts
#	src/app/lookup-by-id/lookup-by-id.module.ts
#	src/app/lookup-by-id/objectnotfound/objectnotfound.component.spec.ts
#	src/app/lookup-by-id/objectnotfound/objectnotfound.component.ts
#	src/app/menu.resolver.spec.ts
#	src/app/menu.resolver.ts
#	src/app/my-dspace-page/collection-selector/collection-selector.component.spec.ts
#	src/app/my-dspace-page/collection-selector/collection-selector.component.ts
#	src/app/my-dspace-page/my-dspace-configuration.service.ts
#	src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-external-dropdown/my-dspace-new-external-dropdown.component.spec.ts
#	src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-external-dropdown/my-dspace-new-external-dropdown.component.ts
#	src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component.spec.ts
#	src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission-dropdown/my-dspace-new-submission-dropdown.component.ts
#	src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts
#	src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts
#	src/app/my-dspace-page/my-dspace-page-routing.module.ts
#	src/app/my-dspace-page/my-dspace-page.component.spec.ts
#	src/app/my-dspace-page/my-dspace-page.component.ts
#	src/app/my-dspace-page/my-dspace-page.module.ts
#	src/app/my-dspace-page/my-dspace-qa-events-notifications/my-dspace-qa-events-notifications.component.spec.ts
#	src/app/my-dspace-page/my-dspace-qa-events-notifications/my-dspace-qa-events-notifications.component.ts
#	src/app/my-dspace-page/my-dspace-search.module.ts
#	src/app/my-dspace-page/themed-my-dspace-page.component.ts
#	src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.spec.ts
#	src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts
#	src/app/navbar/navbar-section/navbar-section.component.spec.ts
#	src/app/navbar/navbar-section/navbar-section.component.ts
#	src/app/navbar/navbar.component.spec.ts
#	src/app/navbar/navbar.component.ts
#	src/app/navbar/navbar.module.ts
#	src/app/notifications/notifications.module.ts
#	src/app/notifications/qa/events/ePerson-data/ePerson-data.component.spec.ts
#	src/app/notifications/qa/events/ePerson-data/ePerson-data.component.ts
#	src/app/notifications/qa/events/quality-assurance-events.component.spec.ts
#	src/app/notifications/qa/events/quality-assurance-events.component.ts
#	src/app/notifications/qa/project-entry-import-modal/project-entry-import-modal.component.spec.ts
#	src/app/notifications/qa/project-entry-import-modal/project-entry-import-modal.component.ts
#	src/app/notifications/qa/source/quality-assurance-source.component.spec.ts
#	src/app/notifications/qa/source/quality-assurance-source.component.ts
#	src/app/notifications/qa/topics/quality-assurance-topics.component.spec.ts
#	src/app/notifications/qa/topics/quality-assurance-topics.component.ts
#	src/app/notifications/suggestion-actions/suggestion-actions.component.ts
#	src/app/notifications/suggestion-list-element/suggestion-evidences/suggestion-evidences.component.ts
#	src/app/notifications/suggestion-list-element/suggestion-list-element.component.spec.ts
#	src/app/notifications/suggestion-list-element/suggestion-list-element.component.ts
#	src/app/notifications/suggestion-targets/publication-claim/publication-claim.component.ts
#	src/app/notifications/suggestions-notification/suggestions-notification.component.ts
#	src/app/notifications/suggestions-popup/suggestions-popup.component.spec.ts
#	src/app/notifications/suggestions-popup/suggestions-popup.component.ts
#	src/app/page-error/page-error.component.spec.ts
#	src/app/page-error/page-error.component.ts
#	src/app/page-internal-server-error/page-internal-server-error.component.ts
#	src/app/pagenotfound/pagenotfound.component.ts
#	src/app/process-page/detail/process-detail-field/process-detail-field.component.spec.ts
#	src/app/process-page/detail/process-detail-field/process-detail-field.component.ts
#	src/app/process-page/detail/process-detail.component.spec.ts
#	src/app/process-page/detail/process-detail.component.ts
#	src/app/process-page/form/process-form.component.spec.ts
#	src/app/process-page/form/process-form.component.ts
#	src/app/process-page/form/process-parameters/parameter-select/parameter-select.component.spec.ts
#	src/app/process-page/form/process-parameters/parameter-select/parameter-select.component.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/boolean-value-input/boolean-value-input.component.spec.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/boolean-value-input/boolean-value-input.component.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/date-value-input/date-value-input.component.spec.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/date-value-input/date-value-input.component.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/file-value-input/file-value-input.component.spec.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/file-value-input/file-value-input.component.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/parameter-value-input.component.spec.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/parameter-value-input.component.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/string-value-input/string-value-input.component.spec.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/string-value-input/string-value-input.component.ts
#	src/app/process-page/form/process-parameters/process-parameters.component.spec.ts
#	src/app/process-page/form/process-parameters/process-parameters.component.ts
#	src/app/process-page/form/script-help/script-help.component.spec.ts
#	src/app/process-page/form/script-help/script-help.component.ts
#	src/app/process-page/form/scripts-select/scripts-select.component.spec.ts
#	src/app/process-page/form/scripts-select/scripts-select.component.ts
#	src/app/process-page/new/new-process.component.spec.ts
#	src/app/process-page/overview/process-overview.component.spec.ts
#	src/app/process-page/overview/process-overview.component.ts
#	src/app/process-page/overview/table/process-overview-table.component.ts
#	src/app/process-page/process-page-routing.module.ts
#	src/app/process-page/process-page-shared.module.ts
#	src/app/process-page/process-page.module.ts
#	src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.spec.ts
#	src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.ts
#	src/app/profile-page/profile-page-metadata-form/profile-page-metadata-form.component.spec.ts
#	src/app/profile-page/profile-page-metadata-form/profile-page-metadata-form.component.ts
#	src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.spec.ts
#	src/app/profile-page/profile-page-researcher-form/profile-page-researcher-form.component.ts
#	src/app/profile-page/profile-page-routing.module.ts
#	src/app/profile-page/profile-page-security-form/profile-page-security-form.component.spec.ts
#	src/app/profile-page/profile-page-security-form/profile-page-security-form.component.ts
#	src/app/profile-page/profile-page.component.spec.ts
#	src/app/profile-page/profile-page.component.ts
#	src/app/profile-page/profile-page.module.ts
#	src/app/profile-page/themed-profile-page.component.ts
#	src/app/quality-assurance-notifications-pages/notifications-pages-routing.module.ts
#	src/app/quality-assurance-notifications-pages/notifications-pages.module.ts
#	src/app/quality-assurance-notifications-pages/notifications-suggestion-targets-page/notifications-suggestion-targets-page.component.spec.ts
#	src/app/quality-assurance-notifications-pages/notifications-suggestion-targets-page/notifications-suggestion-targets-page.component.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.component.spec.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.component.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-source-page-component/quality-assurance-source-page.component.spec.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-topics-page/quality-assurance-topics-page.component.spec.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-topics-page/quality-assurance-topics-page.component.ts
#	src/app/register-email-form/register-email-form.component.spec.ts
#	src/app/register-email-form/register-email-form.component.ts
#	src/app/register-email-form/register-email-form.module.ts
#	src/app/register-page/create-profile/create-profile.component.spec.ts
#	src/app/register-page/create-profile/create-profile.component.ts
#	src/app/register-page/create-profile/themed-create-profile.component.ts
#	src/app/register-page/register-email/register-email.component.spec.ts
#	src/app/register-page/register-email/register-email.component.ts
#	src/app/register-page/register-page-routing.module.ts
#	src/app/register-page/register-page.module.ts
#	src/app/request-copy/deny-request-copy/deny-request-copy.component.spec.ts
#	src/app/request-copy/deny-request-copy/deny-request-copy.component.ts
#	src/app/request-copy/email-request-copy/email-request-copy.component.spec.ts
#	src/app/request-copy/email-request-copy/email-request-copy.component.ts
#	src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.spec.ts
#	src/app/request-copy/grant-deny-request-copy/grant-deny-request-copy.component.ts
#	src/app/request-copy/grant-request-copy/grant-request-copy.component.spec.ts
#	src/app/request-copy/grant-request-copy/grant-request-copy.component.ts
#	src/app/request-copy/request-copy-routing.module.ts
#	src/app/request-copy/request-copy.module.ts
#	src/app/root.module.ts
#	src/app/root/root.component.spec.ts
#	src/app/root/root.component.ts
#	src/app/search-navbar/search-navbar.component.spec.ts
#	src/app/search-navbar/search-navbar.component.ts
#	src/app/search-page/configuration-search-page.component.ts
#	src/app/search-page/search-page-routing.module.ts
#	src/app/search-page/search-page.component.ts
#	src/app/search-page/search-page.module.ts
#	src/app/shared/abstract-component-loader/dynamic-component-loader.directive.ts
#	src/app/shared/access-control-form-container/access-control-array-form/access-control-array-form.component.spec.ts
#	src/app/shared/access-control-form-container/access-control-array-form/access-control-array-form.component.ts
#	src/app/shared/access-control-form-container/access-control-array-form/to-date.pipe.ts
#	src/app/shared/access-control-form-container/access-control-form-container.component.spec.ts
#	src/app/shared/access-control-form-container/access-control-form-container.component.ts
#	src/app/shared/access-control-form-container/access-control-form.module.ts
#	src/app/shared/access-control-form-container/item-access-control-select-bitstreams-modal/item-access-control-select-bitstreams-modal.component.spec.ts
#	src/app/shared/access-control-form-container/item-access-control-select-bitstreams-modal/item-access-control-select-bitstreams-modal.component.ts
#	src/app/shared/alert/alert.component.spec.ts
#	src/app/shared/alert/alert.component.ts
#	src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts
#	src/app/shared/auth-nav-menu/auth-nav-menu.component.ts
#	src/app/shared/auth-nav-menu/user-menu/themed-user-menu.component.ts
#	src/app/shared/auth-nav-menu/user-menu/user-menu.component.spec.ts
#	src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts
#	src/app/shared/browse-by/browse-by.component.spec.ts
#	src/app/shared/browse-by/browse-by.component.ts
#	src/app/shared/browse-by/shared-browse-by.module.ts
#	src/app/shared/browse-by/themed-browse-by.component.ts
#	src/app/shared/collection-dropdown/collection-dropdown.component.spec.ts
#	src/app/shared/collection-dropdown/collection-dropdown.component.ts
#	src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.spec.ts
#	src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts
#	src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.spec.ts
#	src/app/shared/comcol/comcol-forms/create-comcol-page/create-comcol-page.component.ts
#	src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts
#	src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.ts
#	src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.spec.ts
#	src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-metadata/comcol-metadata.component.ts
#	src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts
#	src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts
#	src/app/shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.spec.ts
#	src/app/shared/comcol/comcol-forms/edit-comcol-page/edit-comcol-page.component.ts
#	src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts
#	src/app/shared/comcol/comcol-page-content/comcol-page-content.component.ts
#	src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.spec.ts
#	src/app/shared/comcol/comcol-page-handle/comcol-page-handle.component.ts
#	src/app/shared/comcol/comcol-page-header/comcol-page-header.component.ts
#	src/app/shared/comcol/comcol.module.ts
#	src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.ts
#	src/app/shared/confirmation-modal/confirmation-modal.component.spec.ts
#	src/app/shared/context-help-wrapper/context-help-wrapper.component.spec.ts
#	src/app/shared/context-help-wrapper/context-help-wrapper.component.ts
#	src/app/shared/context-help.directive.spec.ts
#	src/app/shared/correction-suggestion/withdrawn-reinstate-modal.component.ts
#	src/app/shared/ds-select/ds-select.component.ts
#	src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts
#	src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.spec.ts
#	src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.ts
#	src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.spec.ts
#	src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.ts
#	src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.spec.ts
#	src/app/shared/dso-page/dso-edit-menu/dso-edit-menu.component.ts
#	src/app/shared/dso-page/dso-page.module.ts
#	src/app/shared/dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component.spec.ts
#	src/app/shared/dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component.ts
#	src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts
#	src/app/shared/dso-selector/dso-selector/dso-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/themed-create-collection-parent-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/themed-create-community-parent-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/themed-create-item-parent-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/edit-collection-selector/themed-edit-collection-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/edit-community-selector/themed-edit-community-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/edit-item-selector/themed-edit-item-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.ts
#	src/app/shared/entity-dropdown/entity-dropdown.component.spec.ts
#	src/app/shared/entity-dropdown/entity-dropdown.component.ts
#	src/app/shared/eperson-group-list/eperson-group-list.component.spec.ts
#	src/app/shared/eperson-group-list/eperson-group-list.component.ts
#	src/app/shared/eperson-group-list/eperson-search-box/eperson-search-box.component.spec.ts
#	src/app/shared/eperson-group-list/eperson-search-box/eperson-search-box.component.ts
#	src/app/shared/eperson-group-list/group-search-box/group-search-box.component.spec.ts
#	src/app/shared/eperson-group-list/group-search-box/group-search-box.component.ts
#	src/app/shared/error/error.component.spec.ts
#	src/app/shared/error/error.component.ts
#	src/app/shared/file-download-link/file-download-link.component.spec.ts
#	src/app/shared/file-download-link/file-download-link.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/existing-relation-list-element/existing-relation-list-element.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/form-group/dynamic-form-group.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/external-source-entry-import-modal/external-source-entry-import-modal.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/selection-tab/dynamic-lookup-relation-selection-tab.component.ts
#	src/app/shared/form/chips/chips.component.spec.ts
#	src/app/shared/form/chips/chips.component.ts
#	src/app/shared/form/directives/authority-confidence-state.directive.ts
#	src/app/shared/form/form.component.spec.ts
#	src/app/shared/form/form.component.ts
#	src/app/shared/form/form.module.ts
#	src/app/shared/form/number-picker/number-picker.component.spec.ts
#	src/app/shared/form/number-picker/number-picker.component.ts
#	src/app/shared/form/vocabulary-treeview-modal/vocabulary-treeview-modal.component.spec.ts
#	src/app/shared/form/vocabulary-treeview-modal/vocabulary-treeview-modal.component.ts
#	src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.spec.ts
#	src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts
#	src/app/shared/google-recaptcha/google-recaptcha.component.spec.ts
#	src/app/shared/hover-class.directive.spec.ts
#	src/app/shared/hover-class.directive.ts
#	src/app/shared/idle-modal/idle-modal.component.spec.ts
#	src/app/shared/idle-modal/idle-modal.component.ts
#	src/app/shared/impersonate-navbar/impersonate-navbar.component.spec.ts
#	src/app/shared/impersonate-navbar/impersonate-navbar.component.ts
#	src/app/shared/input-suggestions/dso-input-suggestions/dso-input-suggestions.component.spec.ts
#	src/app/shared/input-suggestions/dso-input-suggestions/dso-input-suggestions.component.ts
#	src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.spec.ts
#	src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.ts
#	src/app/shared/input-suggestions/input-suggestions.component.spec.ts
#	src/app/shared/input-suggestions/input-suggestions.component.ts
#	src/app/shared/input-suggestions/validation-suggestions/validation-suggestions.component.spec.ts
#	src/app/shared/input-suggestions/validation-suggestions/validation-suggestions.component.ts
#	src/app/shared/lang-switch/lang-switch.component.spec.ts
#	src/app/shared/lang-switch/lang-switch.component.ts
#	src/app/shared/loading/loading.component.spec.ts
#	src/app/shared/loading/loading.component.ts
#	src/app/shared/log-in/container/log-in-container.component.spec.ts
#	src/app/shared/log-in/container/log-in-container.component.ts
#	src/app/shared/log-in/log-in.component.spec.ts
#	src/app/shared/log-in/log-in.component.ts
#	src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.spec.ts
#	src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.ts
#	src/app/shared/log-in/methods/log-in.methods-decorator.ts
#	src/app/shared/log-in/methods/password/log-in-password.component.spec.ts
#	src/app/shared/log-in/methods/password/log-in-password.component.ts
#	src/app/shared/log-in/themed-log-in.component.ts
#	src/app/shared/log-out/log-out.component.spec.ts
#	src/app/shared/log-out/log-out.component.ts
#	src/app/shared/menu/menu-item/external-link-menu-item.component.spec.ts
#	src/app/shared/menu/menu-item/external-link-menu-item.component.ts
#	src/app/shared/menu/menu-item/link-menu-item.component.spec.ts
#	src/app/shared/menu/menu-item/link-menu-item.component.ts
#	src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts
#	src/app/shared/menu/menu-item/onclick-menu-item.component.ts
#	src/app/shared/menu/menu-item/text-menu-item.component.spec.ts
#	src/app/shared/menu/menu-item/text-menu-item.component.ts
#	src/app/shared/menu/menu-section.decorator.ts
#	src/app/shared/menu/menu-section/menu-section.component.spec.ts
#	src/app/shared/menu/menu-section/menu-section.component.ts
#	src/app/shared/menu/menu.component.ts
#	src/app/shared/menu/menu.module.ts
#	src/app/shared/metadata-field-wrapper/metadata-field-wrapper.component.spec.ts
#	src/app/shared/metadata-field-wrapper/metadata-field-wrapper.component.ts
#	src/app/shared/metadata-representation/metadata-representation-loader.component.spec.ts
#	src/app/shared/metadata-representation/metadata-representation-loader.component.ts
#	src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.spec.ts
#	src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts
#	src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.spec.ts
#	src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.ts
#	src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.spec.ts
#	src/app/shared/mydspace-actions/claimed-task/decline-task/claimed-task-actions-decline-task.component.ts
#	src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.spec.ts
#	src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.ts
#	src/app/shared/mydspace-actions/claimed-task/rating/advanced-claimed-task-action-rating.component.ts
#	src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts
#	src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts
#	src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts
#	src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts
#	src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.spec.ts
#	src/app/shared/mydspace-actions/claimed-task/select-reviewer/advanced-claimed-task-action-select-reviewer.component.ts
#	src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator.spec.ts
#	src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.spec.ts
#	src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts
#	src/app/shared/mydspace-actions/item/item-actions.component.spec.ts
#	src/app/shared/mydspace-actions/item/item-actions.component.ts
#	src/app/shared/mydspace-actions/mydspace-actions.module.ts
#	src/app/shared/mydspace-actions/mydspace-reloadable-actions.spec.ts
#	src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts
#	src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts
#	src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.spec.ts
#	src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts
#	src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts
#	src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts
#	src/app/shared/notifications/notification/notification.component.spec.ts
#	src/app/shared/notifications/notification/notification.component.ts
#	src/app/shared/notifications/notifications-board/notifications-board.component.spec.ts
#	src/app/shared/notifications/notifications-board/notifications-board.component.ts
#	src/app/shared/notifications/notifications.reducers.spec.ts
#	src/app/shared/notifications/notifications.service.spec.ts
#	src/app/shared/object-collection/object-collection.component.spec.ts
#	src/app/shared/object-collection/object-collection.component.ts
#	src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.spec.ts
#	src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.ts
#	src/app/shared/object-collection/shared/badges/badges.component.spec.ts
#	src/app/shared/object-collection/shared/badges/badges.component.ts
#	src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.spec.ts
#	src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.ts
#	src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.spec.ts
#	src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.ts
#	src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.spec.ts
#	src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.ts
#	src/app/shared/object-collection/shared/importable-list-item-control/importable-list-item-control.component.ts
#	src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts
#	src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts
#	src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts
#	src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts
#	src/app/shared/object-collection/shared/selectable-list-item-control/selectable-list-item-control.component.spec.ts
#	src/app/shared/object-collection/shared/selectable-list-item-control/selectable-list-item-control.component.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.spec.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.spec.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.spec.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.spec.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/search-result-detail-element.component.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.spec.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.spec.ts
#	src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.ts
#	src/app/shared/object-detail/object-detail.component.spec.ts
#	src/app/shared/object-detail/object-detail.component.ts
#	src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.spec.ts
#	src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts
#	src/app/shared/object-grid/community-grid-element/community-grid-element.component.spec.ts
#	src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts
#	src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.spec.ts
#	src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.ts
#	src/app/shared/object-grid/object-grid.component.ts
#	src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts
#	src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts
#	src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts
#	src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts
#	src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts
#	src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts
#	src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts
#	src/app/shared/object-list/bitstream-list-item/bitstream-list-item.component.spec.ts
#	src/app/shared/object-list/bitstream-list-item/bitstream-list-item.component.ts
#	src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.spec.ts
#	src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts
#	src/app/shared/object-list/bundle-list-element/bundle-list-element.component.ts
#	src/app/shared/object-list/collection-list-element/collection-list-element.component.spec.ts
#	src/app/shared/object-list/collection-list-element/collection-list-element.component.ts
#	src/app/shared/object-list/community-list-element/community-list-element.component.spec.ts
#	src/app/shared/object-list/community-list-element/community-list-element.component.ts
#	src/app/shared/object-list/identifier-data/identifier-data.component.ts
#	src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts
#	src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.ts
#	src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.spec.ts
#	src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.ts
#	src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts
#	src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.ts
#	src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component.ts
#	src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.spec.ts
#	src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts
#	src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts
#	src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts
#	src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.spec.ts
#	src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts
#	src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.spec.ts
#	src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts
#	src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.spec.ts
#	src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.ts
#	src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts
#	src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts
#	src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts
#	src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts
#	src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts
#	src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.spec.ts
#	src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.ts
#	src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.spec.ts
#	src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts
#	src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.spec.ts
#	src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts
#	src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.spec.ts
#	src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts
#	src/app/shared/object-list/object-list.component.spec.ts
#	src/app/shared/object-list/object-list.component.ts
#	src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts
#	src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts
#	src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts
#	src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts
#	src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts
#	src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts
#	src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts
#	src/app/shared/object-list/sidebar-search-list-element/collection/collection-sidebar-search-list-element.component.ts
#	src/app/shared/object-list/sidebar-search-list-element/community/community-sidebar-search-list-element.component.ts
#	src/app/shared/object-list/sidebar-search-list-element/item-types/publication/publication-sidebar-search-list-element.component.ts
#	src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.spec.ts
#	src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts
#	src/app/shared/object-select/collection-select/collection-select.component.spec.ts
#	src/app/shared/object-select/collection-select/collection-select.component.ts
#	src/app/shared/object-select/item-select/item-select.component.spec.ts
#	src/app/shared/object-select/item-select/item-select.component.ts
#	src/app/shared/page-size-selector/page-size-selector.component.spec.ts
#	src/app/shared/page-size-selector/page-size-selector.component.ts
#	src/app/shared/pagination/pagination.component.spec.ts
#	src/app/shared/pagination/pagination.component.ts
#	src/app/shared/resource-policies/create/resource-policy-create.component.spec.ts
#	src/app/shared/resource-policies/create/resource-policy-create.component.ts
#	src/app/shared/resource-policies/edit/resource-policy-edit.component.spec.ts
#	src/app/shared/resource-policies/edit/resource-policy-edit.component.ts
#	src/app/shared/resource-policies/entry/resource-policy-entry.component.spec.ts
#	src/app/shared/resource-policies/entry/resource-policy-entry.component.ts
#	src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts
#	src/app/shared/resource-policies/form/resource-policy-form.component.ts
#	src/app/shared/resource-policies/resource-policies.component.spec.ts
#	src/app/shared/resource-policies/resource-policies.component.ts
#	src/app/shared/resource-policies/resource-policies.module.ts
#	src/app/shared/results-back-button/results-back-button.component.spec.ts
#	src/app/shared/results-back-button/results-back-button.component.ts
#	src/app/shared/results-back-button/results-back-button.module.ts
#	src/app/shared/roles/role.directive.ts
#	src/app/shared/rss-feed/rss.component.spec.ts
#	src/app/shared/rss-feed/rss.component.ts
#	src/app/shared/search-form/scope-selector-modal/scope-selector-modal.component.spec.ts
#	src/app/shared/search-form/scope-selector-modal/scope-selector-modal.component.ts
#	src/app/shared/search-form/search-form.component.spec.ts
#	src/app/shared/search-form/search-form.component.ts
#	src/app/shared/search/advanced-search/advanced-search.component.spec.ts
#	src/app/shared/search/advanced-search/advanced-search.component.ts
#	src/app/shared/search/search-export-csv/search-export-csv.component.spec.ts
#	src/app/shared/search/search-export-csv/search-export-csv.component.ts
#	src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts
#	src/app/shared/search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.ts
#	src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts
#	src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.ts
#	src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts
#	src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts
#	src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts
#	src/app/shared/search/search-filters/search-filter/search-filter.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-filter.component.ts
#	src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts
#	src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts
#	src/app/shared/search/search-filters/search-filter/search-text-filter/search-text-filter.component.ts
#	src/app/shared/search/search-filters/search-filters.component.spec.ts
#	src/app/shared/search/search-filters/search-filters.component.ts
#	src/app/shared/search/search-labels/search-label/search-label.component.spec.ts
#	src/app/shared/search/search-labels/search-label/search-label.component.ts
#	src/app/shared/search/search-labels/search-labels.component.spec.ts
#	src/app/shared/search/search-labels/search-labels.component.ts
#	src/app/shared/search/search-results/search-results.component.spec.ts
#	src/app/shared/search/search-results/search-results.component.ts
#	src/app/shared/search/search-settings/search-settings.component.spec.ts
#	src/app/shared/search/search-settings/search-settings.component.ts
#	src/app/shared/search/search-sidebar/search-sidebar.component.spec.ts
#	src/app/shared/search/search-sidebar/search-sidebar.component.ts
#	src/app/shared/search/search-switch-configuration/search-switch-configuration.component.spec.ts
#	src/app/shared/search/search-switch-configuration/search-switch-configuration.component.ts
#	src/app/shared/search/search.component.spec.ts
#	src/app/shared/search/search.component.ts
#	src/app/shared/search/search.module.ts
#	src/app/shared/shared.module.ts
#	src/app/shared/sidebar/page-with-sidebar.component.spec.ts
#	src/app/shared/sidebar/sidebar-dropdown.component.ts
#	src/app/shared/starts-with/date/starts-with-date.component.spec.ts
#	src/app/shared/starts-with/date/starts-with-date.component.ts
#	src/app/shared/starts-with/starts-with-abstract.component.ts
#	src/app/shared/starts-with/starts-with-decorator.spec.ts
#	src/app/shared/starts-with/starts-with-loader.component.spec.ts
#	src/app/shared/starts-with/starts-with-loader.component.ts
#	src/app/shared/starts-with/text/starts-with-text.component.spec.ts
#	src/app/shared/starts-with/text/starts-with-text.component.ts
#	src/app/shared/subscriptions/subscription-modal/subscription-modal.component.spec.ts
#	src/app/shared/subscriptions/subscription-modal/subscription-modal.component.ts
#	src/app/shared/subscriptions/subscription-view/subscription-view.component.spec.ts
#	src/app/shared/subscriptions/subscription-view/subscription-view.component.ts
#	src/app/shared/subscriptions/subscriptions-data.service.ts
#	src/app/shared/subscriptions/subscriptions.module.ts
#	src/app/shared/testing/browser-only-mock.pipe.ts
#	src/app/shared/testing/dynamic-form-mock-services.ts
#	src/app/shared/testing/route-service.stub.ts
#	src/app/shared/testing/test-module.ts
#	src/app/shared/testing/utils.test.ts
#	src/app/shared/theme-support/themed.component.spec.ts
#	src/app/shared/trackable/abstract-trackable.component.spec.ts
#	src/app/shared/trackable/abstract-trackable.component.ts
#	src/app/shared/truncatable/truncatable-part/truncatable-part.component.spec.ts
#	src/app/shared/truncatable/truncatable-part/truncatable-part.component.ts
#	src/app/shared/truncatable/truncatable.component.spec.ts
#	src/app/shared/upload/file-dropzone-no-uploader/file-dropzone-no-uploader.component.ts
#	src/app/shared/upload/upload.module.ts
#	src/app/shared/upload/uploader/uploader.component.spec.ts
#	src/app/shared/upload/uploader/uploader.component.ts
#	src/app/shared/utils/auto-focus.directive.ts
#	src/app/shared/utils/browser-only.pipe.ts
#	src/app/shared/utils/capitalize.pipe.ts
#	src/app/shared/utils/click-outside.directive.ts
#	src/app/shared/utils/console.pipe.ts
#	src/app/shared/utils/drag-click.directive.ts
#	src/app/shared/utils/file-value-accessor.directive.ts
#	src/app/shared/utils/in-list-validator.directive.ts
#	src/app/shared/utils/markdown.pipe.ts
#	src/app/shared/utils/metadatafield-validator.directive.ts
#	src/app/shared/utils/object-keys-pipe.ts
#	src/app/shared/utils/object-ngfor.pipe.ts
#	src/app/shared/utils/object-values-pipe.ts
#	src/app/shared/utils/require-file.validator.ts
#	src/app/shared/utils/short-number.pipe.ts
#	src/app/shared/utils/split.pipe.ts
#	src/app/shared/utils/truncate.pipe.ts
#	src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts
#	src/app/shared/view-mode-switch/view-mode-switch.component.ts
#	src/app/statistics-page/collection-statistics-page/collection-statistics-page.component.spec.ts
#	src/app/statistics-page/collection-statistics-page/collection-statistics-page.component.ts
#	src/app/statistics-page/community-statistics-page/community-statistics-page.component.spec.ts
#	src/app/statistics-page/community-statistics-page/community-statistics-page.component.ts
#	src/app/statistics-page/item-statistics-page/item-statistics-page.component.spec.ts
#	src/app/statistics-page/item-statistics-page/item-statistics-page.component.ts
#	src/app/statistics-page/site-statistics-page/site-statistics-page.component.spec.ts
#	src/app/statistics-page/site-statistics-page/site-statistics-page.component.ts
#	src/app/statistics-page/statistics-page-routing.module.ts
#	src/app/statistics-page/statistics-page.module.ts
#	src/app/statistics-page/statistics-page/statistics-page.directive.ts
#	src/app/statistics-page/statistics-table/statistics-table.component.spec.ts
#	src/app/statistics-page/statistics-table/statistics-table.component.ts
#	src/app/statistics/statistics.module.ts
#	src/app/submission/edit/submission-edit.component.spec.ts
#	src/app/submission/edit/submission-edit.component.ts
#	src/app/submission/edit/themed-submission-edit.component.ts
#	src/app/submission/form/collection/submission-form-collection.component.spec.ts
#	src/app/submission/form/collection/submission-form-collection.component.ts
#	src/app/submission/form/footer/submission-form-footer.component.spec.ts
#	src/app/submission/form/footer/submission-form-footer.component.ts
#	src/app/submission/form/section-add/submission-form-section-add.component.spec.ts
#	src/app/submission/form/section-add/submission-form-section-add.component.ts
#	src/app/submission/form/submission-form.component.spec.ts
#	src/app/submission/form/submission-form.component.ts
#	src/app/submission/form/submission-upload-files/submission-upload-files.component.spec.ts
#	src/app/submission/form/submission-upload-files/submission-upload-files.component.ts
#	src/app/submission/import-external/import-external-collection/submission-import-external-collection.component.spec.ts
#	src/app/submission/import-external/import-external-collection/submission-import-external-collection.component.ts
#	src/app/submission/import-external/import-external-preview/submission-import-external-preview.component.spec.ts
#	src/app/submission/import-external/import-external-preview/submission-import-external-preview.component.ts
#	src/app/submission/import-external/import-external-searchbar/submission-import-external-searchbar.component.spec.ts
#	src/app/submission/import-external/import-external-searchbar/submission-import-external-searchbar.component.ts
#	src/app/submission/import-external/submission-import-external.component.spec.ts
#	src/app/submission/import-external/submission-import-external.component.ts
#	src/app/submission/import-external/themed-submission-import-external.component.ts
#	src/app/submission/sections/accesses/section-accesses.component.spec.ts
#	src/app/submission/sections/accesses/section-accesses.component.ts
#	src/app/submission/sections/cc-license/submission-section-cc-licenses.component.spec.ts
#	src/app/submission/sections/cc-license/submission-section-cc-licenses.component.ts
#	src/app/submission/sections/container/section-container.component.spec.ts
#	src/app/submission/sections/container/section-container.component.ts
#	src/app/submission/sections/form/section-form-operations.service.spec.ts
#	src/app/submission/sections/form/section-form.component.spec.ts
#	src/app/submission/sections/form/section-form.component.ts
#	src/app/submission/sections/identifiers/section-identifiers.component.spec.ts
#	src/app/submission/sections/identifiers/section-identifiers.component.ts
#	src/app/submission/sections/license/section-license.component.spec.ts
#	src/app/submission/sections/license/section-license.component.ts
#	src/app/submission/sections/section-coar-notify/section-coar-notify.component.ts
#	src/app/submission/sections/sections.directive.ts
#	src/app/submission/sections/sherpa-policies/content-accordion/content-accordion.component.spec.ts
#	src/app/submission/sections/sherpa-policies/content-accordion/content-accordion.component.ts
#	src/app/submission/sections/sherpa-policies/metadata-information/metadata-information.component.spec.ts
#	src/app/submission/sections/sherpa-policies/metadata-information/metadata-information.component.ts
#	src/app/submission/sections/sherpa-policies/publication-information/publication-information.component.spec.ts
#	src/app/submission/sections/sherpa-policies/publication-information/publication-information.component.ts
#	src/app/submission/sections/sherpa-policies/publisher-policy/publisher-policy.component.spec.ts
#	src/app/submission/sections/sherpa-policies/publisher-policy/publisher-policy.component.ts
#	src/app/submission/sections/sherpa-policies/section-sherpa-policies.component.spec.ts
#	src/app/submission/sections/sherpa-policies/section-sherpa-policies.component.ts
#	src/app/submission/sections/upload/accessConditions/submission-section-upload-access-conditions.component.ts
#	src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts
#	src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts
#	src/app/submission/sections/upload/file/section-upload-file.component.spec.ts
#	src/app/submission/sections/upload/file/section-upload-file.component.ts
#	src/app/submission/sections/upload/file/themed-section-upload-file.component.ts
#	src/app/submission/sections/upload/file/view/section-upload-file-view.component.spec.ts
#	src/app/submission/sections/upload/file/view/section-upload-file-view.component.ts
#	src/app/submission/sections/upload/section-upload.component.spec.ts
#	src/app/submission/sections/upload/section-upload.component.ts
#	src/app/submission/submission.module.ts
#	src/app/submission/submit/submission-submit.component.spec.ts
#	src/app/submission/submit/submission-submit.component.ts
#	src/app/submission/submit/themed-submission-submit.component.ts
#	src/app/submit-page/submit-page-routing.module.ts
#	src/app/submit-page/submit-page.module.ts
#	src/app/subscriptions-page/subscriptions-page-routing.module.ts
#	src/app/subscriptions-page/subscriptions-page.component.spec.ts
#	src/app/subscriptions-page/subscriptions-page.component.ts
#	src/app/subscriptions-page/subscriptions-page.module.ts
#	src/app/suggestions-page/suggestions-page-routing.module.ts
#	src/app/suggestions-page/suggestions-page.component.spec.ts
#	src/app/suggestions-page/suggestions-page.component.ts
#	src/app/suggestions-page/suggestions-page.module.ts
#	src/app/system-wide-alert/alert-banner/system-wide-alert-banner.component.spec.ts
#	src/app/system-wide-alert/alert-banner/system-wide-alert-banner.component.ts
#	src/app/system-wide-alert/alert-form/system-wide-alert-form.component.spec.ts
#	src/app/system-wide-alert/alert-form/system-wide-alert-form.component.ts
#	src/app/system-wide-alert/system-wide-alert-routing.module.ts
#	src/app/system-wide-alert/system-wide-alert.module.ts
#	src/app/thumbnail/thumbnail.component.spec.ts
#	src/app/thumbnail/thumbnail.component.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-rating/advanced-workflow-action-rating.component.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/advanced-workflow-action-select-reviewer.component.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.spec.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action/advanced-workflow-action.component.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts
#	src/app/workflowitems-edit-page/workflow-item-action-page.component.spec.ts
#	src/app/workflowitems-edit-page/workflow-item-action-page.component.ts
#	src/app/workflowitems-edit-page/workflow-item-delete/themed-workflow-item-delete.component.ts
#	src/app/workflowitems-edit-page/workflow-item-delete/workflow-item-delete.component.spec.ts
#	src/app/workflowitems-edit-page/workflow-item-delete/workflow-item-delete.component.ts
#	src/app/workflowitems-edit-page/workflow-item-send-back/themed-workflow-item-send-back.component.ts
#	src/app/workflowitems-edit-page/workflow-item-send-back/workflow-item-send-back.component.spec.ts
#	src/app/workflowitems-edit-page/workflow-item-send-back/workflow-item-send-back.component.ts
#	src/app/workflowitems-edit-page/workflowitems-edit-page-routing.module.ts
#	src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts
#	src/app/workspaceitems-edit-page/workspaceitems-delete-page/themed-workspaceitems-delete-page.component.ts
#	src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts
#	src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts
#	src/app/workspaceitems-edit-page/workspaceitems-edit-page-routing.module.ts
#	src/app/workspaceitems-edit-page/workspaceitems-edit-page.module.ts
#	src/config/app-config.interface.ts
#	src/modules/app/browser-app.module.ts
#	src/modules/app/server-app.module.ts
#	src/test.ts
#	src/themes/custom/app/admin/admin-sidebar/admin-sidebar.component.ts
#	src/themes/custom/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts
#	src/themes/custom/app/breadcrumbs/breadcrumbs.component.ts
#	src/themes/custom/app/browse-by/browse-by-title/browse-by-title.component.ts
#	src/themes/custom/app/collection-page/collection-page.component.ts
#	src/themes/custom/app/collection-page/edit-item-template-page/edit-item-template-page.component.ts
#	src/themes/custom/app/community-list-page/community-list-page.component.ts
#	src/themes/custom/app/community-list-page/community-list/community-list.component.ts
#	src/themes/custom/app/community-page/community-page.component.ts
#	src/themes/custom/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts
#	src/themes/custom/app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component.ts
#	src/themes/custom/app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component.ts
#	src/themes/custom/app/entity-groups/journal-entities/item-pages/journal/journal.component.ts
#	src/themes/custom/app/entity-groups/research-entities/item-pages/person/person.component.ts
#	src/themes/custom/app/footer/footer.component.ts
#	src/themes/custom/app/forbidden/forbidden.component.ts
#	src/themes/custom/app/forgot-password/forgot-password-email/forgot-email.component.ts
#	src/themes/custom/app/forgot-password/forgot-password-form/forgot-password-form.component.ts
#	src/themes/custom/app/header-nav-wrapper/header-navbar-wrapper.component.ts
#	src/themes/custom/app/home-page/home-news/home-news.component.ts
#	src/themes/custom/app/home-page/home-page.component.ts
#	src/themes/custom/app/home-page/top-level-community-list/top-level-community-list.component.ts
#	src/themes/custom/app/info/end-user-agreement/end-user-agreement.component.ts
#	src/themes/custom/app/info/feedback/feedback-form/feedback-form.component.ts
#	src/themes/custom/app/info/feedback/feedback.component.ts
#	src/themes/custom/app/info/privacy/privacy.component.ts
#	src/themes/custom/app/item-page/edit-item-page/item-status/item-status.component.ts
#	src/themes/custom/app/item-page/full/field-components/file-section/full-file-section.component.ts
#	src/themes/custom/app/item-page/full/full-item-page.component.ts
#	src/themes/custom/app/item-page/media-viewer/media-viewer-image/media-viewer-image.component.ts
#	src/themes/custom/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.ts
#	src/themes/custom/app/item-page/media-viewer/media-viewer.component.ts
#	src/themes/custom/app/item-page/simple/field-components/file-section/file-section.component.ts
#	src/themes/custom/app/item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts
#	src/themes/custom/app/item-page/simple/item-page.component.ts
#	src/themes/custom/app/item-page/simple/item-types/publication/publication.component.ts
#	src/themes/custom/app/item-page/simple/item-types/untyped-item/untyped-item.component.ts
#	src/themes/custom/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.ts
#	src/themes/custom/app/login-page/login-page.component.ts
#	src/themes/custom/app/logout-page/logout-page.component.ts
#	src/themes/custom/app/lookup-by-id/objectnotfound/objectnotfound.component.ts
#	src/themes/custom/app/my-dspace-page/my-dspace-page.component.ts
#	src/themes/custom/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts
#	src/themes/custom/app/navbar/navbar.component.ts
#	src/themes/custom/app/pagenotfound/pagenotfound.component.ts
#	src/themes/custom/app/profile-page/profile-page.component.ts
#	src/themes/custom/app/register-email-form/register-email-form.component.ts
#	src/themes/custom/app/register-page/create-profile/create-profile.component.ts
#	src/themes/custom/app/register-page/register-email/register-email.component.ts
#	src/themes/custom/app/request-copy/deny-request-copy/deny-request-copy.component.ts
#	src/themes/custom/app/request-copy/email-request-copy/email-request-copy.component.ts
#	src/themes/custom/app/request-copy/grant-request-copy/grant-request-copy.component.ts
#	src/themes/custom/app/root/root.component.ts
#	src/themes/custom/app/search-navbar/search-navbar.component.ts
#	src/themes/custom/app/search-page/configuration-search-page.component.ts
#	src/themes/custom/app/search-page/search-page.component.ts
#	src/themes/custom/app/shared/auth-nav-menu/auth-nav-menu.component.ts
#	src/themes/custom/app/shared/comcol-page-browse-by/comcol-page-browse-by.component.ts
#	src/themes/custom/app/shared/comcol-page-handle/comcol-page-handle.component.ts
#	src/themes/custom/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts
#	src/themes/custom/app/shared/dso-selector/modal-wrappers/create-community-parent-selector/create-community-parent-selector.component.ts
#	src/themes/custom/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts
#	src/themes/custom/app/shared/dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component.ts
#	src/themes/custom/app/shared/dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component.ts
#	src/themes/custom/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts
#	src/themes/custom/app/shared/file-download-link/file-download-link.component.ts
#	src/themes/custom/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/external-source-tab/dynamic-lookup-relation-external-source-tab.component.ts
#	src/themes/custom/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/search-tab/dynamic-lookup-relation-search-tab.component.ts
#	src/themes/custom/app/shared/loading/loading.component.ts
#	src/themes/custom/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts
#	src/themes/custom/app/shared/object-list/collection-list-element/collection-list-element.component.ts
#	src/themes/custom/app/shared/object-list/community-list-element/community-list-element.component.ts
#	src/themes/custom/app/shared/object-list/object-list.component.ts
#	src/themes/custom/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts
#	src/themes/custom/app/shared/object-list/sidebar-search-list-element/item-types/publication-sidebar-search-list-element.component.ts
#	src/themes/custom/app/shared/results-back-button/results-back-button.component.ts
#	src/themes/custom/app/shared/search-form/search-form.component.ts
#	src/themes/custom/app/shared/search/search-filters/search-filters.component.ts
#	src/themes/custom/app/shared/search/search-results/search-results.component.ts
#	src/themes/custom/app/shared/search/search-settings/search-settings.component.ts
#	src/themes/custom/app/shared/search/search-sidebar/search-sidebar.component.ts
#	src/themes/custom/app/shared/starts-with/date/starts-with-date.component.ts
#	src/themes/custom/app/shared/starts-with/text/starts-with-text.component.ts
#	src/themes/custom/app/statistics-page/collection-statistics-page/collection-statistics-page.component.ts
#	src/themes/custom/app/statistics-page/community-statistics-page/community-statistics-page.component.ts
#	src/themes/custom/app/statistics-page/item-statistics-page/item-statistics-page.component.ts
#	src/themes/custom/app/statistics-page/site-statistics-page/site-statistics-page.component.ts
#	src/themes/custom/app/submission/edit/submission-edit.component.ts
#	src/themes/custom/app/submission/import-external/submission-import-external.component.ts
#	src/themes/custom/app/submission/sections/upload/file/section-upload-file.component.ts
#	src/themes/custom/app/submission/submit/submission-submit.component.ts
#	src/themes/custom/app/workflowitems-edit-page/workflow-item-delete/workflow-item-delete.component.ts
#	src/themes/custom/app/workflowitems-edit-page/workflow-item-send-back/workflow-item-send-back.component.ts
#	src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.ts
#	src/themes/custom/eager-theme.module.ts
#	src/themes/custom/lazy-theme.module.ts
#	src/themes/dspace/app/header-nav-wrapper/header-navbar-wrapper.component.ts
#	src/themes/dspace/app/header/header.component.ts
#	src/themes/dspace/app/home-page/home-news/home-news.component.ts
#	src/themes/dspace/app/navbar/navbar.component.ts
#	src/themes/dspace/eager-theme.module.ts
#	src/themes/dspace/lazy-theme.module.ts
2024-03-12 17:47:51 +01:00
Yury Bondarenko
55573c5827 Merge remote-tracking branch 'origin/main' into disable-inline-css 2024-03-12 17:17:39 +01:00
Alan Orth
a8f31948bc Merge pull request #2777 from TAMULib/dynamic-tag-on-blur
Element reference for typeahead directive is only defined when model has authority
2024-03-08 23:12:59 +03:00
Giuseppe Digilio
f90880bbac [DURACOM-191] Fix error on home when coar is not enabled 2024-03-08 19:14:03 +01:00
Andrea Barbasso
ae436fbd5f [DURACOM-191] remove already present strings 2024-03-08 17:22:13 +01:00
Andrea Barbasso
cc8d931263 [DURACOM-191] fix missing pipe in createReportMenuSections 2024-03-08 16:58:42 +01:00
Tim Donohue
a2531e5f6e Merge pull request #2343 from ybnd/more-eslint
Stricter ESLint configuration for code quality, consistency & cleaner merges
2024-03-08 09:24:30 -06:00
Andrea Barbasso
a9c975b72a [DURACOM-191] fix runtime errors for store features 2024-03-08 15:59:50 +01:00
Andrea Barbasso
dbbf99229a [DURACOM-191] delete core module 2024-03-08 15:27:38 +01:00
Andrea Barbasso
a147f78a1d [DURACOM-191] fix errors at runtime 2024-03-08 13:12:05 +01:00
Yury Bondarenko
33d5253d47 Only log errors for DspaceRestService.get
When refactoring to meet the rxjs/no-implicit-any-catch ESLint rule, I'd made it so errors were logged for DspaceRestService.request calls as well, making it a lot more noisy
The console.log in .get is explicitly required by tests though
2024-03-08 11:02:48 +01:00
Andrea Barbasso
19f806caf2 [DURACOM-191] fix dynamicComponentLoader 2024-03-07 15:41:37 +01:00
Yury Bondarenko
86885f7dce Merge remote-tracking branch 'origin/main' into more-eslint 2024-03-07 09:56:55 +01:00
Yury Bondarenko
5edc689bc1 Fix null/undefined incosistencies
- Introduced pipes for combined null/undefined checks: dsHasValue & dsHasNoValue
- Safer to async !== true than async === false
- Went over other instances of async === to confirm that they should be fine
2024-03-07 09:55:36 +01:00
Andrea Barbasso
a8b4c82b9a [DURACOM-191] fix errors at runtime 2024-03-06 17:19:31 +01:00
Tim Donohue
a9c58a1d5a Merge pull request #2856 from 4Science/coar-notify-itempage-error-on-ldnenabled-false
fix 'undefined' error on anonymous item page landing
2024-03-06 09:22:40 -06:00
Andrea Barbasso
3a5f9cdd63 [DURACOM-191] fix circular dependencies 2024-03-06 15:35:29 +01:00
Yury Bondarenko
ebbbd64bae Fix tests 2024-03-06 11:56:17 +01:00
Yury Bondarenko
3b48d9a26d Manually fix lint issues 2024-03-06 11:56:17 +01:00
Yury Bondarenko
2b540cd91c Autofix lint issues 2024-03-06 10:30:19 +01:00
Yury Bondarenko
dbf7fd67fd Merge remote-tracking branch 'origin/main' into more-eslint 2024-03-06 10:26:07 +01:00
frabacche
e6389a73df fix e2e test 18x aria on <ul> html tag 2024-03-06 09:30:42 +01:00
Andrea Barbasso
92c944a8fb [DURACOM-191] run migration script 2024-03-05 16:25:21 +01:00
Andrea Barbasso
59cb88ee45 [DURACOM-191] fix build errors 2024-03-05 16:22:53 +01:00
frabacche
dd9fc1881f fix 'undefined' error on anonymous item page landing 2024-03-05 16:13:00 +01:00
Tim Donohue
230055ce6f Merge pull request #2851 from saschaszott/patch-19
fix selector value in CommunitySidebarSearchListElementComponent
2024-03-05 09:12:58 -06:00
Simone Ramundi
3907bc114f [DURACOM-237] - Added imports inside ts component 2024-03-05 12:20:33 +01:00
Andrea Barbasso
a38f96d13b [DURACOM-191] delete useless modules 2024-03-05 12:01:41 +01:00
Andrea Barbasso
c5730e554d [DURACOM-191] fix build errors 2024-03-05 11:45:57 +01:00
Simone Ramundi
6970f1ee67 [DURACOM-237] - Refactoring ts based on @angular/cdk/drag-drop 2024-03-05 11:39:22 +01:00
Simone Ramundi
4b0bf74d29 [DURACOM-237] - Refactoring html based on @angular/cdk/drag-drop 2024-03-05 11:39:22 +01:00
Simone Ramundi
478783f1f3 [DURACOM-237] - Refactoring scss based on @angular/cdk/drag-drop 2024-03-05 11:39:22 +01:00
Simone Ramundi
671fa8f220 [DURACOM-237] - Refactoring html based on @angular/cdk/drag-drop 2024-03-05 11:39:22 +01:00
Simone Ramundi
61bf50e152 [DURACOM-237] - Refactoring html based on @angular/cdk/drag-drop 2024-03-05 11:39:22 +01:00
Simone Ramundi
9e724f38c9 [DURACOM-237] - Refactoring scss based on @angular/cdk/drag-drop 2024-03-05 11:39:22 +01:00
Simone Ramundi
e092914c00 [DURACOM-237] - Refactoring html based on @angular/cdk/drag-drop 2024-03-05 11:39:22 +01:00
Simone Ramundi
dbf6b9d1f9 [DURACOM-237] - Refactoring scss based on @angular/cdk/drag-drop 2024-03-05 11:39:21 +01:00
Simone Ramundi
0c80b1a366 [DURACOM-237] - Refactoring html based on @angular/cdk/drag-drop 2024-03-05 11:39:21 +01:00
Simone Ramundi
3807a3563c [DURACOM-237] - Refactoring ts based on @angular/cdk/drag-drop 2024-03-05 11:36:58 +01:00
Simone Ramundi
3051fa8d8d [DURACOM-237] - Added DragDropModule which replaced the SortablejsModule inside form.module.ts 2024-03-05 11:31:37 +01:00
Andrea Barbasso
9cd1db54c0 [DURACOM-191] fix browse-by-decorator map 2024-03-05 11:17:25 +01:00
Sascha Szott
ad3ef56e06 fix selector value in CommunitySidebarSearchListElementComponent 2024-03-05 11:10:12 +01:00
Andrea Barbasso
36edc58979 [DURACOM-191] migrate components to standalone 2024-03-05 10:44:39 +01:00
Andrea Barbasso
8bfb776eef [DURACOM-191] fix standalone components 2024-03-05 09:55:31 +01:00
Andrea Barbasso
7b3571e606 [DURACOM-191] make section-coar-notify standalone 2024-03-05 09:32:43 +01:00
Andrea Barbasso
344cbf1111 [DURACOM-191] run migration script 2024-03-05 09:28:13 +01:00
Andrea Barbasso
e5d7f11e15 [DURACOM-191] fix missing import 2024-03-05 08:46:42 +01:00
Andrea Barbasso
ad1a3e6172 [DURACOM-191] fix routing module 2024-03-05 08:46:03 +01:00
Andrea Barbasso
0f97fc3d86 [DURACOM-191] fix routing modules 2024-03-05 08:44:07 +01:00
Andrea Barbasso
5ec649f329 [DURACOM-191] fix routing modules 2024-03-05 08:14:13 +01:00
Tim Donohue
761b0c2203 Merge pull request #2751 from 4Science/coar-notify-7-part-two
Coar Notify Integration - Administer/Log
2024-03-04 16:32:23 -06:00
Tim Donohue
76f5d34bbe Merge pull request #2847 from DSpace/dependabot/npm_and_yarn/sanitize-html-2.12.1
Bump sanitize-html from 2.10.0 to 2.12.1
2024-03-04 16:02:23 -06:00
Tim Donohue
4cc5a383cc Merge pull request #2371 from alexandrevryghem/fix-specs-without-expectations_contribute-main
Fix Spec has no expectations errors
2024-03-04 14:11:29 -06:00
Andrea Barbasso
923c375d67 [DURACOM-191] add more fixes 2024-03-04 17:26:21 +01:00
frabacche
0f7965ea6e Merge remote-tracking branch 'github/main' into coar-notify-7-part-two 2024-03-04 17:26:17 +01:00
Andrea Barbasso
8503b4a476 [DURACOM-191] run migration script 2024-03-04 16:40:12 +01:00
Andrea Barbasso
5a57035b61 [DURACOM-191] merge fixes 2024-03-04 16:30:46 +01:00
Tim Donohue
f6f396221d Merge pull request #2749 from the-library-code/DSpace_detect_duplicates_PR
Basic Duplicate Detection
2024-03-04 09:28:26 -06:00
Andrea Barbasso
914ca68ca2 Merge remote-tracking branch 'github/main' into DURACOM-191-20240304-align-to-main
# Conflicts:
#	src/app/admin/admin-notifications/admin-notifications-routing.module.ts
#	src/app/admin/admin-notifications/admin-notifications.module.ts
#	src/app/admin/admin-notifications/admin-quality-assurance-source-page-component/admin-quality-assurance-source-page.component.spec.ts
#	src/app/admin/admin-notifications/admin-quality-assurance-source-page-component/admin-quality-assurance-source-page.component.ts
#	src/app/admin/admin-notifications/admin-quality-assurance-topics-page/admin-quality-assurance-topics-page.component.spec.ts
#	src/app/admin/admin-routing.module.ts
#	src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.spec.ts
#	src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts
#	src/app/admin/admin.module.ts
#	src/app/app-routing.module.ts
#	src/app/bitstream-page/edit-bitstream-page/edit-bitstream-page.component.ts
#	src/app/browse-by/browse-by-date-page/themed-browse-by-date-page.component.ts
#	src/app/browse-by/browse-by-date/browse-by-date.component.spec.ts
#	src/app/browse-by/browse-by-date/browse-by-date.component.ts
#	src/app/browse-by/browse-by-guard.spec.ts
#	src/app/browse-by/browse-by-metadata-page/themed-browse-by-metadata-page.component.ts
#	src/app/browse-by/browse-by-metadata/browse-by-metadata.component.spec.ts
#	src/app/browse-by/browse-by-metadata/browse-by-metadata.component.ts
#	src/app/browse-by/browse-by-page.module.ts
#	src/app/browse-by/browse-by-routing.module.ts
#	src/app/browse-by/browse-by-switcher/browse-by-data-type.ts
#	src/app/browse-by/browse-by-switcher/browse-by-decorator.spec.ts
#	src/app/browse-by/browse-by-switcher/browse-by-decorator.ts
#	src/app/browse-by/browse-by-switcher/browse-by-switcher.component.spec.ts
#	src/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts
#	src/app/browse-by/browse-by-switcher/dynamic-component-loader.directive.ts
#	src/app/browse-by/browse-by-switcher/themed-browse-by-switcher.component.ts
#	src/app/browse-by/browse-by-taxonomy-page/themed-browse-by-taxonomy-page.component.ts
#	src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.spec.ts
#	src/app/browse-by/browse-by-taxonomy/browse-by-taxonomy.component.ts
#	src/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts
#	src/app/browse-by/browse-by-title-page/themed-browse-by-title-page.component.ts
#	src/app/browse-by/browse-by-title/browse-by-title.component.spec.ts
#	src/app/browse-by/browse-by.module.ts
#	src/app/collection-page/collection-page-routing.module.ts
#	src/app/collection-page/collection-page.component.ts
#	src/app/collection-page/collection-page.module.ts
#	src/app/community-page/community-page-routing.module.ts
#	src/app/community-page/community-page.module.ts
#	src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.spec.ts
#	src/app/community-page/sections/sub-com-col-section/sub-collection-list/community-page-sub-collection-list.component.ts
#	src/app/community-page/sections/sub-com-col-section/sub-collection-list/themed-community-page-sub-collection-list.component.ts
#	src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.spec.ts
#	src/app/community-page/sections/sub-com-col-section/sub-community-list/community-page-sub-community-list.component.ts
#	src/app/community-page/sections/sub-com-col-section/sub-community-list/themed-community-page-sub-community-list.component.ts
#	src/app/core/data/processes/process-data.service.ts
#	src/app/core/shared/non-hierarchical-browse-definition.ts
#	src/app/core/submission/vocabularies/vocabulary.data.service.ts
#	src/app/core/submission/workspaceitem-data.service.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.ts
#	src/app/dso-shared/dso-edit-metadata/dso-edit-metadata.component.ts
#	src/app/dso-shared/dso-shared.module.ts
#	src/app/footer/footer.component.spec.ts
#	src/app/header-nav-wrapper/header-navbar-wrapper.component.ts
#	src/app/header/header.component.ts
#	src/app/home-page/home-page.component.ts
#	src/app/home-page/home-page.module.ts
#	src/app/info/info-routing.module.ts
#	src/app/item-page/alerts/item-alerts.component.spec.ts
#	src/app/item-page/alerts/item-alerts.component.ts
#	src/app/item-page/field-components/metadata-values/metadata-values.component.ts
#	src/app/item-page/full/full-item-page.component.spec.ts
#	src/app/item-page/full/full-item-page.component.ts
#	src/app/item-page/item-shared.module.ts
#	src/app/item-page/simple/item-page.component.spec.ts
#	src/app/item-page/simple/item-page.component.ts
#	src/app/menu.resolver.spec.ts
#	src/app/my-dspace-page/my-dspace-page.module.ts
#	src/app/navbar/expandable-navbar-section/expandable-navbar-section.component.ts
#	src/app/navbar/navbar.component.spec.ts
#	src/app/notifications/notifications.module.ts
#	src/app/notifications/qa/events/quality-assurance-events.component.ts
#	src/app/notifications/qa/source/quality-assurance-source.component.ts
#	src/app/notifications/qa/topics/quality-assurance-topics.component.spec.ts
#	src/app/notifications/qa/topics/quality-assurance-topics.component.ts
#	src/app/process-page/detail/process-detail.component.spec.ts
#	src/app/process-page/detail/process-detail.component.ts
#	src/app/process-page/form/process-form.component.ts
#	src/app/process-page/overview/process-overview.component.spec.ts
#	src/app/process-page/overview/process-overview.component.ts
#	src/app/process-page/process-page-shared.module.ts
#	src/app/profile-page/profile-page.module.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.component.spec.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-events-page/quality-assurance-events-page.component.ts
#	src/app/quality-assurance-notifications-pages/quality-assurance-topics-page/quality-assurance-topics-page.component.ts
#	src/app/root/root.component.ts
#	src/app/search-page/configuration-search-page.component.ts
#	src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts
#	src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts
#	src/app/shared/comcol/comcol.module.ts
#	src/app/shared/dso-page/dso-edit-menu.resolver.spec.ts
#	src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts
#	src/app/shared/menu/menu-item/external-link-menu-item.component.ts
#	src/app/shared/menu/menu-item/link-menu-item.component.ts
#	src/app/shared/menu/menu-item/onclick-menu-item.component.ts
#	src/app/shared/menu/menu-item/text-menu-item.component.ts
#	src/app/shared/metadata-representation/metadata-representation-loader.component.spec.ts
#	src/app/shared/metadata-representation/metadata-representation-loader.component.ts
#	src/app/shared/metadata-representation/metadata-representation.directive.ts
#	src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.spec.ts
#	src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts
#	src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions.directive.ts
#	src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts
#	src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts
#	src/app/shared/object-collection/shared/listable-object/listable-object.directive.ts
#	src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts
#	src/app/shared/search/search-filters/search-filters.component.spec.ts
#	src/app/shared/search/search-filters/search-filters.component.ts
#	src/app/shared/search/search-settings/search-settings.component.spec.ts
#	src/app/shared/search/search.component.spec.ts
#	src/app/shared/search/search.component.ts
#	src/app/shared/search/themed-search.component.ts
#	src/app/shared/shared.module.ts
#	src/app/shared/starts-with/date/starts-with-date.component.spec.ts
#	src/app/shared/starts-with/date/starts-with-date.component.ts
#	src/app/shared/starts-with/text/starts-with-text.component.spec.ts
#	src/app/shared/testing/vocabulary-service.stub.ts
#	src/app/submission/form/submission-form.component.html
#	src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.spec.ts
#	src/app/submission/sections/upload/file/edit/section-upload-file-edit.component.ts
#	src/app/submission/sections/upload/section-upload.component.html
#	src/app/submission/sections/upload/section-upload.component.ts
#	src/app/submission/submission.module.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.spec.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-actions-loader/advanced-workflow-actions-loader.component.ts
#	src/app/workflowitems-edit-page/workflowitems-edit-page.module.ts
#	src/config/app-config.interface.ts
#	src/themes/custom/app/browse-by/browse-by-date-page/browse-by-date-page.component.ts
#	src/themes/custom/app/browse-by/browse-by-metadata-page/browse-by-metadata-page.component.ts
#	src/themes/custom/app/browse-by/browse-by-switcher/browse-by-switcher.component.ts
#	src/themes/custom/app/browse-by/browse-by-taxonomy-page/browse-by-taxonomy-page.component.ts
#	src/themes/custom/app/browse-by/browse-by-title-page/browse-by-title-page.component.ts
#	src/themes/custom/app/community-page/sub-collection-list/community-page-sub-collection-list.component.ts
#	src/themes/custom/app/community-page/sub-community-list/community-page-sub-community-list.component.ts
#	src/themes/custom/eager-theme.module.ts
#	src/themes/custom/lazy-theme.module.ts
#	src/themes/dspace/app/header-nav-wrapper/header-navbar-wrapper.component.ts
#	src/themes/dspace/app/header/header.component.ts
2024-03-04 15:04:33 +01:00
frabacche
add125790a Merge pull request #78 from 4Science/coar-notify-7-part-two-refactor-enqueue
ldn message: move enqueueretry from get to post
2024-03-04 13:25:32 +01:00
FrancescoMolinaro
1fd16a2d3e Merge remote-tracking branch 'origin/coar-notify-7-part-two' into coar-notify-7-part-two 2024-03-04 11:40:30 +01:00
FrancescoMolinaro
fd1483d537 restyle header 2024-03-04 11:40:06 +01:00
FrancescoMolinaro
c03ab07556 add route guard 2024-03-04 11:38:56 +01:00
FrancescoMolinaro
a79c4036cb fix menu resolver condition for LDN 2024-03-04 11:38:56 +01:00
FrancescoMolinaro
23cb4ca7b7 Add components descriptions, fix bug on navigation, align labels, add config example 2024-03-04 11:38:56 +01:00
FrancescoMolinaro
38ac31eafe add route guard 2024-03-04 11:37:27 +01:00
FrancescoMolinaro
f925273d90 fix menu resolver condition for LDN 2024-03-04 11:31:35 +01:00
FrancescoMolinaro
c27e758ffb Add components descriptions, fix bug on navigation, align labels, add config example 2024-03-04 11:29:02 +01:00
frabacche
4fdade892b ldn message: move enqueueretry from get to post 2024-03-04 11:17:15 +01:00
Kim Shepherd
e4a91e7261 [TLC-674] New duplicate data service, object reducer tests 2024-03-04 14:29:26 +13:00
Kim Shepherd
ca32314951 [TLC-674] Fix findDuplicates error in claimed task (old code)
Update spec tests
2024-03-04 13:31:48 +13:00
Kim Shepherd
2000b1b240 [TLC-674] Resolve sections type list problem (introduced in conflict merge) 2024-03-04 12:00:51 +13:00
Kim Shepherd
1206d61440 [TLC-674] Update duplicate data service to use searchBy method 2024-03-04 11:50:55 +13:00
Kim Shepherd
1e36a10dd5 [TLC-674] Properly test for existence of duplicates section 2024-03-04 11:50:55 +13:00
Kim Shepherd
e76b6c962c [TLC-674] Duplicate detection frontend changes as per feedback 2024-03-04 11:50:54 +13:00
Kim Shepherd
6229966f7a [TLC-674] Mock duplicateDataService.findDuplicates() 2024-03-04 11:50:15 +13:00
Kim Shepherd
b79dab3fcb [TLC-674] Mock duplicateDataService providers in spec tests 2024-03-04 11:50:15 +13:00
Kim Shepherd
0d42af38cd [TLC-674] Refactor Basic Duplicate detection endpoint / data service 2024-03-04 11:50:14 +13:00
Kim Shepherd
911cf8905c [TLC-674] Hide empty duplicate section UNLESS config overrides
A new config property allows the user to force the duplicate
section to be displayed even if there are no duplicates
as sometimes this is useful information to a reviewer or
submitter
2024-03-04 11:49:24 +13:00
Kim Shepherd
68dd35095c [TLC-674] Do not display duplicate section if data is empty 2024-03-04 11:49:24 +13:00
Kim Shepherd
a9a87d31ef [TLC-674] Allow for no duplicate section in submitted workspace item 2024-03-04 11:49:24 +13:00
Kim Shepherd
b4591b1372 [TLC-674] Potential duplicate msg shown as warning alert 2024-03-04 11:49:24 +13:00
Kim Shepherd
7a10834f82 [TLC-674] Dupe layout changes as per review
i18n fix
alignment fix
comment fix
2024-03-04 11:49:24 +13:00
Kim Shepherd
b8e2cac399 [TLC-674] Fix duplicate section unit test 2024-03-04 11:49:24 +13:00
Kim Shepherd
db8d47e598 [TLC-674] Refactor duplicates from item link to searchBy 2024-03-04 11:49:24 +13:00
Pascal-Nicolas Becker
140cb88510 Tiny code style fix in pool-search-result-list-element.component.ts 2024-03-04 11:49:24 +13:00
Kim Shepherd
e474862edf [TLC-674] Lint fixes 2024-03-04 11:49:24 +13:00
Kim Shepherd
aceffa0468 [TLC-674] TypeDoc 2024-03-04 11:49:24 +13:00
Kim Shepherd
d06d5b1b13 [TLC-674] Unit test fixes for comp, claimed/pooled tasks 2024-03-04 11:49:24 +13:00
Kim Shepherd
43620a44f2 [TLC-674] Lint fixes 2024-03-04 11:49:22 +13:00
Kim Shepherd
0e4ad5e674 [TLC-674] Tidy up components, WIP spec tests 2024-03-04 11:48:13 +13:00
Kim Shepherd
b672668e15 [TLC-674] Duplicate detection comp, template, i18n
Duplicate data is accessed in the submission section,
pooled tasks list and claimed tasks list.
2024-03-04 11:48:11 +13:00
dependabot[bot]
e7eaa9f61b Bump sanitize-html from 2.10.0 to 2.12.1
Bumps [sanitize-html](https://github.com/apostrophecms/sanitize-html) from 2.10.0 to 2.12.1.
- [Changelog](https://github.com/apostrophecms/sanitize-html/blob/main/CHANGELOG.md)
- [Commits](https://github.com/apostrophecms/sanitize-html/compare/2.10.0...2.12.1)

---
updated-dependencies:
- dependency-name: sanitize-html
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-01 17:25:29 +00:00
frabacche
0e4b220db1 Merge pull request #77 from 4Science/coar-notify-7-part-two-submission-patch-remove
coar-notify-7 item submission coar section send patch remove on blanking notify services
2024-03-01 17:12:01 +01:00
frabacche
c00134f372 coar-notify-7 item submission coar section send patch remove on blanking services 2024-03-01 14:57:57 +01:00
FrancescoMolinaro
b94167ad7b resolve issue #2842 2024-03-01 10:05:43 +01:00
FrancescoMolinaro
1f463f7329 Merge branch 'main' into coar-notify-7-part-two 2024-03-01 08:59:28 +01:00
Alexandre Vryghem
371e766078 Fixed tests without expectations from #2759 & #2681
Added the necessary done() calls back & fixed tests
2024-02-29 23:42:17 +01:00
Alexandre Vryghem
87788e7e02 Merge remote-tracking branch 'upstream/main' into fix-specs-without-expectations_contribute-main 2024-02-29 23:12:00 +01:00
Tim Donohue
061129ecb7 Merge pull request #2822 from DSpace/dependabot/npm_and_yarn/ip-2.0.1
Bump ip from 2.0.0 to 2.0.1
2024-02-29 15:26:14 -06:00
Tim Donohue
4fa5237b58 Merge pull request #2356 from mwoodiupui/node20
Update dependencies for Node v20
2024-02-29 15:10:35 -06:00
Tim Donohue
ae15fe5fa5 Merge pull request #2676 from 4Science/DURACOM-195
Header, navbar, and admin sidebar refactoring
2024-02-29 14:38:17 -06:00
Tim Donohue
25955aec8a Merge pull request #2840 from jeffmorin/main
Removed 250 and 1000 items/page options and set 10 as default value
2024-02-29 14:13:34 -06:00
Alexandre Vryghem
a340eb2a07 Removed broken tests
https://github.com/DSpace/dspace-angular/pull/2371#issuecomment-1637089839
2024-02-29 21:06:43 +01:00
Jean-François Morin
bb20b8d18a Cleaned obsolete code 2024-02-29 15:00:39 -05:00
jeffmorin
c5463eab8d Merge branch 'DSpace:main' into main 2024-02-29 13:16:12 -05:00
Davide Negretti
91f01ad542 Merge branch 'main' into DURACOM-195
# Conflicts:
#	src/assets/i18n/en.json5
#	src/themes/dspace/app/navbar/navbar.component.html
2024-02-29 19:11:03 +01:00
Tim Donohue
c195175fd7 Merge pull request #2681 from 4Science/coar-notify-7
Coar Notify Integration
2024-02-29 12:00:23 -06:00
FrancescoMolinaro
4fcb66022b fix lint, fix tests 2024-02-29 15:30:02 +01:00
FrancescoMolinaro
0a3f884793 Resolve conflicts after merge of part 1 2024-02-29 15:13:53 +01:00
FrancescoMolinaro
eab079124c Merge branch 'coar-notify-7' into coar-notify-7-part-two 2024-02-29 15:00:35 +01:00
Simone Ramundi
2660601dff Merged in DURACOM-238-update-ng2-file-upload (pull request #1413)
DURACOM-238 update ng2 file upload

Approved-by: Andrea Barbasso
2024-02-29 10:17:36 +00:00
Andrea Barbasso
6b538faf0d Merged DURACOM-191-align-to-angular-16 into DURACOM-238-update-ng2-file-upload 2024-02-29 10:16:04 +00:00
FrancescoMolinaro
cd6a00e48b resolve conflicts after alignment with main 2024-02-29 09:04:17 +01:00
FrancescoMolinaro
6fc4c05627 Merge branch 'main' into coar-notify-7 2024-02-29 08:56:49 +01:00
Jean-François Morin
0bc60a0187 Removed 250 and 1000 items/page options and set 10 as default value 2024-02-28 22:51:00 -05:00
Tim Donohue
ff05094008 Merge pull request #2163 from jeffmorin/main
Content reports ported from DSpace 6.x
2024-02-28 11:49:36 -06:00
FrancescoMolinaro
5eb2405a68 resolve conflicts after main alignment 2024-02-28 17:58:24 +01:00
FrancescoMolinaro
47a907c1d2 Merge branch 'main' into coar-notify-7 2024-02-28 15:41:07 +01:00
Jean-François Morin
410a278eca Resolved conflicts 2024-02-27 22:06:24 -05:00
Tim Donohue
0d63a851e1 Merge pull request #2759 from 4Science/CST-12109-WITHDRAWN-REINSTATE-requests
WITHDRAW / REINSTATE requests for an item
2024-02-27 16:07:43 -06:00
frabacche
aaf0ce24e0 coar-notify-7 item submission coar section show label if no inbound pattern are configured on LDN Notify Services 2024-02-27 20:59:56 +01:00
FrancescoMolinaro
6be10a81f4 fix section data load for coar section in submission 2024-02-27 18:19:34 +01:00
Jean-François Morin
26caab1741 Merge branch 'main' of github.com:jeffmorin/dspace-angular 2024-02-27 11:34:46 -05:00
Jean-François Morin
92553e04cd Requested changes and fixes 2024-02-27 11:34:37 -05:00
Alisa Ismailati
e4b28146ca Merge branch 'main' of https://bitbucket.org/4Science/dspace-angular into CST-12109-WITHDRAWN-REINSTATE-requests 2024-02-27 16:22:08 +01:00
Andrea Barbasso
576f262052 [DURACOM-238] add missing imports in standalone component 2024-02-27 16:04:39 +01:00
Andrea Barbasso
377b217e5d [DURACOM-238] add missing imports in standalone component 2024-02-27 16:03:25 +01:00
Andrea Barbasso
a2e3b08f27 [DURACOM-238] add missing imports in standalone component 2024-02-27 15:59:43 +01:00
Simone Ramundi
0ce10b489e Upgrade ng2-file-upload to v5.0.0 2024-02-27 15:03:51 +01:00
Davide Negretti
a88282d70b [DURACOM-195] fix spacing in collapsible menu 2024-02-27 14:05:48 +01:00
frabacche
3ed617eb69 NOtifyService item-filter patch fix 2024-02-27 12:10:18 +01:00
Alisa Ismailati
447b275387 [CST-12109] Updated notification text messages 2024-02-27 10:33:03 +01:00
frabacche
c1b22826c6 en double label 2024-02-27 09:39:59 +01:00
frabacche
153cbc1848 Merge remote-tracking branch 'origin/main' into coar-notify-7 2024-02-27 09:34:58 +01:00
jeffmorin
ba5f50e473 Merge branch 'DSpace:main' into main 2024-02-26 20:02:55 -05:00
gaurav patel
91a419fb0f Advance search search page (#2608)
* Update homepage-config.interface.ts

change comment of homepage-config.interface.ts

* advance Search add

* slove error while unti test

* write unit test

* Ensures select element has an accessible name

* change data pass into url

* error resolve

* Search.Filters.Applied.F.Title given name as Title

* Advanced filters configurable in the User interface (in config.*.yml)

* turn on/off and add filters as a list

* remove currenturl

* resolve

* change envierment config and url pass data

* set enabled: false ,and remove debugger ,remove searchConfig

* expect clauses  add

* reslove conflict

* merge added

* remove  commented and design change advance search

* moving the "add" button to its own col-lg-12

* resolve conflict

* reslove error

* merge en.json5 file

* remove trailing spaces

* resolve Conflicts

* Fix typo. property is filter not filters

---------

Co-authored-by: Tim Donohue <tim.donohue@lyrasis.org>
2024-02-26 14:11:28 -06:00
Davide Negretti
fdbe7a6005 [DURACOM-195] fix 2024-02-26 20:41:44 +01:00
Davide Negretti
d1dc8e6038 [DURACOM-195] replace ul/li with div in sidebar 2024-02-26 20:32:31 +01:00
Davide Negretti
8e35c0f713 [DURACOM-195] add background on mouse over to dropdown user menu items 2024-02-26 20:24:12 +01:00
Davide Negretti
bff93a0847 [DURACOM-195] fix issue with logo on small screens 2024-02-26 18:23:51 +01:00
Davide Negretti
abba806d40 [DURACOM-195] replace ul/li with div in navbar 2024-02-26 16:22:28 +01:00
FrancescoMolinaro
03ee758f63 update filter labels 2024-02-26 15:42:41 +01:00
frabacche
14976e0e06 fix Test errors, hide COAR requests with no LDN associable 2024-02-26 15:30:26 +01:00
frabacche
fbd5dd41ed Merge remote-tracking branch 'origin/main' into coar-notify-7 2024-02-26 14:40:17 +01:00
FrancescoMolinaro
2b57275e6d map constraints, add guard, small restyle (badge), remove unused patterns 2024-02-26 13:07:47 +01:00
Davide Negretti
1fb742c00a Merge branch 'main' into DURACOM-195
# Conflicts:
#	src/assets/i18n/en.json5
#	src/styles/_custom_variables.scss
#	src/themes/dspace/styles/_theme_css_variable_overrides.scss
2024-02-26 12:44:31 +01:00
Alisa Ismailati
d0ed40536d [CST-12109] added discernable text to delete button for non-admin users 2024-02-26 10:26:35 +01:00
Alexandre Vryghem
db710c59bb Fixed some components not destroying their ViewContainerRef 2024-02-24 14:20:35 +01:00
Alexandre Vryghem
109418bcdf Merge remote-tracking branch 'upstream/main' into fix-specs-without-expectations_contribute-main
# Conflicts:
#	src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts
2024-02-24 13:33:56 +01:00
Jean-François Morin
91d97f8af0 Fixed unit test 2024-02-23 17:12:38 -05:00
gaurav patel
bfeeeac96f only add Display Search Facets to the Homepage (#2275)
* Update homepage-config.interface.ts

change comment of homepage-config.interface.ts

* advance Search add

* slove error while unti test

* write unit test

* Ensures select element has an accessible name

* change data pass into url

* error resolve

* Search.Filters.Applied.F.Title given name as Title

* Advanced filters configurable in the User interface (in config.*.yml)

* Search Facets on all Home, Community, Collection

* should pass accessibility tests error resolve

* change label name

* unique role or role/label/title

* remove same role name

* order of headings is semantically correct

* semantically correct advance search and global css

* URL pattern

* headings is semantically correct

* Merge branch 'Search-Facets-home-community-collection' of https://github.com/GauravD2t/Advanced-search into Search-Facets-home-community-collection

* change update

* change update

* remove advance search code from here

* removing all code relating to Community/Collection pages

* resolve  code conflict

* remove the space

* remove commented code

* add  'merge'

* resolve confilt

* remove back file

* add lable dynamic

* Update search-navbar.component.spec.ts

* Update search-filter.component.html

* showDiscoverFilters config in yml file

* Ensures the order of headings is semantically correct

* showDiscoverFilters change default to false

* Update homepage-config.interface.ts to use boolean instead of false

---------

Co-authored-by: Tim Donohue <tim.donohue@lyrasis.org>
2024-02-23 16:01:36 -06:00
Jean-François Morin
600c99178a Fixed for loops over arrays 2024-02-23 16:56:52 -05:00
Jean-François Morin
1cea4690da Fixed styling errors 2024-02-23 16:49:48 -05:00
Jean-François Morin
e2bf9d2bcf Switched to GET requests for Content Reports 2024-02-23 16:34:29 -05:00
jeffmorin
04979919f7 Merge branch 'DSpace:main' into main 2024-02-23 16:20:48 -05:00
Tim Donohue
fc4f0ec6a3 Merge pull request #2653 from toniprieto/edit-item-authorities
Implement vocabulary value selectors in item’s metadata edit form
2024-02-23 15:04:45 -06:00
Tim Donohue
fc6da94827 Merge pull request #2756 from atmire/process-admin-ui-redesign-8.0.0-next
Split processes overview page into sections
2024-02-23 14:53:55 -06:00
Alisa Ismailati
49a3310273 [CST-12109] rewrite unit test case 2024-02-23 17:16:06 +01:00
Alisa Ismailati
37dc8ccd11 [CST-12109] logo alignment 2024-02-23 15:47:58 +01:00
Alisa Ismailati
e7579b7bf5 [CST-12109] fix of text messages / source image 2024-02-23 15:03:52 +01:00
Toni Prieto
1fc046249b Preserve the confidence value when loading the initial value of the model into the DsDynamicOneboxComponent. Previously, the accepted confidence value was always displayed if a valid vocabulary entry was retrieved, incorrectly displaying it for values without accepted authority. 2024-02-23 11:05:26 +01:00
Toni Prieto
7253a5917c Update the value with the searched text in the onBlur() event of DsDynamicOneBoxComponent when the user doesn't select any typeahead suggestion. The previous code attempted to maintain focus on the input field to not lose changes but it wasn't working. 2024-02-23 10:54:14 +01:00
Andreas Awouters
ff4b4a6881 Merge remote-tracking branch 'dspace/main' into process-admin-ui-redesign-8.0.0-next 2024-02-23 08:12:47 +01:00
Jean-François Morin
c7649505e2 Second attempt to fix menu.resolver.spec.ts 2024-02-22 18:42:32 -05:00
Jean-François Morin
1c6216d40a Revert to original version 2024-02-22 17:00:55 -05:00
Jean-François Morin
5f41bc275a The newly added ConfigurationDataService seemed to be missing in the unit test class (according to cryptic error message). 2024-02-22 16:38:02 -05:00
Jean-François Morin
f1cfe9991d Fixed single-quote constraint 2024-02-22 16:07:32 -05:00
Jean-François Morin
64e3149e7e Added enable/disable Conte Reports functionality 2024-02-22 15:49:41 -05:00
jeffmorin
513b28b3ef Merge branch 'DSpace:main' into main 2024-02-22 14:57:40 -05:00
Toni Prieto
b4a9a0dd1e Add a new mode to AuthorityConfidenceStateDirective to configure use fontawesome icons instead of class defined in style property 2024-02-22 20:29:53 +01:00
Toni Prieto
f3a64bcadc Implement vocabulary selector for new fields input 2024-02-22 20:29:53 +01:00
Toni Prieto
a5512f08e7 No display authority key if confidence is unset (-1) or no-value (0) 2024-02-22 20:29:52 +01:00
Toni Prieto
45eaa87f9c Implementation of controlled vocabularies value selectors during item editing 2024-02-22 20:29:52 +01:00
Toni Prieto
439739ab42 Add support for MetadataValue class to AuthorityConfidenceStateDirective 2024-02-22 20:29:52 +01:00
Toni Prieto
c596c6eb14 Add aria-label and loadingInitialValue property to DsDynamicOneboxComponent 2024-02-22 20:29:52 +01:00
Toni Prieto
8d25ba764c Implement getVocabularyByMetadataAndCollection in VocabularyService 2024-02-22 20:29:52 +01:00
frabacche
c3d5b4cbe5 remove coar-notify.enable config, use ldn.enabled instead 2024-02-22 20:13:52 +01:00
Mark H. Wood
44cb4ec9c9 Address merge conflict. 2024-02-22 13:11:09 -05:00
frabacche
e4a03404ad LDN Service link always visible to admin 2024-02-22 18:53:54 +01:00
Mark H. Wood
82267fe2d2 Merge remote-tracking branch 'upstream/main' into node20 2024-02-22 12:46:53 -05:00
Mark H. Wood
5e8a767c1f Update locked versions. 2024-02-22 12:42:18 -05:00
frabacche
1f80d7f456 qa-coar-notify-logo.png file 2024-02-22 18:22:25 +01:00
frabacche
57b777b42a fix suggestion.service.ts main merge 2024-02-22 17:45:51 +01:00
Tim Donohue
45650c1a2a Merge pull request #2480 from atmire/feature-process_polling
Feature process polling
2024-02-22 10:43:14 -06:00
frabacche
fbab8ef70e Merge remote-tracking branch 'origin/main' into coar-notify-7 2024-02-22 17:33:31 +01:00
jeffmorin
79993ce502 Merge branch 'DSpace:main' into main 2024-02-22 11:19:55 -05:00
Alisa Ismailati
6c73a2e3a8 Merge branch 'main' into CST-12109-WITHDRAWN-REINSTATE-requests 2024-02-22 17:19:08 +01:00
frabacche
58c8dd6516 Merge remote-tracking branch 'github/main' into coar-notify-7 2024-02-22 17:15:29 +01:00
frabacche
7e11b74eed coar-notify-7 remove useless files 2024-02-22 17:11:03 +01:00
Tim Donohue
1c7f098ddd Merge pull request #2830 from 4Science/fix-issue-9355
[issue-9355] fix error on e2e tests
2024-02-22 10:09:13 -06:00
FrancescoMolinaro
10e8768fc2 [issue-9355] fix error on e2e tests 2024-02-22 16:25:47 +01:00
Sascha Szott
d5aca7633c site search should not adopt existing URL query string parameters (#2818)
src/app/search-navbar: do not merge existing query params

Do not merge existing query params when using site search from MyDSpace workflow.
2024-02-22 11:10:02 +03:00
frabacche
97dfe7998b Merge branch 'coar-notify-7' of bitbucket.org:4Science/dspace-angular into coar-notify-7 2024-02-22 08:55:43 +01:00
frabacche
ff1444b241 coar-notify-7 LDN Service menu show/hide configuration 2024-02-22 08:55:23 +01:00
jeffmorin
2a65bd81b3 Merge branch 'DSpace:main' into main 2024-02-21 14:26:33 -05:00
Jean-François Morin
27a9ce5280 Merge branch 'main' of github.com:jeffmorin/dspace-angular 2024-02-21 14:24:03 -05:00
Jean-François Morin
8bcd82e855 Forgot styling on two buttons 2024-02-21 14:23:50 -05:00
Tim Donohue
32bac12bbd Merge pull request #2732 from alexandrevryghem/added-recently-added-section-to-community-page_contribute-main
Added search facets to community & collection pages
2024-02-21 12:17:09 -06:00
Alisa Ismailati
c34a49e55a [CST-12109] lint fix 2024-02-21 18:11:30 +01:00
Alisa Ismailati
7350fdd125 [CST-12109] qa-event notification box fixes 2024-02-21 18:06:45 +01:00
Tim Donohue
d61d688848 Merge pull request #2820 from philipprumpf/multiple-import
Add translation for DOI multiple parallel import source.
2024-02-21 09:47:35 -06:00
FrancescoMolinaro
a64e4c9f71 remove duplicated log, unused translations, refactor ldn discard button 2024-02-21 11:55:42 +01:00
Alisa Ismailati
8d0971d9ff Merge branch 'main' into CST-12109-WITHDRAWN-REINSTATE-requests 2024-02-21 10:14:50 +01:00
Andreas Awouters
adea818306 Merge remote-tracking branch 'dspace/main' into process-admin-ui-redesign-8.0.0-next 2024-02-21 08:51:41 +01:00
Andreas Awouters
b1d9b4bd62 Merge remote-tracking branch 'atmire/feature-process_polling' into process-admin-ui-redesign-8.0.0-next 2024-02-21 08:51:13 +01:00
Andreas Awouters
5ef168b57b Merge branch 'w2p-111638_Process-admin-UI-redesign_Overview-page-tables_PR-feedback' into process-admin-ui-redesign-8.0.0-next 2024-02-21 08:50:25 +01:00
Art Lowel
f887997fb3 update tests 2024-02-21 08:13:51 +01:00
Andreas Awouters
960bfbbfe5 111638: Fix autoRefreshingSearchBy test 2024-02-21 08:13:51 +01:00
Andreas Awouters
fa2f3e63e7 111638: Clean up autoRefreshing calls 2024-02-21 08:13:51 +01:00
Andreas Awouters
08299e5c78 111638: Only collapse tables on initial page open 2024-02-21 08:13:51 +01:00
Tim Donohue
dcf583670f Merge pull request #2631 from vNovski/CST-12044-visualize-the-primary-bitstream
CST-12044 visualize the primary bitstream & CST-12043 primary bitstream flag
2024-02-20 17:09:45 -06:00
jeffmorin
2d2a74a408 Merge branch 'DSpace:main' into main 2024-02-20 15:59:16 -05:00
dependabot[bot]
c147c7d6ea Bump ip from 2.0.0 to 2.0.1
Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
- [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: ip
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-20 20:49:23 +00:00
Tim Donohue
daf6dfeeeb Merge pull request #2733 from atmire/w2p-110088_keyboard-to-select-values
Use keyboard to select values
2024-02-20 09:38:30 -06:00
Philipp Rumpf
22919eb7fd Add translation for DOI multiple parallel import source.
Goes with DSpace#9132.
2024-02-20 14:54:12 +00:00
Jean-François Morin
998dd94fd4 Merge branch 'main' of github.com:jeffmorin/dspace-angular 2024-02-19 15:48:59 -05:00
Jean-François Morin
cadd1df8bf Applied Bootstrap styles to tables and form controls 2024-02-19 15:48:39 -05:00
Alexandre Vryghem
049fbb8778 Merge branch 'feature-process_polling-7.6' into feature-process_polling 2024-02-19 18:57:38 +01:00
Alexandre Vryghem
8ddc62168e 108915: Prevent self links & unresolved followLinks to be added as dependencies 2024-02-19 18:56:43 +01:00
FrancescoMolinaro
9336e793de FIx issues from main alignment, remove leftover removed in Suggestion PR 2024-02-19 12:34:48 +01:00
Jens Vannerum
f7f86b0a9e 111768: move original code back to init only for performance 2024-02-19 11:42:57 +01:00
Andreas Awouters
ab479251ad 111638: Fix lint errors & small typo 2024-02-19 08:54:55 +01:00
Alexandre Vryghem
bba6971440 Fixed new failing tests 2024-02-18 21:42:49 +01:00
Alexandre Vryghem
b57712094d Merge remote-tracking branch 'upstream/main' into fix-specs-without-expectations_contribute-main 2024-02-18 21:23:03 +01:00
Alexandre Vryghem
629c06cc16 Merge branch 'added-recently-added-section-to-community-page_contribute-7.6' into added-recently-added-section-to-community-page_contribute-main 2024-02-18 18:50:44 +01:00
Alexandre Vryghem
7968f68976 110889: Fixed trackStatistics still sending backend requests to track search event when trackStatistics is false 2024-02-18 18:49:55 +01:00
Alexandre Vryghem
5590ef46bb Merge branch 'added-recently-added-section-to-community-page_contribute-7.6' into added-recently-added-section-to-community-page_contribute-main 2024-02-18 17:54:28 +01:00
Alexandre Vryghem
37c534b809 110889: Don't retrieve the accessStatus when item.showAccessStatuses is false 2024-02-18 17:54:01 +01:00
Alexandre Vryghem
9ff3ed4351 Merge remote-tracking branch 'upstream/main' into added-recently-added-section-to-community-page_contribute-main
# Conflicts:
#	config/config.example.yml
#	src/app/shared/search-form/search-form.component.ts
#	src/assets/i18n/en.json5
#	src/config/default-app-config.ts
2024-02-18 15:32:39 +01:00
Alexandre Vryghem
877c2d4eaa 110889: Added discovery configuration to community & collection search sections 2024-02-18 15:13:09 +01:00
Alexandre Vryghem
9e7a59ddd0 110889: Prevent vocabulary undefined from being retrieved in hierarchy filter when none is configured 2024-02-18 11:58:04 +01:00
Alexandre Vryghem
cbe3f2f8f5 110889: Fixed searchevents request containing the query params in the clickedObject
Also fixed minor CSS issue in mobile mode where sidebar is still visible
2024-02-18 11:55:23 +01:00
Alexandre Vryghem
7bf7ee0ba7 110889: Added config to disable community/collection sidebar 2024-02-18 02:00:51 +01:00
Alexandre Vryghem
0bee02a501 110889: Remove the scope query parameter from the search on comcol pages 2024-02-18 00:32:53 +01:00
Alexandre Vryghem
0e2630d729 110889: Added search form to the community & collection pages 2024-02-18 00:32:52 +01:00
Francesco Molinaro
f4f48c8bb2 Merged in align-coar-notify-7-with-main (pull request #1366)
Align coar notify 7 with main
2024-02-16 14:53:39 +00:00
FrancescoMolinaro
251729e869 fix empty result js error 2024-02-16 15:18:42 +01:00
jeffmorin
872a670f02 Merge branch 'DSpace:main' into main 2024-02-16 08:47:39 -05:00
FrancescoMolinaro
6380abc759 Fix merge conflicts 2024-02-16 13:10:38 +01:00
FrancescoMolinaro
a249cf5986 Merge branch 'main' into coar-notify-7 2024-02-16 11:00:24 +01:00
Tim Donohue
055122918f Merge pull request #2770 from saschaszott/patch-13
German translations of feedback page translation keys
2024-02-15 16:11:22 -06:00
Jean-François Morin
85630ba5ea Fixed i18n files 2024-02-15 16:14:17 -05:00
Jean-François Morin
4cd20d6dcd Removed unneeded actions file and fixed invalid TS file 2024-02-15 15:49:25 -05:00
Tim Donohue
4c3d374e9c Merge pull request #2794 from alexandrevryghem/fix-scope-issues-on-search-form_contribute-main
Added scope @Input() to the SearchComponent & fixed related bugs
2024-02-15 14:37:46 -06:00
Jean-François Morin
5f88a07673 Removed non-working tests 2024-02-15 15:34:02 -05:00
Alexandre Vryghem
e23c1f7b5d 110889: Replaced recently added component with search comcol section 2024-02-15 21:27:59 +01:00
Tim Donohue
5c3f51b9fb Merge pull request #2647 from mspalti/access-conditions
File edit component updated to work for forms without access conditions.
2024-02-15 14:00:28 -06:00
Alexandre Vryghem
bae48007b3 Merge remote-tracking branch 'alex/fix-scope-issues-on-search-form_contribute-7.6' into added-recently-added-section-to-community-page_contribute-7.6 2024-02-15 19:57:31 +01:00
Vlad Nouski
02baf86831 [CST-12044] removed aria hidden attribute 2024-02-15 17:32:46 +01:00
Tim Donohue
eb433c8727 Merge pull request #1638 from 4Science/CST-5249_suggestion
Openaire suggestions (publication claim)
2024-02-15 10:24:31 -06:00
FrancescoMolinaro
75f5462a5b [CST-5249] fix ssr rendering 2024-02-15 16:10:13 +01:00
jeffmorin
d686cb1909 Merge branch 'DSpace:main' into main 2024-02-15 09:00:53 -05:00
Tim Donohue
e1c639edcd Merge pull request #2719 from 4Science/main_CST-12825
ROR Integration - Identifier Visualization
2024-02-14 15:16:32 -06:00
Davide Negretti
523850bc45 [DURACOM-195] fixes 2024-02-14 12:05:55 +01:00
FrancescoMolinaro
2b3339748d fix conflicts with main 2024-02-14 11:20:09 +01:00
Andrea Barbasso
5ff253e581 remove mathjax (it uses the xmldom-sre library, that has a documentElement.setAttribute error) 2024-02-14 11:16:33 +01:00
FrancescoMolinaro
47f1b843e4 Merge branch 'main' into coar-notify-7 2024-02-14 11:14:14 +01:00
FrancescoMolinaro
f475a4f516 [CST-5249] fix deletion propagation 2024-02-14 11:08:06 +01:00
Andrea Barbasso
387f5ee26f update missing dependencies 2024-02-14 10:57:27 +01:00
Jens Vannerum
9767260b9a 111768: add back necessary setCurrentValue calls that were removed earlier 2024-02-14 10:41:24 +01:00
Francesco Molinaro
824cdfaf37 Merged in CST-13526-untrusted-ip-mapping (pull request #1312)
CST-13526 untrusted ip mapping

Approved-by: Stefano Maffei
2024-02-14 09:14:33 +00:00
Jens Vannerum
5c84ede728 Merge remote-tracking branch 'dspaceMain/main' into w2p-110088_keyboard-to-select-values 2024-02-14 09:42:54 +01:00
Davide Negretti
5b096deb36 Merge branch 'main' into DURACOM-195
# Conflicts:
#	src/app/shared/log-in/methods/password/log-in-password.component.html
2024-02-13 22:12:54 +01:00
Alexandre Vryghem
21cc311d2a Merge remote-tracking branch 'upstream/main' into feature-process_polling 2024-02-13 21:28:51 +01:00
Tim Donohue
c75e6e29e8 Merge pull request #2722 from alexandrevryghem/refactor-comcol-pages_contribute-main
Refactored community & collection pages
2024-02-13 14:22:20 -06:00
Tim Donohue
56b38f621e Merge pull request #2624 from 4Science/DURACOM-204
[#2622] Makes forgot-password link removable
2024-02-13 14:19:22 -06:00
Vincenzo Mecca
678d0896c5 [#2719][CST-12825] Refactors item-page-img-field component 2024-02-13 18:21:51 +01:00
Vincenzo Mecca
26bfc58278 [#2719][CST-12825] Refactors item-page-img-field component 2024-02-13 18:08:31 +01:00
Andrea Barbasso
53e8b69aab remove or update libraries 2024-02-13 16:59:22 +01:00
FrancescoMolinaro
5d85e511a1 [CST-5249] fix tests 2024-02-13 16:13:47 +01:00
Andrea Barbasso
bc29293411 change TransferState import 2024-02-13 14:14:34 +01:00
Andrea Barbasso
3794007e9e do not use componentFactories anymore 2024-02-13 14:10:54 +01:00
Vincenzo Mecca
0e4151efa8 [#2624][DURACOM-204] Created new guard for forgot route 2024-02-13 13:43:14 +01:00
FrancescoMolinaro
62dc53d006 Fix merge conflicts with main 2024-02-13 13:04:12 +01:00
FrancescoMolinaro
c09a6e819f Merge branch 'main' into coar-notify-7 2024-02-13 12:52:33 +01:00
FrancescoMolinaro
8dc7eeb242 [CST-5249] fix suggestion refresh after ignore 2024-02-13 12:37:29 +01:00
Art Lowel
cb6ed39219 ensure all subscriptions are tracked and cleaned up 2024-02-13 11:48:47 +01:00
Andrea Barbasso
6cdb3d1eb2 provide APP_ID instead of BrowserModule.withServerTransition, remove BrowserModule import 2024-02-13 11:21:04 +01:00
Vincenzo Mecca
ca3749be4a Merge remote-tracking branch 'github/main' into main_CST-12825
# Conflicts:
#	src/assets/i18n/en.json5
#	src/styles/_custom_variables.scss
2024-02-13 11:20:26 +01:00
Andrea Barbasso
4e6d90b501 import renderModule instead of renderModuleFactory 2024-02-13 11:18:17 +01:00
Vincenzo Mecca
5a970775ae [#2719][CST-12825] Extracts new image-page-field component with css variable 2024-02-13 11:16:54 +01:00
jeffmorin
16708d6bbb Merge branch 'DSpace:main' into main 2024-02-12 13:17:37 -05:00
Andrea Barbasso
2195ca415b update custom-webpack 2024-02-12 17:02:00 +01:00
Vincenzo Mecca
8f488220fb [#2719][CST-12825] Adds i18n missing keys 2024-02-12 16:56:06 +01:00
Andrea Barbasso
9b213b6381 run ng update @angular/core@16 @angular/cli@16 --force 2024-02-12 16:22:22 +01:00
Andrea Barbasso
a98039c687 update typescript 2024-02-12 16:09:43 +01:00
Alisa Ismailati
d72610f562 Merge branch 'main' into CST-12109-WITHDRAWN-REINSTATE-requests 2024-02-12 12:01:28 +01:00
Alexandre Vryghem
193d56d422 Merge branch 'refactor-comcol-pages_contribute-7.6' into refactor-comcol-pages_contribute-main 2024-02-09 23:25:49 +01:00
Tim Donohue
645024d6f0 Merge pull request #2339 from alexandrevryghem/generify-component-loaders_contribute-main
Created abstract component loader class
2024-02-09 15:13:39 -06:00
Tim Donohue
cb7c2c9f0b Merge pull request #2373 from alexandrevryghem/w2p-102415_Remote-lookup-entity-on-admin-edit_contribute-main
Added support to import from external source on Edit Item > Relationships
2024-02-09 12:16:53 -06:00
FrancescoMolinaro
c860faa452 Align translations 2024-02-09 13:37:13 +01:00
FrancescoMolinaro
3561063af7 Merge branch 'main' into coar-notify-7 2024-02-09 13:18:59 +01:00
FrancescoMolinaro
5b7ca7cfa5 CST-5249 fix lint 2024-02-09 09:32:03 +01:00
FrancescoMolinaro
0ee6ee8ea1 CST-5249 resolve conflicts 2024-02-09 09:24:58 +01:00
FrancescoMolinaro
f6eac0936b Merge branch 'main' into CST-5249_suggestion 2024-02-09 09:17:59 +01:00
Alexandre Vryghem
7f842f10a5 Merge branch 'w2p-102415_Remote-lookup-entity-on-admin-edit_contribute-7.4' into w2p-102415_Remote-lookup-entity-on-admin-edit_contribute-7.6
# Conflicts:
#	src/assets/i18n/en.json5
2024-02-08 18:15:14 +01:00
Alexandre Vryghem
bb5ee5f512 Merge branch 'w2p-102415_Remote-lookup-entity-on-admin-edit_contribute-7.2' into w2p-102415_Remote-lookup-entity-on-admin-edit_contribute-7.4 2024-02-08 18:14:38 +01:00
Alexandre Vryghem
f7dbdb37ea 102415: Added additional i18n keys for the relationship modal 2024-02-08 18:12:12 +01:00
Alexandre Vryghem
110633c692 Merge branch 'feature-process_polling-7.6' into feature-process_polling 2024-02-08 16:24:22 +01:00
Alexandre Vryghem
ecb20bbcbf 108915: Fixed Proxy Timout error when retrieving a non-existing process 2024-02-08 16:16:06 +01:00
Alexandre Vryghem
252b3673ee 108915: Fixed issue that redirected you to 404 page when deleting the process on the process page 2024-02-08 16:14:38 +01:00
Andreas Awouters
4e195b61b6 111638: Fix table collapsing when it has no processes 2024-02-08 13:31:13 +01:00
Andreas Awouters
c5b5a3b18b 111638: Remove 'container' class from processes table 2024-02-08 13:31:06 +01:00
Andreas Awouters
024fd17244 111638: Fix table collapsing when it has no processes 2024-02-08 13:28:04 +01:00
Andreas Awouters
244a7db868 111638: Remove 'container' class from processes table 2024-02-08 13:22:59 +01:00
Tim Donohue
44e84f8912 Merge pull request #2705 from ilri/i18n-fixes
Minor updates to i18n strings for clarity and consistency
2024-02-07 11:22:48 -06:00
Tim Donohue
a8000937bc Merge pull request #2766 from amgciadev/fix-2765-b
Fix missing Register DOI button if configuration enabled
2024-02-07 10:57:04 -06:00
Tim Donohue
e35fed1be4 Merge pull request #2799 from DSpace/dependabot/npm_and_yarn/follow-redirects-1.15.5
Bump follow-redirects from 1.15.3 to 1.15.5
2024-02-07 10:34:08 -06:00
Andreas Awouters
27d05aea95 Merge branch 'w2p-111638_Process-admin-UI-redesign_Overview-page-tables_PR-feedback' into process-admin-ui-redesign-8.0.0-next 2024-02-07 11:18:28 +01:00
Vlad Nouski
c596b6c7ec [CST-12044] refactor: code 2024-02-07 08:31:06 +01:00
Alexandre Vryghem
6e29f306a7 108588: Fixed browse by date tab's year dropdown always being empty
- The data passed to the injector in BrowseByComponent was not updated by ngOnChanges
- Also refactored the injector logic to StartsWithLoaderComponent
2024-02-07 00:44:18 +01:00
Alexandre Vryghem
6f51bd866c 108588: Fixed browse by issue date show loading icon indefinitely when empty 2024-02-07 00:44:18 +01:00
Alexandre Vryghem
b240b2c160 108588: Fixed scope not being set when browsing by taxonomy on community/collection pages 2024-02-07 00:44:18 +01:00
Alexandre Vryghem
ca5fc8217e 108588: Fix header ordering issues on community & collection pages 2024-02-07 00:44:18 +01:00
Alexandre Vryghem
d95f9d0140 108588: Passed the scope to the browse sections 2024-02-07 00:44:18 +01:00
Alexandre Vryghem
d9759d1136 108588: Renamed components, because now these aren't page components anymore 2024-02-07 00:44:17 +01:00
Alexandre Vryghem
fb2c7cee9d 108588: Created separate section for communities & collections browse sections 2024-02-07 00:44:17 +01:00
Alexandre Vryghem
a695784fc8 108588: Removed the community & collection parts from the browse sections 2024-02-07 00:44:17 +01:00
Alexandre Vryghem
12478cf7e2 108588: Updated browse by switcher to work more like the new AbstractLoaderComponent 2024-02-07 00:44:17 +01:00
Alexandre Vryghem
15342b3903 108588: Updated CollectionPageRoutingModule to use custom sections 2024-02-07 00:44:17 +01:00
Alexandre Vryghem
e66a14cffd 108588: Fixed accessibility issues on ds-comcol-page-browse-by 2024-02-07 00:44:17 +01:00
Alexandre Vryghem
2c543ad570 108588: Updated CommunityPageRoutingModule to use custom sections 2024-02-07 00:22:14 +01:00
Alexandre Vryghem
bb0b66f927 108588: Moved sub communities & collections from community page to new component 2024-02-07 00:22:14 +01:00
Alexandre Vryghem
0483612819 108588: Moved CommunityPageSubCommunityListComponent & CommunityPageSubCollectionListComponent to a custom sections folder 2024-02-07 00:22:14 +01:00
Kim Shepherd
68475d1ed6 Improve cfg handling in item-status.component.ts 2024-02-07 12:09:16 +13:00
Kim Shepherd
3e33785311 Improve cfg handling in item-status.component.ts 2024-02-07 11:55:44 +13:00
Alexandre Vryghem
4602cdde1a Merge branch 'memory-leak-fixes_contribute-7.6' into generify-component-loaders_contribute-main 2024-02-06 23:33:49 +01:00
dependabot[bot]
90a277fbeb Bump follow-redirects from 1.15.3 to 1.15.5
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.5.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.5)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-06 22:29:37 +00:00
Tim Donohue
0d09bbe853 Merge pull request #2795 from saschaszott/patch-16
fix German translation of admin.workflow.item.workflow
2024-02-06 16:02:56 -06:00
Alexandre Vryghem
82d4ccfb29 Merge branch 'generify-component-loaders_contribute-7.6' into generify-component-loaders_contribute-main 2024-02-06 22:37:16 +01:00
Alexandre Vryghem
2fb393db7f Removed the AbstractBrowseByTypeComponent 2024-02-06 21:45:22 +01:00
Vlad Nouski
18e62f4706 [CST-12044] fix accept/reject the license bug 2024-02-06 14:57:03 +01:00
Andreas Awouters
4651d3a0d0 111638: Fix typeError when useAutoRefreshing is set to false 2024-02-06 14:21:40 +01:00
Andreas Awouters
ca18d5ff90 111638: Highlight newly created processes 2024-02-06 14:21:40 +01:00
Andreas Awouters
4da63cc30e 111638: Serialize processId as string to match type
processId was originally of type 'number' even though it has 'string'
as type annotation. With this change it actually is a string.
2024-02-06 14:21:40 +01:00
Andreas Awouters
8d473eaead 111638: Show process ID instead of status 2024-02-06 14:21:40 +01:00
Andreas Awouters
14294e20ee 111638: Disable autorefreshing on non-browser platforms 2024-02-06 14:21:40 +01:00
Andreas Awouters
545b17fc9e 111638: Clean up process-overview component
Removed all unused code & reduced the tests to the minimum necessary
without removing testcases.
2024-02-06 14:21:40 +01:00
Andreas Awouters
8e11777e5b 111638: Change completed processes header to 'Succeeded processes' 2024-02-06 14:21:40 +01:00
Andreas Awouters
4af303d0f2 111638: Use hasValue instead of nullCheck 2024-02-06 14:21:40 +01:00
Andreas Awouters
72c04cfc77 111638: Sort processes with creationTime as default field 2024-02-06 14:21:40 +01:00
Andreas Awouters
5569d7fd32 111638: Use creation time for scheduled processes 2024-02-06 14:21:40 +01:00
Andreas Awouters
d64f521f0c 111638: Add creationTime to process model 2024-02-06 14:21:40 +01:00
Andreas Awouters
e96a7582f4 111638: Duplicate buttons at bottom of page 2024-02-06 14:21:40 +01:00
Andreas Awouters
e25ce44a47 111638: Collapse table when it contains no processes 2024-02-06 14:21:40 +01:00
Vlad Nouski
b86525990d [CST-12044] refactor: code 2024-02-06 13:23:02 +01:00
Alisa Ismailati
f916bd7776 [CST-12109] fixes 2024-02-05 19:37:08 +01:00
Tim Donohue
c67e90b808 Merge pull request #2791 from tdonohue/fix_docker_issues
Fix issue with cli.yml not using same network as backend.
2024-02-05 11:12:43 -06:00
FrancescoMolinaro
71e3d40c79 fix breadcrumb test 2024-02-05 17:49:49 +01:00
FrancescoMolinaro
044ec06cb1 update breadcrumb label 2024-02-05 16:53:21 +01:00
Tim Donohue
be0a6c71ef Resync docker-compose-ci and docker/docker-compose-rest with DSpace/DSpace docker scripts 2024-02-05 08:52:46 -06:00
Tim Donohue
9043f63cd2 Bug fix to GitHub CI build. Set compose project name via env variable. Also add default name if unspecified 2024-02-05 08:50:43 -06:00
FrancescoMolinaro
47c3d3cf66 CST-13526 update filter label 2024-02-05 15:43:47 +01:00
Andreas Awouters
1c73a0c509 111638: Improve accessibility 2024-02-05 15:10:53 +01:00
FrancescoMolinaro
020103fbd7 Merge branch 'coar-notify-7-part-two' into CST-13526-untrusted-ip-mapping 2024-02-05 15:04:32 +01:00
FrancescoMolinaro
c2a29924ff CST-13526 update status mapping 2024-02-05 15:04:00 +01:00
FrancescoMolinaro
06cc944f2b fix test 2024-02-05 13:00:08 +01:00
FrancescoMolinaro
937687c414 fix routing, update issue, adapt labels 2024-02-05 12:48:23 +01:00
Sascha Szott
ac5670a514 remove translation (use singular form instead of plural form) 2024-02-05 12:06:11 +01:00
Alexandre Vryghem
239f082cce Moved the context @Input() to the loaders (since they are not always required for all the loaders) & added AbstractComponentLoaderComponent documentation 2024-02-03 19:10:46 +01:00
Alexandre Vryghem
2433082ea7 Merge remote-tracking branch 'upstream/main' into fix-scope-issues-on-search-form_contribute-main
# Conflicts:
#	src/app/shared/search/themed-search.component.ts
2024-02-03 15:23:15 +01:00
Alexandre Vryghem
93b22cba1f 111639: Fixed search settings not using the scope 2024-02-03 14:51:35 +01:00
Alexandre Vryghem
22e87a5f5a 111639: Added scope @Input() and made the search facets & search results use that scope 2024-02-03 14:51:34 +01:00
Tim Donohue
2c2f59c7ef Update cli.yml to reference dspacenet in docker-compose-rest. Remove unnecessary network from cli.assetstore.yml 2024-02-02 14:27:33 -06:00
Davide Negretti
46fe7f63e3 [DURACOM-195] fix prefix of custom CSS variables 2024-02-02 14:01:42 +01:00
Davide Negretti
87d3383bba [DURACOM-195] misc fixes 2024-02-02 14:01:42 +01:00
Vincenzo Mecca
833033dac0 Merge branch 'main' into DURACOM-204
# Conflicts:
#	src/app/core/data/feature-authorization/feature-id.ts
2024-02-02 11:26:26 +01:00
Davide Negretti
6d82c77c00 Merge branch 'main' into DURACOM-195 2024-02-02 10:41:47 +01:00
Andrea Barbasso
9ead6f5b33 [DURACOM-191] fix missing imports 2024-02-02 10:12:29 +01:00
Alan Orth
5a42f391cf Merge pull request #2778 from TAMULib/dedupe-patch-operations
Dedupe patch operations by op and path
2024-02-01 22:00:35 +03:00
Andrea Barbasso
16bd9b17ac [DURACOM-191] fix tests 2024-02-01 17:52:19 +01:00
Andrea Barbasso
dc8d3ace09 Merge branch 'main' into DURACOM-191
# Conflicts:
#	src/app/community-list-page/community-list/community-list.component.ts
#	src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-suggestions/org-unit-input-suggestions.component.spec.ts
#	src/app/forgot-password/forgot-password-form/forgot-password-form.component.spec.ts
2024-02-01 17:27:00 +01:00
FrancescoMolinaro
2da613bff1 clean up 2024-02-01 11:31:26 +01:00
Tim Donohue
85369aac10 Merge pull request #2717 from alexandrevryghem/fix-accessibility-issues_contribute-main
Fix accessibility issues (part 2)
2024-01-31 09:34:58 -06:00
Alexandre Vryghem
ca24713d6d Fixed dropdown accessibility issue
- Removed non-existing dropdownMenuButton references
- Added appropriate roles to dropdown menus
2024-01-30 23:24:29 +01:00
Alexandre Vryghem
9e29cfb68d Fixed user authentication/registration accessibility issue
- Added the correct autocomplete value
- Removed dangling labels, because aria labels already describe those input fields & we can't use ids in this component because otherwise there are duplicate ids on the /login page
2024-01-30 23:24:29 +01:00
Alexandre Vryghem
e36bf645f4 Fixed edit metadata page accessibility issue
- Hide fontawesome icons for screen readers
- Added missing aria-label to metadata field input
2024-01-30 23:24:29 +01:00
Alexandre Vryghem
8ad1b4b0d4 Fixed feedback form page accessibility issue
- The form used a fieldset tag instead of a form tag
2024-01-30 23:24:28 +01:00
Alexandre Vryghem
7f0264ed1c Fixed remaining community/collection page accessibility issues
- Hide fontawesome icons for screen readers
- Replaced dangling labels with spans and added more descriptive aria label to browse link
- Fixed empty label/missing header in edit resource policy table
- Added missing comcol logo al text
- Refactored dso-edit-menu-section to not display empty link buttons and duplicate titles
2024-01-30 23:24:28 +01:00
Alexandre Vryghem
07e89acab5 Fixed accessibility issues on AccessControlFormContainerComponent
- Replaced invisible label in AccessControlArrayFormComponent that was only used for styling with span
- Fixed radio buttons not being part of a fieldset
- Fixed empty table header
2024-01-30 23:24:28 +01:00
Alexandre Vryghem
c30a2eea45 Fixed accessibility issues on BitstreamFormatsComponent
- Removed empty label of the select box and replaced it with an aria-label
- Fixed empty table header
2024-01-30 23:24:28 +01:00
Alexandre Vryghem
54ef97d607 Fixed accessibility issues on MetadataRegistryComponent
- Removed empty label of the select box and replaced it with an aria-label
- Fixed empty table header
2024-01-30 23:24:28 +01:00
Alexandre Vryghem
8ab4f1c074 Fixed accessibility issues on MetadataSchemaComponent
- Removed empty label of the select box and replaced it with an aria-label
- Fixed empty table header
2024-01-30 23:24:28 +01:00
Alexandre Vryghem
1989a6c042 Fixed accessibility issues with pagination button
- Replaced the h6 tags with the role heading
- Gave the gear button the roles in order to be detected as an expandable menu
- Replaced the dropdown structure to render a menu of listboxes
- Added the aria-selected attribute
2024-01-30 23:24:27 +01:00
Stefano Maffei
47e6bc8070 [CST-13444] configured label for date facet for LDN Message Entity 2024-01-30 18:27:57 +01:00
Andreas Awouters
87bb3cae30 Merge remote-tracking branch 'dspace/main' into process-admin-ui-redesign-8.0.0-next 2024-01-29 11:19:42 +01:00
Andreas Awouters
5d00c6bae2 107873: Add number of processes badge 2024-01-29 11:05:05 +01:00
Andreas Awouters
e8ed533874 107873: Remove pagination gear 2024-01-29 11:04:55 +01:00
Andreas Awouters
21f1beb8e9 107873: Set process overview table column widths 2024-01-29 11:04:47 +01:00
Andreas Awouters
73e823e969 107873: Add number of processes badge 2024-01-29 10:57:41 +01:00
Francesco Molinaro
7b42d41580 Merged in CST-13397-fix-ssr-issue (pull request #1285)
fix ssr issue

Approved-by: Stefano Maffei
2024-01-29 08:40:57 +00:00
Andreas Awouters
536c02a078 107873: Remove pagination gear 2024-01-29 09:12:50 +01:00
Andreas Awouters
2ad97d2845 107873: Set process overview table column widths 2024-01-29 09:08:46 +01:00
William Welling
f40639de86 Dedupe patch operations by op and path 2024-01-26 16:01:01 -06:00
William Welling
018d44c616 NgbTypeahead instance is only defined when model has authority 2024-01-26 15:50:41 -06:00
FrancescoMolinaro
2f943bbb72 fix ssr issue 2024-01-26 18:39:39 +01:00
Stefano Maffei
8d49fe6266 Merge remote-tracking branch 'origin/coar-notify-7' into coar-notify-7-part-two 2024-01-26 16:41:07 +01:00
Francesco Molinaro
5bf9cead0d Merged in CST-13401-coar-metadata (pull request #1280)
CST-13401 add missing metadata

Approved-by: Stefano Maffei
2024-01-26 15:36:20 +00:00
Francesco Molinaro
58da92d2a2 Merged in CST-13405-ui-fine-tuning-part-1 (pull request #1281)
CST-13405 fix QA breadcrumbs, ldn iprange hint

Approved-by: Stefano Maffei
2024-01-26 15:35:11 +00:00
Francesco Molinaro
4ff0c7920d Merged in CST-13397-ui-fine-tuning-part-2 (pull request #1282)
CST-13397 - fine tune UI

Approved-by: Stefano Maffei
2024-01-26 15:32:17 +00:00
FrancescoMolinaro
9b045070c8 CST-13397 - fine tune UI 2024-01-26 16:19:10 +01:00
FrancescoMolinaro
e0a30bd86a CST-13405 fix QA breadcrumbs, ldn iprange hint 2024-01-26 15:54:37 +01:00
FrancescoMolinaro
db0c82b9b2 CST-13401 add missing metadata 2024-01-26 15:18:32 +01:00
Francesco Molinaro
a27e55ec6b Merged in CST-13383-breadcrumb-bug (pull request #1277)
CST-13383 breadcrumb bug

Approved-by: Stefano Maffei
2024-01-26 13:31:26 +00:00
FrancescoMolinaro
9056e9830d fix issue with breadcrumb 2024-01-26 13:42:58 +01:00
FrancescoMolinaro
b8555f53c3 add commented example 2024-01-26 11:53:56 +01:00
FrancescoMolinaro
dfaaf298b1 refactor, adapt breadcrumbs for non admin users, fix translations 2024-01-26 11:47:18 +01:00
Tim Donohue
5b9a98adc1 Merge pull request #2755 from 4Science/DURACOM-223
Add logo max constraints
2024-01-25 15:07:54 -06:00
FrancescoMolinaro
80174817fd add breadcrumbs, adapt tests, fix issue on page loading 2024-01-25 19:32:12 +01:00
Tim Donohue
38ae9ce0e0 Merge pull request #2769 from saschaszott/patch-12
fixed typo in info.feedback.page_help
2024-01-25 10:25:33 -06:00
Sascha Szott
e03b492dbd minor improvement of German translation 2024-01-25 16:51:25 +01:00
Sascha Szott
26ebc9f8fc added german translations of feedback page translation keys 2024-01-25 16:42:36 +01:00
Sascha Szott
4b7798fcc7 fixed typo 2024-01-25 16:10:00 +01:00
FrancescoMolinaro
0caacd98da small clean up 2024-01-25 15:26:44 +01:00
Art Lowel
db813621f1 Merge branch 'w2p-107873_Process-admin-UI-redesign-01-Overview-page-tables' into process-admin-ui-redesign-8.0.0-next 2024-01-25 14:19:19 +01:00
Art Lowel
ba31670db7 style tweaks 2024-01-25 14:18:48 +01:00
FrancescoMolinaro
604d355902 update labels 2024-01-25 13:59:17 +01:00
FrancescoMolinaro
e77343604e refactor and add tests 2024-01-25 13:50:23 +01:00
FrancescoMolinaro
4cca9015a1 remove unused imports 2024-01-25 13:23:01 +01:00
FrancescoMolinaro
d4bf3a519a refactor, clean up 2024-01-25 13:22:17 +01:00
FrancescoMolinaro
66cd035f87 refactor, fix tests 2024-01-25 11:50:02 +01:00
FrancescoMolinaro
f82ba8c5ad add width 100% to prevent overflow on mobile 2024-01-25 09:00:05 +01:00
frabacche
afa6559b19 CST-5249_suggestion refactor deleting 'reciter', docs 2024-01-24 18:32:21 +01:00
Davide Negretti
80cc4c5d9a [DURACOM-195] fix comment 2024-01-24 18:23:14 +01:00
Andreas Awouters
5857a8d972 Fix Processes Overview accessibility issues
See commit 4ea487cc0d
2024-01-24 15:36:57 +01:00
Andreas Awouters
a2f5f8291c Merge remote-tracking branch 'dspace/main' into process-admin-ui-redesign-8.0.0-next 2024-01-24 15:25:45 +01:00
Andreas Awouters
151bac5d3a Merge remote-tracking branch 'atmire/feature-process_polling' into process-admin-ui-redesign-8.0.0-next 2024-01-24 15:25:27 +01:00
Andrea Barbasso
4ecfdf2b79 [DURACOM-191] fix missing imports and providers 2024-01-24 14:26:31 +01:00
Andrea Barbasso
1afec99f91 Merge branch 'main' into DURACOM-191
# Conflicts:
#	src/app/shared/confirmation-modal/confirmation-modal.component.ts
#	src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts
#	src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts
2024-01-24 11:45:45 +01:00
Alisa Ismailati
1b5007aae0 [CST-12109] display user's email info on topic list 2024-01-24 10:12:47 +01:00
Andreas Awouters
6b36f0e03f 107873: Fix ProcessOverviewTableComponent tests 2024-01-24 08:54:55 +01:00
Andreas Awouters
d5ac25a1a5 107873: Fix ProcessOverviewService doc comment 2024-01-24 08:54:55 +01:00
Andreas Awouters
41bbf81f18 107873: Precompute process info for display in table 2024-01-24 08:54:55 +01:00
Agustina Martinez
e77898eaac Update item-status.component.ts
Fix initialOperations typo and return ops with register doi op
2024-01-23 21:47:29 +00:00
Alisa Ismailati
ac6c890d78 [CST-12109] tests & translations 2024-01-23 17:51:12 +01:00
frabacche
2bb421c7cf CST-5249_suggestion community first code review 2024-01-23 17:44:49 +01:00
frabacche
956d661e62 Merge remote-tracking branch 'origin/main' into CST-5249_suggestion 2024-01-23 15:01:00 +01:00
Alisa Ismailati
2f43bb71a0 [CST-12109] small fixes 2024-01-23 14:53:41 +01:00
frabacche
1a4536ebb9 Merge branch 'CST-5249_suggestion' of github.com:4Science/dspace-angular into CST-5249_suggestion 2024-01-23 14:31:51 +01:00
frabacche
4dedda3f9f CST-5249_suggestion refactor label name openaire 2024-01-23 14:30:07 +01:00
Alisa Ismailati
8cc45e8cbe [CST-12109] request reinstate for normal users 2024-01-23 12:59:47 +01:00
Andrea Barbasso
b53f5a1ea2 [DURACOM-191] fix edit item relationships 2024-01-23 12:21:50 +01:00
FrancescoMolinaro
d9e25882ce remove class from template 2024-01-23 09:17:18 +01:00
FrancescoMolinaro
d3c788a4e1 adapt solution to prevent img overflow 2024-01-23 09:16:06 +01:00
Jens Vannerum
c02b46ca7d 110088: new implementation for keyboard support in dropdowns 2024-01-23 11:15:40 +03:00
Andreas Awouters
f784523d61 107873: Add small margins so buttons don't stick to next component 2024-01-23 08:49:03 +01:00
Andreas Awouters
907f2e83e6 107873: Shorten paginationId
I chose 2 characters instead of one in case a 'SUCCEEDED' process status
is added in the future. 'SUCCEEDED' could encompass both 'COMPLETED' and
'FAILED' processes, but it's paginationId would clash with 'SCHEDULED'
(both start with 's'). By choosing the first 2 characters of the status,
this clash does not occur (ids would be 'sc' and 'su').
2024-01-23 08:22:22 +01:00
Andreas Awouters
492f4c22fc 107873: Add redirect on 4xx 2024-01-23 08:17:03 +01:00
Davide Negretti
f25ec6210b [DURACOM-195] fix issue with event handling in expandable admin sidebar section 2024-01-23 00:08:57 +01:00
Alisa Ismailati
ad5e3af141 [CST-12109] "refresh" page refactor 2024-01-22 16:59:10 +01:00
Jens Vannerum
05eb21250e 110088: changes to keyboard navigation / selection 2024-01-22 15:06:57 +01:00
Andrea Barbasso
2a5fd4e3a2 [DURACOM-191] fix tests after merge 2024-01-22 11:01:49 +01:00
Andrea Barbasso
c3b31f0144 [DURACOM-191] fix missing import after merge 2024-01-22 09:47:53 +01:00
FrancescoMolinaro
e4eb8b5a63 add tests 2024-01-20 13:36:24 +01:00
FrancescoMolinaro
b77aa2d9d8 add tests 2024-01-20 11:24:42 +01:00
Tim Donohue
eee7267669 Merge pull request #2338 from alexandrevryghem/improve-confirmation-modal_contribute-main
Made ConfirmationModalComponent more generic
2024-01-19 16:32:27 -06:00
Andrea Barbasso
3eb91a0aab Merge branch 'main' into DURACOM-191
# Conflicts:
#	src/app/shared/form/number-picker/number-picker.component.spec.ts
#	src/app/shared/form/number-picker/number-picker.component.ts
2024-01-19 19:05:23 +01:00
Andrea Barbasso
4322bca859 [DURACOM-191] add deprecated comment 2024-01-19 18:54:43 +01:00
FrancescoMolinaro
d497689317 add role to not semantic tag with aria-label (span) 2024-01-19 18:45:53 +01:00
Andrea Barbasso
107e960a9f Merge remote-tracking branch 'github/DURACOM-191' into DURACOM-191 2024-01-19 18:00:15 +01:00
Giuseppe Digilio
4716a3c492 [DURACOM-191] fix unit test with missing providers 2024-01-19 17:59:49 +01:00
Andrea Barbasso
47a257e7a0 [DURACOM-191] add deprecated in jsdocs for decorators 2024-01-19 17:56:24 +01:00
Andrea Barbasso
d63caf5242 Merge branch 'main' into DURACOM-191
# Conflicts:
#	src/app/access-control/group-registry/group-form/members-list/members-list.component.ts
#	src/app/core/eperson/eperson-data.service.ts
2024-01-19 16:47:40 +01:00
Davide Negretti
45e4335437 Merge branch 'main' into DURACOM-195
# Conflicts:
#	src/app/shared/auth-nav-menu/auth-nav-menu.component.scss
2024-01-19 16:30:49 +01:00
Tim Donohue
a59f371ecf Merge pull request #2691 from misilot/fix-2690
Decrease min-height for Login Dropdown Menu
2024-01-19 09:22:36 -06:00
Tim Donohue
6536fbff62 Merge pull request #2736 from saschaszott/patch-11
use localized error message
2024-01-19 09:21:26 -06:00
FrancescoMolinaro
1600bd5af2 add tests 2024-01-19 15:48:40 +01:00
Andrea Barbasso
31f89e8cb9 [DURACOM-191] fix errors 2024-01-19 14:30:45 +01:00
Andrea Barbasso
59e9e0fe62 Merge remote-tracking branch 'github/DURACOM-191' into DURACOM-191 2024-01-19 14:20:10 +01:00
Giuseppe Digilio
cfe802096c [DURACOM-191] fix circular dependencies 2024-01-19 13:11:49 +01:00
Andrea Barbasso
8e3a145bf8 Merge remote-tracking branch 'github/DURACOM-191' into DURACOM-191 2024-01-19 12:25:17 +01:00
Andrea Barbasso
891f18d96d [DURACOM-191] remove leftover provide files, use exported consts instead 2024-01-19 12:24:27 +01:00
Giuseppe Digilio
8861a21a2a [DURACOM-191] fix circular dependencies 2024-01-19 12:21:26 +01:00
FrancescoMolinaro
32e3141b19 add logo max costraints 2024-01-19 08:57:12 +01:00
Alan Orth
477ea93fd1 Merge pull request #2721 from alanorth/fix-community-cdk-tree-links
Fix whitespace in links due to newlines
2024-01-19 08:13:42 +03:00
Davide Negretti
8f73b8ff9d [DURACOM-195] accessibility issue fixed on log-out button 2024-01-18 21:07:52 +01:00
Giuseppe Digilio
171e0e3dc9 [DURACOM-191] fix circular dependencies 2024-01-18 19:13:43 +01:00
Alisa Ismailati
e4858f2239 [CST-12109] fixes 2024-01-18 18:48:31 +01:00
Alisa Ismailati
415bdeabc1 cherry-picked "[CST-12145] get qa-sources by target for item-page & refactor" into [CST-12109] 2024-01-18 17:35:59 +01:00
FrancescoMolinaro
5dcd52da26 fix tests 2024-01-18 17:35:42 +01:00
Alisa Ismailati
80a0cd40cb [CST-12109] "refresh" page on item withdrawn from details page 2024-01-18 17:27:13 +01:00
Andrea Barbasso
bb64be3a2b [DURACOM-191] remove provide-render-auth-method, use exported const instead 2024-01-18 15:53:31 +01:00
Andrea Barbasso
e287e25906 [DURACOM-191] add withEntryComponents() to listable.module.ts 2024-01-18 15:26:59 +01:00
Andrea Barbasso
25381697e0 [DURACOM-191] remove provide-render-browse-by, use exported const instead 2024-01-18 15:18:18 +01:00
Alisa Ismailati
a4b0c5a13d cherry-picked "[CST-12791] changed the route for suggestions to be be accessible to any logged in user" to CST-12109 2024-01-18 12:46:49 +01:00
Andrea Barbasso
93853ba542 [DURACOM-191] fix missing listable components 2024-01-18 11:10:19 +01:00
FrancescoMolinaro
532e529201 resolve conflict on missing IpV4Validator 2024-01-18 10:33:11 +01:00
FrancescoMolinaro
c2f44c33f3 resolve conflict on missing IpV4Validator 2024-01-18 10:28:05 +01:00
FrancescoMolinaro
f83d75fb41 Merge branch 'coar-notify-7' into coar-notify-7-part-two 2024-01-18 10:27:35 +01:00
Stefano Maffei
abfdaa0690 Merge remote-tracking branch 'origin/CST-13316-improvments-part-two' into coar-notify-7-part-two 2024-01-18 09:31:00 +01:00
Stefano Maffei
a1ad620a04 [CST-13316] added missing labels and fixed typo 2024-01-18 09:30:41 +01:00
Andrea Barbasso
4d23626aed [DURACOM-191] fix lint 2024-01-18 09:08:32 +01:00
Andrea Barbasso
579bedc352 [DURACOM-191] fix tests 2024-01-18 08:44:08 +01:00
Alexandre Vryghem
b8e0401388 Fix header ordering 2024-01-17 23:26:11 +01:00
Alexandre Vryghem
d876fadd2a Fixed accessibility issues on SubmissionImportExternalSearchbarComponent
- Fixed input not describing itself
- Put the items from the dropdown in an ul
- Fixed broken aria labels
- Removed titles who where identical to the displayed value
2024-01-17 23:26:11 +01:00
Alexandre Vryghem
3c1243f6f1 Fixed search filters having empty input buttons 2024-01-17 23:26:11 +01:00
Alexandre Vryghem
086c5463a8 Removed invisible buttons on the CommunityListComponent & fixed content displacement when expanding com/col 2024-01-17 23:26:10 +01:00
Alexandre Vryghem
2f5370a085 Fixed accessibility issues related to dso selectors
- Fixed header ordering
- Fixed input field not having a description what it does (because the header isn't always shown I decided to use aria-labels instead of regular labels)
2024-01-17 23:26:10 +01:00
Davide Negretti
6dec34cb00 Merge branch 'main' into DURACOM-195
# Conflicts:
#	cypress/e2e/collection-statistics.cy.ts
#	cypress/e2e/community-statistics.cy.ts
#	cypress/e2e/item-statistics.cy.ts
2024-01-17 22:24:06 +01:00
Tim Donohue
1c782b2656 Merge pull request #2711 from tdonohue/add_accessibility_tests
Add more automated accessibility scanning to e2e tests & fix a few minor accessibility bugs
2024-01-17 13:37:49 -06:00
Giuseppe Digilio
65e5ec801f [DURACOM-191] fix tests 2024-01-17 20:22:08 +01:00
Tim Donohue
a5a89a4b98 Add environment variable for Person collection name. Use it in submission e2e tests. 2024-01-17 12:05:00 -06:00
Giuseppe Digilio
f0b094e609 [DURACOM-191] fix lint errors 2024-01-17 19:04:29 +01:00
Tim Donohue
0aaf3131fa Move increment/decrement to i18n. Update to use "name". 2024-01-17 11:49:00 -06:00
Alisa Ismailati
805e98b837 [CST-12109] fixed failing unit tests after merge 2024-01-17 18:39:54 +01:00
FrancescoMolinaro
f6851fa132 finalize UI improvements 2024-01-17 18:18:40 +01:00
Tim Donohue
31bc05235e Add accessibility testing for admin sidebar 2024-01-17 11:16:54 -06:00
Tim Donohue
80492cd88a Add Item Edit accessibility tests. Switch Item to use to one with bitstreams. Minor updates to HTML to pass accessibility tests. 2024-01-17 11:16:54 -06:00
Tim Donohue
00cb2f9e8a Add accessibility tests (and minor fixes) for Edit Collection's Content Source tab and Item Mapper tab 2024-01-17 11:16:54 -06:00
Tim Donohue
9894d315a0 Add Edit Collection accessibility Testing 2024-01-17 11:16:54 -06:00
Tim Donohue
6c76d8c1d7 Add Edit Community accessibility tests & minor cleanup 2024-01-17 11:16:54 -06:00
Tim Donohue
e47593b303 Fix circular dependency issue by using Cypres env variables directly instead of global constants 2024-01-17 11:16:54 -06:00
Tim Donohue
8d61fa6ac3 Improve accessibility testing on Submission page 2024-01-17 11:16:54 -06:00
Tim Donohue
a581219df5 Refactor e2e test infrastruction to allow easier way to lookup REST API info and generate CSRF tokens 2024-01-17 11:16:54 -06:00
Tim Donohue
5dad8bec39 Fix ARIA labels and tabindex on date picker in submission form. Tabindex is unnecessary & throws accessibility errors from AXE tools 2024-01-17 11:16:53 -06:00
Tim Donohue
aeea1cd592 Fix ARIA labels on submission form relationship button and dynamic dropdowns 2024-01-17 11:16:53 -06:00
Tim Donohue
53d521a87e Add new e2e accessibility tests & update some existing ones 2024-01-17 11:16:53 -06:00
Andrea Barbasso
a348be5d8c [DURACOM-191] fix tests of non-standalone components 2024-01-17 17:59:48 +01:00
Francesco Molinaro
034beb2a0b Merged in CST-13315-improvements-part-1 (pull request #1240)
improve ldn form template

Approved-by: Stefano Maffei
2024-01-17 16:19:08 +00:00
FrancescoMolinaro
9ee2ecf718 fix style 2024-01-17 17:16:38 +01:00
FrancescoMolinaro
b870d538f7 add dashboard description 2024-01-17 17:05:27 +01:00
FrancescoMolinaro
25890248ad remove pointless double negation 2024-01-17 17:00:25 +01:00
FrancescoMolinaro
37edb06352 restore ip section 2024-01-17 16:58:42 +01:00
FrancescoMolinaro
890b333265 Merge branch 'coar-notify-7' into CST-13315-improvements-part-1 2024-01-17 16:47:28 +01:00
FrancescoMolinaro
1a65b56c2b configure menu section, reprocess button condition 2024-01-17 16:42:06 +01:00
Francesco Molinaro
ae8e9fbfe2 Merged in CST-12867-ip-range-for-ldn-service (pull request #1239)
CST-12867 ip range for ldn service

Approved-by: Stefano Maffei
2024-01-17 15:30:35 +00:00
FrancescoMolinaro
8c100682cf improve ldn form template 2024-01-17 16:22:38 +01:00
Alisa Ismailati
3ecc1ca68b Merge branch 'main' into CST-12109-WITHDRAWN-REINSTATE-requests 2024-01-17 16:02:21 +01:00
frabacche
a878696493 CST-12820 QAEvent Service Name: to Service URL 2024-01-17 15:59:53 +01:00
FrancescoMolinaro
7ad2e1ea52 fix tests 2024-01-17 15:50:13 +01:00
Andrea Barbasso
d6bf788ad3 [DURACOM-191] delete provide-listable-objects.ts 2024-01-17 15:39:43 +01:00
Andrea Barbasso
bf6b30364f [DURACOM-191] create listable.module 2024-01-17 15:26:25 +01:00
Stefano Maffei
9e9b4cae15 Merge branch 'coar-notify-7' into coar-notify-7-part-two 2024-01-17 14:41:07 +01:00
Francesco Molinaro
6069eaea0a Merged in CST-13289-notify-ui-improvements (pull request #1222)
CST-13289 notify ui improvements

Approved-by: Stefano Maffei
2024-01-17 13:30:10 +00:00
Davide Negretti
6dee989609 Merge remote-tracking branch 'GitHub_DSpace/main' into DURACOM-195
# Conflicts:
#	src/styles/_bootstrap_variables.scss
#	src/styles/_custom_variables.scss
#	src/themes/dspace/styles/_theme_sass_variable_overrides.scss
2024-01-17 12:55:27 +01:00
Andrea Barbasso
89705d8afe [DURACOM-191] fix menu item map 2024-01-17 10:09:01 +01:00
Andrea Barbasso
b61720cff7 [DURACOM-191] fix section map 2024-01-17 09:56:02 +01:00
frabacche
aa825af5f1 Merge branch 'coar-notify-7-part-two' of bitbucket.org:4Science/dspace-angular into coar-notify-7-part-two 2024-01-17 09:00:10 +01:00
frabacche
fc4b954e97 CST-10644 administrative search facets 2024-01-17 08:58:55 +01:00
Davide Negretti
717041b110 Merge branch 'main' into DURACOM-195 2024-01-16 23:57:56 +01:00
Tim Donohue
55c191e742 Merge pull request #2587 from Dawnkai/main
Deque Analysis Color Contrast fixes
2024-01-16 16:01:50 -06:00
FrancescoMolinaro
fb1d20afc0 fix action type error 2024-01-16 13:35:46 +01:00
Andrea Barbasso
091395a296 [DURACOM-191] set map for browse by 2024-01-16 12:24:16 +01:00
Andrea Barbasso
076482eacc [DURACOM-191] set map for search filters 2024-01-16 11:54:15 +01:00
Andrea Barbasso
0fb34e984d [DURACOM-191] set map for menu section 2024-01-16 11:45:53 +01:00
FrancescoMolinaro
91706bf1ee minor UI improvements, adapt notifications menu section, change translations, minor restyle 2024-01-16 11:15:42 +01:00
Andrea Barbasso
09c070e164 [DURACOM-191] set map for workflow actions 2024-01-16 10:03:09 +01:00
FrancescoMolinaro
02a289ed64 Merge branch 'coar-notify-7-part-two' into CST-13289-notify-ui-improvements 2024-01-16 08:49:39 +01:00
Maciej Kleban
c20b0a7c11 Replace hard-coded colors with bootstrap variants
* Replace custom variables with Bootstrap variants
* Replace custom button colors with Bootstrap variants
* Remove custom colors and replace them with bootstrap variants
* Fix checkbox offset styles
---------
Co-authored-by: Maciej Kleban <maciej.kleban@pcgacademia.pl>
2024-01-15 20:26:04 +01:00
Dawnkai
7fbb692e94 Deque Analysis Color Contrast fixes 2024-01-15 20:25:32 +01:00
FrancescoMolinaro
9db4d9d6d7 fix lint 2024-01-15 17:47:15 +01:00
FrancescoMolinaro
67d8230cf6 fix merge conflicts 2024-01-15 17:38:29 +01:00
FrancescoMolinaro
89411193e5 Merge branch 'coar-notify-7' into coar-notify-7-part-two 2024-01-15 17:38:16 +01:00
Andrea Barbasso
a91363044e [DURACOM-191] set map for starts-with decorator 2024-01-15 17:20:36 +01:00
FrancescoMolinaro
a183b0a923 add content fallback and truncate text 2024-01-15 16:43:51 +01:00
Francesco Molinaro
8726fccad3 Merged in CST-13053-logs-detail-message (pull request #1217)
CST-13053 logs detail message

Approved-by: Stefano Maffei
2024-01-15 15:36:45 +00:00
Francesco Molinaro
20efab260b Merged in CST-13039-multiple-request-patterns (pull request #1141)
CST-13039 multiple request patterns

Approved-by: Stefano Maffei
2024-01-15 15:32:32 +00:00
FrancescoMolinaro
57861a1126 fix tests, small refactor, add translations 2024-01-15 16:08:30 +01:00
Andrea Barbasso
cbae8b4ea7 [DURACOM-191] refactor sections-decorator.ts 2024-01-15 15:42:02 +01:00
FrancescoMolinaro
ba8345c6ab Merge branch 'coar-notify-7-part-two' into CST-13053-logs-detail-message 2024-01-15 15:35:39 +01:00
FrancescoMolinaro
1fdd152735 fix tests 2024-01-15 15:33:54 +01:00
Francesco Molinaro
b34c2ec040 Merged in CST-13262-clickable-notify-boxes (pull request #1214)
CST-13262 clickable notify boxes

Approved-by: Stefano Maffei
2024-01-15 14:25:03 +00:00
Alisa Ismailati
b9d865885d Merged in coar-CST-12791 (pull request #1073)
[CST-12791] changed the route for suggestions to be be accessible to any logged in user

Approved-by: Stefano Maffei
2024-01-15 14:21:05 +00:00
Alisa Ismailati
2ecc782839 Merged in CST-12791-merge-main-to-coar (pull request #1204)
CST-12791 merge main to coar

Approved-by: Stefano Maffei
2024-01-15 14:12:15 +00:00
FrancescoMolinaro
6a1871ba38 rename param 2024-01-15 14:59:39 +01:00
Andrea Barbasso
e95a27b854 [DURACOM-191] refactor menu-item.decorator.ts 2024-01-15 14:49:34 +01:00
Andrea Barbasso
e7be5129ba [DURACOM-191] set map for login methods 2024-01-15 14:45:34 +01:00
FrancescoMolinaro
ec9797a2c8 add message detail and translations 2024-01-15 13:42:56 +01:00
Andrea Barbasso
6b827426fa [DURACOM-191] fix defer not being solved 2024-01-15 13:06:37 +01:00
Andrea Barbasso
2d3c5cef06 [DURACOM-191] set static map for menu-item decorator 2024-01-15 12:13:14 +01:00
Andrea Barbasso
4cab7a31af [DURACOM-191] fix missing root provider 2024-01-15 11:53:10 +01:00
FrancescoMolinaro
8e11047ee9 Merge branch 'coar-notify-7-part-two' into CST-13262-clickable-notify-boxes 2024-01-15 11:33:29 +01:00
FrancescoMolinaro
f4c2b0b3fd implement items clickable, fix minor issues, add filters 2024-01-15 11:31:32 +01:00
Andrea Barbasso
b43e873598 [DURACOM-191] fix wrong component declaration 2024-01-15 11:11:14 +01:00
Andrea Barbasso
9cce55327e [DURACOM-191] remove unused decorator 2024-01-15 10:49:50 +01:00
FrancescoMolinaro
93777b9d61 fix lint 2024-01-12 18:10:09 +01:00
FrancescoMolinaro
61a05dff13 add navigation with new config, add custom filter 2024-01-12 18:06:09 +01:00
Stefano Maffei
df0cdc3fa5 Merge remote-tracking branch 'origin/coar-notify-7-part-two' into coar-notify-7-part-two 2024-01-12 17:06:53 +01:00
Stefano Maffei
46eaa583bb [CST-13053] added missing breadcrumbs for new filter 2024-01-12 16:45:00 +01:00
FrancescoMolinaro
dbf4233e07 fix missing label in modal, add search-labels, fix reprocess message 2024-01-12 16:44:32 +01:00
Alisa Ismailati
16b9a14191 [CST-12791] rename of files & components by removing admin 2024-01-12 16:04:12 +01:00
Andrea Barbasso
061ff519d7 [DURACOM-191] fix item submission 2024-01-12 15:52:35 +01:00
frabacche
ca8757f52c Merge branch 'coar-notify-7' of bitbucket.org:4Science/dspace-angular into coar-notify-7 2024-01-12 15:36:53 +01:00
frabacche
fc11a1ac71 CST-12820 coar qaevent view ServiceId on ServiceName 2024-01-12 15:36:34 +01:00
FrancescoMolinaro
3a87044600 remove unused param 2024-01-12 15:08:35 +01:00
FrancescoMolinaro
11a80771a8 add ldn services test 2024-01-12 15:08:08 +01:00
Alisa Ismailati
565f723096 Merge branch 'CST-12791-merge-main-to-coar' into coar-CST-12791 2024-01-12 14:39:02 +01:00
FrancescoMolinaro
1693d49e27 fix SSR issue for notify dashboard 2024-01-12 12:06:06 +01:00
Alisa Ismailati
3ae430460f fixed errors 2024-01-12 11:55:19 +01:00
Andreas Awouters
24ece9f33a 107873: Fix lint error by renaming rxjs timer import 2024-01-12 11:52:05 +01:00
Andreas Awouters
833b7e732d 107873: Remove table tests from ProcessOverviewComponent
ProcessOverviewComponent no longer is responsible for displaying the
table. As a result the tests no longer work and are no longer relevant.
2024-01-12 11:44:13 +01:00
Andreas Awouters
6f73b3cd91 107873: Add ProcessOverviewTableComponent tests 2024-01-12 11:44:13 +01:00
Andreas Awouters
4061aaac2e 107873: Rework autoRefreshingSearchBy test 2024-01-12 11:44:13 +01:00
Andreas Awouters
2bda29ad1d 107873: Rework autoRefreshingSearchBy 2024-01-12 11:44:13 +01:00
FrancescoMolinaro
96a6b80d2c add test fix mocks 2024-01-12 11:35:15 +01:00
Alisa Ismailati
c6993aee80 Merge branch 'main' into coar-notify-7 2024-01-12 11:21:15 +01:00
FrancescoMolinaro
7a5b52f691 fix lint, change translation, clean up component 2024-01-12 11:15:42 +01:00
Stefano Maffei
a03b7e4628 Merge branch 'coar-notify-7' into coar-notify-7-part-two 2024-01-12 11:05:07 +01:00
Yury Bondarenko
6ff2170ca8 Merge remote-tracking branch 'origin/main' into more-eslint 2024-01-12 10:57:51 +01:00
Andrea Barbasso
6313d42937 [DURACOM-191] fix core store imports 2024-01-12 10:46:20 +01:00
FrancescoMolinaro
b5fa46ce86 merge coar-notify-7 2024-01-12 09:39:39 +01:00
FrancescoMolinaro
d1e5287d60 refactor add tests for validator 2024-01-12 09:37:57 +01:00
Alan Orth
c55d518df5 Merge pull request #2662 from alexandrevryghem/fix-eperson-link-on-group-pages_contribute-main
Fixed ePerson link on edit group page
2024-01-12 08:25:02 +03:00
FrancescoMolinaro
1002a4d8a0 add to shared module 2024-01-11 18:06:27 +01:00
FrancescoMolinaro
ccb5444ad1 add base template and validator 2024-01-11 17:52:27 +01:00
Francesco Molinaro
70aeed61ef Merged in CST-12766-UI-adaption (pull request #1151)
fix dropdown overflow and bar z-index

Approved-by: Stefano Maffei
2024-01-11 16:17:36 +00:00
Francesco Molinaro
f20f6a74f1 Merged in CST-12766-porting-from-part-2 (pull request #1202)
fix dropdown overflow and bar z-index

Approved-by: Stefano Maffei
2024-01-11 16:17:31 +00:00
Francesco Molinaro
f6557fef02 Merged in CST-13091-missing-validation-message (pull request #1173)
add validation message

Approved-by: Stefano Maffei
2024-01-11 16:02:20 +00:00
Francesco Molinaro
a6da490d29 Merged in CST-10642-notify-logs (pull request #1183)
CST-10642 notify logs

Approved-by: Stefano Maffei
2024-01-11 15:54:02 +00:00
FrancescoMolinaro
7e25384bca adapt ui improvements due to missing outbound patterns 2024-01-11 16:34:25 +01:00
Andrea Barbasso
c3a2b89de0 [DURACOM-191] fix store declaration 2024-01-11 16:25:41 +01:00
FrancescoMolinaro
7692af10e1 merge UI improvements 2024-01-11 16:21:10 +01:00
Andrea Barbasso
0e52113069 [DURACOM-191] fix services not loading 2024-01-11 16:05:14 +01:00
FrancescoMolinaro
7640dac90a update translations 2024-01-11 15:57:26 +01:00
FrancescoMolinaro
52027578a0 update translations 2024-01-11 15:55:21 +01:00
Andrea Barbasso
3c829cae6e [DURACOM-191] fix typo 2024-01-11 15:28:57 +01:00
FrancescoMolinaro
6079e58d15 add missing translations handler 2024-01-11 14:00:18 +01:00
FrancescoMolinaro
75fb52731a refactor translations mapping 2024-01-11 11:23:53 +01:00
FrancescoMolinaro
a370b14763 fix lint 2024-01-11 10:41:47 +01:00
FrancescoMolinaro
441170d8a3 refactor, add date formatting 2024-01-11 10:29:22 +01:00
Andrea Barbasso
a3fefc20a7 fix tests 2024-01-11 09:46:44 +01:00
FrancescoMolinaro
34cecedfeb refactor test, change type in table 2024-01-11 09:20:22 +01:00
Andreas Awouters
944b614514 107873: Add process overview page tables 2024-01-11 08:22:49 +01:00
Andrea Barbasso
2d879bd3fe [DURACOM-191] change routing for subscriptions-page 2024-01-10 17:58:22 +01:00
Andrea Barbasso
db169307b4 [DURACOM-191] change routing for access-control 2024-01-10 17:50:33 +01:00
Andrea Barbasso
6159c22343 [DURACOM-191] change routing for health-page 2024-01-10 17:48:50 +01:00
Andrea Barbasso
13f5a7f045 [DURACOM-191] change routing for statistics-page 2024-01-10 17:45:51 +01:00
Andrea Barbasso
e6d1765ea3 [DURACOM-191] change routing for request-copy 2024-01-10 17:41:38 +01:00
Andrea Barbasso
2ca8990453 [DURACOM-191] change routing for info 2024-01-10 17:39:32 +01:00
Andrea Barbasso
6aefab13aa [DURACOM-191] change routing for process-page 2024-01-10 17:21:42 +01:00
Andrea Barbasso
7f3739f7ed [DURACOM-191] change routing for profile-page 2024-01-10 17:05:33 +01:00
Andrea Barbasso
ca09cfb07b [DURACOM-191] change routing for workflowitems-edit-page 2024-01-10 17:03:40 +01:00
Andrea Barbasso
43241a237a [DURACOM-191] change routing for workspaceitems-edit-page 2024-01-10 17:02:09 +01:00
Andrea Barbasso
5c0ceab794 [DURACOM-191] change routing for import-external-page 2024-01-10 16:59:24 +01:00
FrancescoMolinaro
9332a33262 add tests 2024-01-10 16:58:16 +01:00
Sascha Szott
92a74cee5a use localized error message 2024-01-10 16:54:18 +01:00
Andrea Barbasso
d6795c2100 [DURACOM-191] change routing for submit-page 2024-01-10 16:53:45 +01:00
Andrea Barbasso
fe23afe896 [DURACOM-191] change routing for logout-page 2024-01-10 16:52:31 +01:00
Andrea Barbasso
dae3659ed6 [DURACOM-191] change routing for login-page 2024-01-10 16:46:55 +01:00
Andrea Barbasso
6136d5dc92 [DURACOM-191] change routing for admin-page 2024-01-10 16:45:06 +01:00
Andrea Barbasso
dcaac9e68f [DURACOM-191] change routing for browse-by-page 2024-01-10 15:49:56 +01:00
Andrea Barbasso
6692eb8ffe [DURACOM-191] change routing for search-page 2024-01-10 15:20:22 +01:00
Andrea Barbasso
737f035357 [DURACOM-191] change routing for my-dspace-page 2024-01-10 15:11:39 +01:00
Andrea Barbasso
df85f8f573 [DURACOM-191] change routing for bitstream-page 2024-01-10 15:05:07 +01:00
Andrea Barbasso
5ade030259 [DURACOM-191] change routing for item-page 2024-01-10 14:53:40 +01:00
Andrea Barbasso
4b84c35417 [DURACOM-191] change routing for collection-page 2024-01-10 14:40:59 +01:00
FrancescoMolinaro
6fda2cb4f7 add tests 2024-01-10 12:57:13 +01:00
Andreas Awouters
86cad57845 107873: Add process-data searchBy tests 2024-01-10 12:07:30 +01:00
Andreas Awouters
64befd2e30 107873: Implement autoRefreshingSearchBy for process-data service 2024-01-10 12:07:30 +01:00
Andreas Awouters
c7922b7e67 107873: Implement SearchData for process-data service 2024-01-10 12:07:30 +01:00
Andrea Barbasso
4d5d1ec6fb [DURACOM-191] change routing for community-page, delete community-page.module.ts 2024-01-10 11:18:45 +01:00
Jens Vannerum
3a265bb0e7 110088: reset pagination on opening of dropdown 2024-01-10 11:03:36 +01:00
Andrea Barbasso
1145c01ba9 [DURACOM-191] change routing for forgot-password, delete forgot-password.module.ts 2024-01-10 10:51:00 +01:00
Andrea Barbasso
d73fc0f073 [DURACOM-191] change routing for register-page, delete register-page.module.ts 2024-01-10 10:14:43 +01:00
Andrea Barbasso
a2d66c6e34 [DURACOM-191] change routing for community-list-page
can't delete module - for now - since it has providers in it
2024-01-10 10:04:47 +01:00
Andrea Barbasso
d1e986a443 [DURACOM-191] change routing for community-list-page, delete community-list-page.module.ts 2024-01-10 09:58:08 +01:00
Andrea Barbasso
6e70aad7b8 [DURACOM-191] change routing for home-page, delete home-page.module.ts 2024-01-10 09:46:03 +01:00
FrancescoMolinaro
d16bf49268 add tests 2024-01-09 21:48:27 +01:00
FrancescoMolinaro
3149842bcb add data services tests 2024-01-09 16:23:09 +01:00
FrancescoMolinaro
eb8a8207d7 rename tab metrics 2024-01-09 12:45:56 +01:00
FrancescoMolinaro
7719626256 add link, rename tabs, small refactor 2024-01-09 12:33:49 +01:00
FrancescoMolinaro
b6245d3c2d improve responsivness 2024-01-09 12:11:53 +01:00
Andrea Barbasso
ba3bea812b [DURACOM-191] set providers in core folder
Missing some modules that always load entryComponents as "before Ivy" (e.g. look at eager-theme.module), need to check if they can be refactored
2024-01-09 12:04:09 +01:00
FrancescoMolinaro
f9474d4586 remove mobile adaption, adapt reprocess 2024-01-09 11:30:44 +01:00
Yury Bondarenko
5af4f89c54 Merge remote-tracking branch 'origin/main' into more-eslint 2024-01-09 10:08:02 +01:00
FrancescoMolinaro
e04c3e6df5 add check on device width for columns rendering 2024-01-09 10:01:57 +01:00
FrancescoMolinaro
728911899a clean up 2024-01-09 09:36:50 +01:00
Andrea Barbasso
ac366bcc54 [DURACOM-191] remove SharedModule 2024-01-09 09:23:20 +01:00
Alexandre Vryghem
359d4520f9 Merge remote-tracking branch 'upstream/main' into feature-process_polling
# Conflicts:
#	src/app/core/data/base/base-data.service.spec.ts
2024-01-08 18:36:33 +01:00
FrancescoMolinaro
7ce0bd0ce6 remove unused method 2024-01-08 18:27:43 +01:00
FrancescoMolinaro
937abc0d50 clean up 2024-01-08 18:14:16 +01:00
Alexandre Vryghem
86d3883853 Merge branch 'feature-process_polling-7.6' into feature-process_polling 2024-01-08 18:07:42 +01:00
FrancescoMolinaro
c20d798502 clean up, fix tests, add missing translations 2024-01-08 18:04:49 +01:00
Tim Donohue
610062bdea Merge pull request #2731 from DSpace/backport-2670-to-main
[Port main] Ensure HALEndpointService doesn't use stale responses
2024-01-08 11:04:14 -06:00
Art Lowel
33b7c39dd1 add ResponsePendingStale state
(cherry picked from commit 790e717199)
2024-01-08 16:17:16 +00:00
Art Lowel
d109a5aeb2 also skip loading hal requests
(cherry picked from commit c8ac260b78)
2024-01-08 16:17:16 +00:00
Art Lowel
bd6c99da2e ensure HALEndpointService doesn't use stale requests
(cherry picked from commit 38752d9d71)
2024-01-08 16:17:16 +00:00
FrancescoMolinaro
c57ad8eeb7 refactor, add detail mapping, add missing translation, optimize modal 2024-01-08 16:47:24 +01:00
frabacche
dfe4b15539 CST-12868 remove Outbound Patterns 2024-01-08 16:01:12 +01:00
FrancescoMolinaro
4d30bc7035 separate results, config routes, add data mapping, add service 2024-01-08 13:07:07 +01:00
FrancescoMolinaro
7185c5548a fix test dependencies, move components in two tabs 2024-01-05 17:39:00 +01:00
Alexandre Vryghem
c91b99fece 108915: Fixed delete process error on ProcessDetailComponent 2024-01-05 16:53:05 +01:00
Alexandre Vryghem
9e31f73f5e 108915: Fixed issue where the observable would emit itself again even when the lastUpdated didn't change 2024-01-05 16:51:23 +01:00
FrancescoMolinaro
af3bfee74e fix test dependencies 2024-01-05 15:56:58 +01:00
FrancescoMolinaro
e9159ce4e8 fix lint 2024-01-05 13:29:16 +01:00
FrancescoMolinaro
4292c01d8f clean up 2024-01-05 13:22:17 +01:00
FrancescoMolinaro
6254efa084 add modal, refine table,refactor, fix lint 2024-01-05 12:54:35 +01:00
Jens Vannerum
74f1c613f6 110088: lint issue 2024-01-04 17:40:08 +01:00
Jens Vannerum
4bbfb4cc1a 110088: new implementation for keyboard support in dropdowns 2024-01-04 17:29:28 +01:00
FrancescoMolinaro
c434c06b04 refactor, fix lint 2024-01-04 17:23:29 +01:00
FrancescoMolinaro
566c8b34a9 remove log 2024-01-04 17:21:03 +01:00
FrancescoMolinaro
b8e353d51c add tabulatable loader missing files 2024-01-04 17:16:21 +01:00
FrancescoMolinaro
de8c0c9528 add tabulatable loader and related configuration 2024-01-04 17:15:25 +01:00
FrancescoMolinaro
9386536ca9 fix lint 2024-01-04 14:37:01 +01:00
FrancescoMolinaro
9617329683 add new dependencies for facets filters isolation 2024-01-04 13:22:53 +01:00
FrancescoMolinaro
d72f522f63 add header translations 2024-01-03 18:01:40 +01:00
FrancescoMolinaro
457c3b351f refactor, decouple pagination and configuration 2024-01-03 17:27:38 +01:00
FrancescoMolinaro
c4b8eaba72 add base config 2024-01-02 14:47:15 +01:00
FrancescoMolinaro
3616e66155 refactor fn name 2024-01-02 10:28:44 +01:00
FrancescoMolinaro
7d3dbc3e46 add validation message
for ldnUrl already associated
2024-01-02 10:26:27 +01:00
FrancescoMolinaro
664dd855ea fix lint 2023-12-29 15:19:03 +01:00
FrancescoMolinaro
cd4b6a4169 refactor, align config with rest 2023-12-29 15:09:37 +01:00
FrancescoMolinaro
055cc50cfb reduce inner space 2023-12-29 12:49:31 +01:00
FrancescoMolinaro
6a41dce3be fix tests 2023-12-29 12:45:47 +01:00
FrancescoMolinaro
a43cb352fd fix lint 2023-12-29 12:25:54 +01:00
FrancescoMolinaro
01a41570d6 fix lint, fix aggregate count, add translations 2023-12-29 12:23:45 +01:00
FrancescoMolinaro
fec6550cba finalize config and mapping 2023-12-29 11:45:13 +01:00
FrancescoMolinaro
e39e034329 update config and integrate rest 2023-12-28 17:06:27 +01:00
FrancescoMolinaro
1f1d594316 merge from github 2023-12-27 13:34:37 +01:00
FrancescoMolinaro
ffd118abf9 align with github 2023-12-27 13:26:04 +01:00
FrancescoMolinaro
5feaa1b5a6 fix tests 2023-12-27 13:00:55 +01:00
FrancescoMolinaro
24ad516c29 fix dropdown overflow and bar z-index 2023-12-27 10:32:03 +01:00
Alexandre Vryghem
d37cd18e89 Merge remote-tracking branch 'upstream/main' into feature-process_polling 2023-12-22 19:29:52 +01:00
FrancescoMolinaro
5d8ba54676 add new component, add mock, refactor 2023-12-22 17:26:46 +01:00
Alexandre Vryghem
f35eab3740 Merge branch 'feature-process_polling-7.6' into feature-process_polling
# Conflicts:
#	src/app/core/data/processes/process-data.service.ts
2023-12-22 14:04:34 +01:00
frabacche
4106e438d6 CST-5249_suggestion test fixes 2023-12-22 12:18:58 +01:00
Francesco Molinaro
ad723a67cf Merged in CST-12914-ldn-breadcrumbs (pull request #1115)
CST-12914 ldn breadcrumbs

Approved-by: Giuseppe Digilio
2023-12-22 09:50:12 +00:00
FrancescoMolinaro
11bb71ea8a fix import spaces 2023-12-22 10:43:39 +01:00
FrancescoMolinaro
604fb4f3d1 add components, refactor sidebar section, add config 2023-12-22 10:42:38 +01:00
FrancescoMolinaro
7445591148 add route, renaming 2023-12-21 18:10:45 +01:00
FrancescoMolinaro
e1cda4fa04 prepare dashboard config 2023-12-21 18:06:39 +01:00
FrancescoMolinaro
6d16bf3037 add flush operation, fix lint, refactor 2023-12-21 16:08:13 +01:00
FrancescoMolinaro
e5cf16c489 fix lint 2023-12-21 14:54:02 +01:00
FrancescoMolinaro
128dd2f566 add mutliple request config 2023-12-21 14:46:11 +01:00
frabacche
108db1e0c5 CST-5249_suggestion alignment to main, merge conflicts 2023-12-21 12:44:12 +01:00
Alan Orth
7207bbbd0e src/app/item-page: remove newlines in links
The newlines cause whitespace to be enapsulated in the rendered link.
2023-12-20 22:34:07 +03:00
Alan Orth
7bedf7fc59 src/app/community-list-page: remove newlines in links
The newlines cause whitespace to be enapsulated in the rendered link.
2023-12-20 22:33:31 +03:00
Andrea Barbasso
031e12f76b [DURACOM-191] fix root component tests 2023-12-20 13:24:50 +01:00
Andrea Barbasso
a833639fb9 [DURACOM-191] fix configuration-search-page tests 2023-12-20 13:05:56 +01:00
Andrea Barbasso
75b8c095b7 [DURACOM-191] fix project-entry-import-modal tests 2023-12-20 12:35:43 +01:00
Andrea Barbasso
d389e665fe [DURACOM-191] fix admin-quality-assurance-source-page tests 2023-12-20 12:29:32 +01:00
Andrea Barbasso
900164952e [DURACOM-191] fix admin-quality-assurance-topics-page tests 2023-12-20 12:27:31 +01:00
Andrea Barbasso
84ea72021d [DURACOM-191] quality-assurance-topics tests 2023-12-20 12:25:19 +01:00
Andrea Barbasso
e98d0de0cd [DURACOM-191] quality-assurance-source tests 2023-12-20 12:15:33 +01:00
Andrea Barbasso
4850856aa6 [DURACOM-191] fix parameter-select.component tests 2023-12-20 12:12:53 +01:00
Vincenzo Mecca
199eaf8340 [CST-12825] Fixes comma in en.json5 2023-12-20 11:56:25 +01:00
Andrea Barbasso
20780fae20 [DURACOM-191] fix import 2023-12-20 11:56:06 +01:00
Andrea Barbasso
9474fcda8f [DURACOM-191] fix admin-quality-assurance-events-page test 2023-12-20 11:53:28 +01:00
Vincenzo Mecca
d979ea5c9f [CST-12825] Removed unecessary escape 2023-12-20 11:47:33 +01:00
Andrea Barbasso
fb979f0c19 [DURACOM-191] fix process-form tests 2023-12-20 11:42:05 +01:00
Andrea Barbasso
8b665a6d99 [DURACOM-191] run angular script for migration 2023-12-20 11:23:00 +01:00
Andrea Barbasso
3a461eee80 Merge branch 'main' into DURACOM-191-20231219-ALIGNMENT
# Conflicts:
#	src/app/access-control/epeople-registry/epeople-registry.component.html
#	src/app/access-control/epeople-registry/epeople-registry.component.ts
#	src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html
#	src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts
#	src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts
#	src/app/access-control/group-registry/group-form/group-form.component.html
#	src/app/access-control/group-registry/group-form/group-form.component.ts
#	src/app/access-control/group-registry/group-form/members-list/members-list.component.html
#	src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts
#	src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts
#	src/app/admin/admin-registries/metadata-schema/metadata-schema.component.ts
#	src/app/browse-by/browse-by.module.ts
#	src/app/community-list-page/community-list/community-list.component.spec.ts
#	src/app/core/core.module.ts
#	src/app/core/data/root-data.service.ts
#	src/app/core/eperson/eperson-data.service.ts
#	src/app/core/eperson/group-data.service.ts
#	src/app/curation-form/curation-form.component.ts
#	src/app/header/header.component.spec.ts
#	src/app/header/header.component.ts
#	src/app/health-page/health-page.component.ts
#	src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.html
#	src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.spec.ts
#	src/app/item-page/edit-item-page/item-status/item-status.component.ts
#	src/app/process-page/form/process-parameters/parameter-select/parameter-select.component.spec.ts
#	src/app/process-page/form/process-parameters/parameter-value-input/boolean-value-input/boolean-value-input.component.spec.ts
#	src/app/register-email-form/register-email-form.module.ts
#	src/app/register-email-form/themed-registry-email-form.component.ts
#	src/app/shared/auth-nav-menu/auth-nav-menu.component.html
#	src/app/shared/auth-nav-menu/user-menu/themed-user-menu.component.ts
#	src/app/shared/browse-by/shared-browse-by.module.ts
#	src/app/shared/browse-by/themed-browse-by.component.ts
#	src/app/shared/dso-page/dso-edit-menu/dso-edit-expandable-menu-section/dso-edit-menu-expandable-section.component.ts
#	src/app/shared/dso-page/dso-edit-menu/dso-edit-menu-section/dso-edit-menu-section.component.ts
#	src/app/shared/file-download-link/file-download-link.component.spec.ts
#	src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts
#	src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.html
#	src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts
#	src/app/shared/lang-switch/lang-switch.component.ts
#	src/app/shared/lang-switch/themed-lang-switch.component.ts
#	src/app/shared/log-in/log-in.component.ts
#	src/app/shared/log-in/themed-log-in.component.ts
#	src/app/shared/menu/menu.component.spec.ts
#	src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.ts
#	src/app/shared/object-collection/shared/selectable-list-item-control/selectable-list-item-control.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-filter.component.html
#	src/app/shared/search/search-labels/search-label/search-label.component.html
#	src/app/submission/import-external/submission-import-external.component.ts
#	src/app/subscriptions-page/subscriptions-page.component.ts
#	src/app/workflowitems-edit-page/advanced-workflow-action/advanced-workflow-action-select-reviewer/reviewers-list/reviewers-list.component.spec.ts
#	src/themes/custom/app/entity-groups/research-entities/item-pages/person/person.component.ts
#	src/themes/custom/app/register-email-form/register-email-form.component.ts
#	src/themes/custom/app/shared/auth-nav-menu/user-menu/user-menu.component.ts
#	src/themes/custom/app/shared/browse-by/browse-by.component.ts
#	src/themes/custom/app/shared/lang-switch/lang-switch.component.ts
#	src/themes/custom/app/shared/log-in/log-in.component.ts
#	src/themes/custom/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts
2023-12-20 11:17:21 +01:00
Andrea Barbasso
59c30c531f [DURACOM-191] fix tests in section-upload-file-edit component 2023-12-19 16:07:14 +01:00
Alexandre Vryghem
5400981751 108915: Added tests proving that the addDependency is called on all the followLinks
Also fixed the mock values used by findListByHref, because they didn't use PaginatedLists but regular items
2023-12-19 15:58:11 +01:00
Vlad Nouski
6804ef1c28 [DURACOM-191] fix: dynamic-relation-group unit tests 2023-12-19 14:06:27 +01:00
Yury Bondarenko
968c23309a Manual lint fixes
This can serve as an example of the type of lint issues we'd have to fix by hand in ongoing PRs if this PR were to be merged.
2023-12-19 13:35:52 +01:00
Yury Bondarenko
ca1d81048a Autofix lint issues 2023-12-19 13:35:51 +01:00
Andrea Barbasso
f5e767afe1 [DURACOM-191] fix a test in dynamic-relation-group component 2023-12-19 09:48:11 +01:00
Yury Bondarenko
4a87c9a8b4 Merge remote-tracking branch 'origin/main' into more-eslint 2023-12-19 09:35:05 +01:00
Alexandre Vryghem
e071b7ffa7 Merge branch 'generify-component-loaders_contribute-7.6' into generify-component-loaders_contribute-main 2023-12-19 01:22:57 +01:00
Davide Negretti
b8ec74a046 [DURACOM-195] [DURACOM-210] Reorder SCSS and CSS theme variables 2023-12-19 01:10:07 +01:00
Davide Negretti
216558125b [DURACOM-195] Header, navbar, and sidebar refactoring 2023-12-19 01:10:07 +01:00
Jean-François Morin
ef651a3686 Resolved conflicts 2023-12-18 16:32:43 -05:00
Tim Donohue
92a10ce9de Merge pull request #1562 from 4Science/CST-5337
Enrich local data via the OpenAIRE Graph
2023-12-18 13:52:56 -06:00
FrancescoMolinaro
56cf0e1451 fix lint 2023-12-18 17:47:19 +01:00
Andrea Barbasso
5872222444 [DURACOM-191] fix some tests, find broken assertions 2023-12-18 17:14:36 +01:00
FrancescoMolinaro
92870452ae add spaces on import 2023-12-18 12:38:28 +01:00
FrancescoMolinaro
c386a4d505 adapt config for url search 2023-12-18 12:22:05 +01:00
Francesco Molinaro
9351b582cc Merged in CST-12904-breadcrumbs-qa (pull request #1107)
CST-12904 breadcrumbs qa

Approved-by: Giuseppe Digilio
2023-12-18 11:11:01 +00:00
FrancescoMolinaro
fd4ca45181 merge CST-5337 2023-12-18 12:10:15 +01:00
FrancescoMolinaro
2d2ca2016a refactor 2023-12-18 11:40:09 +01:00
Andrea Barbasso
113d95d57a [DURACOM-191] fix item-access-control-select-bitstreams-modal component test 2023-12-18 10:15:02 +01:00
FrancescoMolinaro
a483b3068b resolve conflicts 2023-12-18 10:06:33 +01:00
Francesco Molinaro
8c6daf744d Merged in CST-12863-CST-12940-ldn-services-improvments (pull request #1118)
CST-12863 CST 12940 ldn services improvments

Approved-by: Stefano Maffei
2023-12-18 08:31:35 +00:00
Francesco Molinaro
339225ee21 Merged in CST-12915-edit-message-ldn (pull request #1110)
update translations

Approved-by: Stefano Maffei
2023-12-18 08:28:18 +00:00
Francesco Molinaro
b3a345af33 Merged in CST-12906-accessibility-qa (pull request #1108)
add accessibility text

Approved-by: Stefano Maffei
2023-12-18 08:24:50 +00:00
Francesco Molinaro
71783bb951 Merged in CST-12908-remove-console-log (pull request #1109)
remove console logs

Approved-by: Stefano Maffei
2023-12-18 08:23:09 +00:00
Alexandre Vryghem
7942c900f4 Component doesn't have to be initialised anymore in ngOnChanges because connectInputsAndOutputs will automatically call the child component's ngOnChanges already 2023-12-17 16:17:16 +01:00
Alexandre Vryghem
230adc16c8 Fixed failing EPeopleRegistryComponent test
Buttons to which you don't have access are now hidden by default instead of disabled
2023-12-15 23:43:10 +01:00
Alexandre Vryghem
aa42a7eea3 Merge remote-tracking branch 'upstream/main' into fix-specs-without-expectations_contribute-main
# Conflicts:
#	src/app/shared/browse-by/shared-browse-by.module.ts
2023-12-15 22:02:18 +01:00
FrancescoMolinaro
f7be255345 add QA authorization config 2023-12-15 15:20:39 +01:00
Vincenzo Mecca
56d33387b5 [CST-12825] Fixes ROR metadata display 2023-12-15 10:05:03 +01:00
FrancescoMolinaro
e280666dc3 variable rename 2023-12-15 09:46:53 +01:00
FrancescoMolinaro
3177a8c581 add notification on patterns delete, small restyle 2023-12-15 09:32:33 +01:00
Andrea Barbasso
ca5cca384d [DURACOM-191] fix item-access-control component test 2023-12-14 17:53:56 +01:00
Andrea Barbasso
ae452b3388 [DURACOM-191] fix community-access-control component test 2023-12-14 17:48:26 +01:00
Andrea Barbasso
e1001d4346 [DURACOM-191] fix collection-access-control component test 2023-12-14 17:37:45 +01:00
Tim Donohue
38733e6e94 Merge pull request #2707 from reetagithub/patch-1
Update fi.json5
2023-12-14 10:15:46 -06:00
Andrea Barbasso
96c0e1241e [DURACOM-191] fix epeople-registry test 2023-12-14 17:13:06 +01:00
Andrea Barbasso
e262bcabf9 [DURACOM-191] fix header component imports 2023-12-14 17:08:37 +01:00
Andrea Barbasso
4c1f488356 [DURACOM-191] fix various tests
now tests are not breaking anymore, but there are some excluded tests that need to be fixed after everything else.
2023-12-14 16:51:32 +01:00
FrancescoMolinaro
6a202bd685 fix rest, add initialization param 2023-12-14 16:46:02 +01:00
FrancescoMolinaro
8e4408f118 fix lint 2023-12-14 16:30:29 +01:00
FrancescoMolinaro
90ad316db4 fix issue with edit and creation, unify files, refactor 2023-12-14 16:25:02 +01:00
Vincenzo Mecca
e7f5c48d8c [CST-12825] Ror integration 2023-12-14 15:44:19 +01:00
FrancescoMolinaro
f2be578d2a refactor config QA 2023-12-14 12:11:06 +01:00
FrancescoMolinaro
f2a2433bf3 fix lint 2023-12-14 11:34:49 +01:00
FrancescoMolinaro
86ccd8ade7 finalize resolver, service and tests 2023-12-14 11:30:50 +01:00
Andrea Barbasso
bdf1855b12 Revert "[DURACOM-191] fix browse-by-date-page.component.spec.ts"
This reverts commit 30a9a95bf4.
2023-12-14 10:26:24 +01:00
Andrea Barbasso
63521f0121 Revert "[DURACOM-191] fix various tests"
This reverts commit 1b158dd5f6.
2023-12-14 10:26:20 +01:00
FrancescoMolinaro
f4d0217862 add navigation resolver config 2023-12-13 18:13:26 +01:00
FrancescoMolinaro
763311ed29 add navigation resolver config 2023-12-13 18:13:00 +01:00
Andrea Barbasso
1b158dd5f6 [DURACOM-191] fix various tests 2023-12-13 17:55:57 +01:00
FrancescoMolinaro
7fe81ec111 add quality assurance configuration 2023-12-13 17:00:24 +01:00
Andrea Barbasso
30a9a95bf4 [DURACOM-191] fix browse-by-date-page.component.spec.ts 2023-12-13 15:18:44 +01:00
FrancescoMolinaro
b494c9551e add base resolver 2023-12-13 14:55:40 +01:00
FrancescoMolinaro
e77c5595ed update translations 2023-12-13 12:56:00 +01:00
FrancescoMolinaro
8113946f58 remove console logs 2023-12-13 12:38:38 +01:00
Andrea Barbasso
391ae16103 [DURACOM-191] fix linting 2023-12-13 12:11:31 +01:00
FrancescoMolinaro
922172d9ae refactor, improve code according to suggestions 2023-12-13 11:55:10 +01:00
Yury Bondarenko
47875dd1a8 Fix stray lint issue 2023-12-13 11:00:20 +01:00
FrancescoMolinaro
78df2362c9 add accessibility text 2023-12-13 10:45:20 +01:00
FrancescoMolinaro
53329cd92f align to branch, update test 2023-12-13 10:34:20 +01:00
Yury Bondarenko
544306af30 Merge remote-tracking branch 'origin/main' into more-eslint 2023-12-12 18:32:22 +01:00
FrancescoMolinaro
b9af731d5a rename data service 2023-12-12 17:17:23 +01:00
FrancescoMolinaro
b9085d5306 add QA breadcrumb resolver and service 2023-12-12 09:34:40 +01:00
FrancescoMolinaro
eed9896062 add qa breadcrumb 2023-12-12 09:34:36 +01:00
Alexandre Vryghem
d8be26dcc8 Merge branch 'generify-component-loaders_contribute-7.6' into generify-component-loaders_contribute-main 2023-12-12 01:39:39 +01:00
Alexandre Vryghem
a5b7e2a40f Fixed circular dependency by extracting BrowseByDataType to a separate file 2023-12-12 01:39:12 +01:00
Alexandre Vryghem
19384dfdb1 Merge remote-tracking branch 'upstream/main' into generify-component-loaders_contribute-7.6
# Conflicts:
#	src/app/shared/shared.module.ts
2023-12-12 01:00:18 +01:00
Alexandre Vryghem
e6bf2f0ca7 Fixed bug in BrowseService where findListByHref was called with null instead of undefined, which prevented its default values from being used 2023-12-12 00:59:43 +01:00
Alexandre Vryghem
a83d69ee0e Created BrowseByPageComponent that uses the new refactored BrowseBySwitcherComponent extending AbstractComponentLoaderComponent
- Added the context to the rendersBrowseBy decorator
- Created AbstractBrowseByTypeComponent that is extended by all the browse type sections
2023-12-12 00:59:42 +01:00
Thomas Misilo
7b8962a7cd Update "E-mail" to be "Email" for consistency 2023-12-11 22:21:53 +03:00
Alexandre Vryghem
24eb5b4bc0 108915: Always invalidate all followLinks when invalidating linked cached object 2023-12-11 13:25:46 +01:00
Vlad Nouski
1f15b21ba9 [DURACOM-191] align with main branch and migrated to be standalone new components 2023-12-11 11:08:22 +01:00
reetagithub
2339a559e9 Update fi.json5
Corrected a typo  in search.view.switch.show-grid
2023-12-11 11:39:38 +02:00
Alexandre Vryghem
14d42b0d93 Fixed getComponent not triggering again when it's input values change by creating inputNamesDependentForComponent
Also simplified the way @Input() values are passed to their child component and how ngOnChanges is called by using setInput instead
2023-12-11 01:02:24 +01:00
Alexandre Vryghem
26e0bc81c7 Removed Themed components from PR 1842 because those should be themed using the decorator rendersBrowseBy(browseType, themeName) and fixed the components in the custom theme folder
- Removed ngOnDestroy from BrowseByTitlePageComponent because super.ngOnDestroy already included the unsubscribe functionality
- Removed BrowseBySwitcherComponent since a themed version of that isn't really useful
2023-12-11 01:02:24 +01:00
Alexandre Vryghem
2bae174350 Make MetadataRepresentationLoaderComponent extend AbstractComponentLoaderComponent 2023-12-10 12:36:22 +01:00
Alexandre Vryghem
774784a9b9 Make AdvancedWorkflowActionsLoaderComponent extend AbstractComponentLoaderComponent 2023-12-10 12:36:22 +01:00
Alexandre Vryghem
fe60adb47f Make ClaimedTaskActionsLoaderComponent extend AbstractComponentLoaderComponent 2023-12-10 02:37:05 +01:00
Alexandre Vryghem
fb7afaddd0 Make ListableObjectComponentLoaderComponent extend AbstractComponentLoaderComponent 2023-12-10 02:35:44 +01:00
Alexandre Vryghem
2327513dd0 Created AbstractComponentLoaderComponent to load components dynamically 2023-12-10 02:03:32 +01:00
Alan Orth
d6c46847c2 src/assets/i18n/en.json5: minor changes for consistency
Fix some random capitalizations and strange wording.
2023-12-08 23:28:32 +03:00
Alan Orth
f78f4b45fc src/assets/i18n/en.json5: minor updates for consistency
We should be capitalizing acronyms and project-specific nouns like
MyDSpace consistently in our interface.
2023-12-08 23:28:18 +03:00
Alan Orth
98241d8925 src/assets/i18n: change "controller" to "reviewer"
Reviewer is a less obscure term for what this actually is in most
cases.
2023-12-08 23:27:54 +03:00
Vlad Nouski
8a7b459695 [DURACOM-191] fix: unit-tests 2023-12-08 12:54:41 +01:00
Davide Negretti
d59f33278d [DURACOM-195] Base components (simplify header-navbar-wrapper) 2023-12-06 17:44:57 +01:00
Davide Negretti
e84773afb2 [DURACOM-195] Base components 2023-12-06 17:44:53 +01:00
Davide Negretti
640612033c [DURACOM-195] New host-window service methods 2023-12-06 17:43:43 +01:00
Tim Donohue
8ba14aa3be Merge pull request #2683 from alexandrevryghem/fix-accessibility-issues_contribute-main
Fix accessibility issues
2023-12-06 10:03:01 -06:00
Vlad Nouski
5b9250abf7 [DURACOM-191] fix: listable-object-loader tests 2023-12-06 14:57:13 +01:00
Alexandre Vryghem
4545b5354d Fixed Edit Item Bitstream showing empty buttons when css is turned off & replaced authentication dropdown menu link with button
- Also fixed box-shadows not working the same way for all footer buttons/links
2023-12-06 02:03:44 +01:00
Alexandre Vryghem
ff6dff72d8 Fixed Edit Item Bitstream showing empty buttons when css is turned off 2023-12-06 01:08:00 +01:00
Thomas Misilo
09fc44a539 Decrease min-height for Login Dropdown Menu
Fixes #2690

This is noticable when you have disabled local authentication, and only have a singular remote authentication such as Shibboleth or Orcid.
2023-12-05 10:02:17 -06:00
frabacche
62ae9226f5 COAR alert box fix notiyrequeststatus object with the new attribute offerType 2023-12-05 16:34:23 +01:00
Stefano Maffei
486d2ad8c1 [CST-12881] fix typo 2023-12-05 15:29:59 +01:00
Stefano Maffei
a8ad88a2c5 [CST-12881] fix lint error 2023-12-05 15:07:05 +01:00
Stefano Maffei
4b7602fe7d Merge remote-tracking branch 'originGithub/main' into coar-notify-7
# Conflicts:
#	src/app/shared/shared.module.ts
#	src/assets/i18n/en.json5
#	src/modules/app/browser-init.service.ts
#	src/themes/custom/lazy-theme.module.ts
2023-12-05 13:10:25 +01:00
Tim Donohue
08df2ea1ac Merge pull request #2684 from mspalti/label-fix
Updated bitstream read description in en.json5
2023-12-04 15:08:12 -06:00
Michael Spalti
2592f87356 Updated bitstream read description in en.json5 2023-12-04 11:37:14 -08:00
Vlad Nouski
9be8254fbd [DURACOM-191] fix: section-license unit-tests 2023-12-04 18:31:23 +01:00
Vlad Nouski
79389009dc [DURACOM-191] unit test fix 2023-12-04 09:10:41 +01:00
Alexandre Vryghem
e96d3cd2e9 Fixed DSONameService getName & getHitHighlights returning empty string for OrgUnits without organization.legalName field 2023-12-03 23:49:42 +01:00
Alexandre Vryghem
9a5ad0bc9f Fixed accessibility issues on search page 2023-12-03 23:49:42 +01:00
Alexandre Vryghem
5566c99625 Fixed accessibility issues about header ordering 2023-12-03 23:49:42 +01:00
Alexandre Vryghem
30fe424cc5 Fixed browse by vocabulary treeview accessibility issues 2023-12-03 23:49:41 +01:00
Alexandre Vryghem
8ba17c991e Fixed item page accessibility issues 2023-12-03 23:49:41 +01:00
Alexandre Vryghem
1db83ba3c5 Fix collection mapper accessibility issues
- Added missing aria-labels to input checkboxes
- Fixed multiple tab related accessibility issues
2023-12-03 15:57:49 +01:00
Alexandre Vryghem
5b21d14583 Fix item mapper accessibility issues
- Added missing aria-labels to input checkboxes
- Fixed role="tablist" not having direct role="tab" by adding role="presentation" on the li elements
2023-12-03 15:57:49 +01:00
Alexandre Vryghem
09bf8af03c Fix resource policy accessibility issues 2023-12-03 15:57:49 +01:00
Alexandre Vryghem
57cc34cdd1 Fix Bitstream format accessibility issues
- Added missing aria-labels to input checkboxes
- Fixed minor css alignment
2023-12-03 15:57:49 +01:00
Alexandre Vryghem
120835cfcd Fix Process detail accessibility issues
- Made Process Output keyboard accessible
2023-12-03 15:57:48 +01:00
Alexandre Vryghem
52c0977489 Fix Create a new process page accessibility issues
- Added missing aria-label to delete buttons
- Moved hardcoded translation to translation files
- Fix color contrast issues on buttons
- Fix minor alignment issues
- Added missing aria labels to input and select elements
2023-12-03 15:57:48 +01:00
Alexandre Vryghem
4ea487cc0d Fix Processes Overview accessibility issues
- Added missing aria-label to delete button
2023-12-03 15:57:48 +01:00
Alexandre Vryghem
3bdfc386e9 Fix System-wide Alert accessibility issues
- Added missing aria-label to buttons
2023-12-03 15:57:48 +01:00
Alexandre Vryghem
3628c84844 Fix Health Panel usability issues
- Fixed aria-controls on HealthComponentComponent, HealthInfoComponent, HealthInfoComponentComponent, HealthPanelComponent pointing to non-existing id
- Fixed the tabs not having the role tab on its tabs
- Fixed aria-expanded being set to true when collapsed and backwards for HealthPanelComponent & HealthInfoComponent
- Fixed role="tablist" not having direct role="tab" by adding role="presentation" on the li elements
- Fixed minor alignment issues
2023-12-03 15:57:48 +01:00
Alexandre Vryghem
c300123b78 Fix Bulk Access Management usability issues
- Added aria-labels to trash & select bitstream icon
- Added aria-labels ui-switch components (had to upgrade ngx-ui-switch to 14.1.0)
- Fixed aria-controls pointing to non-existing ids
- Fixed bulk-access-browse not having the tab role on it's tabs
- Fixed role="tablist" not having direct role="tab" by adding role="presentation" on the li elements
- Fixed aria-expanded being set to true when collapsed and backwards for BulkAccessBrowseComponent & BulkAccessSettingsComponent
2023-12-03 15:57:47 +01:00
Tim Donohue
81048599d7 Merge pull request #2673 from mspalti/thumbnail-fix
Fix for loading thumbnail images for Items when MediaViewer enabled
2023-11-30 14:14:20 -06:00
Alexandre Vryghem
e339b46228 108915: Added the missing Filetypes followLink to Process 2023-11-30 17:56:34 +01:00
Alexandre Vryghem
6b0f2e7c44 108915: Refactored code to use followLinks to retrieve the files of a process instead of a second request 2023-11-30 17:36:40 +01:00
Francesco Bacchelli
48670ddf6f Merged in CST-12822-coar-display-pattern-type (pull request #1075)
CST-12822 notify request box with offerType
2023-11-30 14:09:29 +00:00
frabacche
f4436a07e9 CST-12822 notify request box with offerType 2023-11-30 15:05:35 +01:00
Alisa Ismailati
71bc0192e3 [CST-12791] changed the route for suggestions to be be accessible to any logged in user 2023-11-30 13:01:46 +01:00
Michael Spalti
88c39e8b26 Fix for thumbnail images in items.
Revert changes in html template

Revert changes in html template

Revert changes in html template
2023-11-29 17:57:44 -08:00
Tim Donohue
474b70b845 Merge pull request #2666 from tdonohue/use_reusable_docker_build
[Docker] Refactor to simply use the reusable-docker-build.yml from DSpace/DSpace.
2023-11-29 09:26:53 -06:00
Tim Donohue
b30fd4bb0b Remove unused env variables 2023-11-28 16:50:55 -06:00
Tim Donohue
fce5cdcc72 Refactor to simply use the reusable-docker-build.yml from DSpace/DSpace. 2023-11-28 16:11:09 -06:00
Mykhaylo Boychuk
101cd0a553 [CST-12109] improvement code 2023-11-28 00:17:04 +01:00
Alexandre Vryghem
cc71d60ebb Fixed duplicate search-navbar-container id & renabled accessibility tests for header 2023-11-26 02:18:54 +01:00
Alexandre Vryghem
8a7bdfe18f Added aria-labels & fixed aria-controls on myDSpace page
- Added aria-label to the new submission buttons (for repositories with more than 1 entity type)
- Fixed aria-controls pointing to non-existing ids
2023-11-26 02:18:54 +01:00
Alexandre Vryghem
9ab30231aa Replaced uncrawlable links with buttons 2023-11-26 02:18:54 +01:00
Alexandre Vryghem
e229be44d6 Added missing aria labels to edit bitstream tab 2023-11-25 14:21:57 +01:00
Mykhaylo Boychuk
1c3776b2d3 [CST-12109] porting missing code 2023-11-24 11:28:38 +01:00
Alexandre Vryghem
430ee3846a Fixed ePerson link on edit group page 2023-11-23 21:29:23 +01:00
Alisa Ismailati
1153c906ac Merged in coar-CST-12768 (pull request #1053)
[CST-12768] LDN Service edit "Automatic" flag fixed

Approved-by: Stefano Maffei
2023-11-23 14:06:00 +00:00
Alisa Ismailati
117415230b [CST-12768] small change 2023-11-23 15:05:08 +01:00
Alisa Ismailati
e74376c0bc Merged in coar-CST-12767 (pull request #1054)
[CST-12767] Fixed Modal behaviour when discarding a LDN service

Approved-by: Stefano Maffei
2023-11-23 14:04:59 +00:00
Alisa Ismailati
86bbd26145 [CST-12768] unit test fix 2023-11-23 15:04:36 +01:00
Alisa Ismailati
d257380670 [CST-12768] other fixes 2023-11-23 14:42:49 +01:00
Alisa Ismailati
8256de50a1 [CST-12767] Fixed Modal behaviour when discarding a LDN service 2023-11-23 12:14:30 +01:00
Alisa Ismailati
867bf8aa23 [CST-12768] LDN Service edit "Automatic" flag fixed 2023-11-23 11:35:02 +01:00
Alisa Ismailati
27d891a88c Merged in coar-CST-12709 (pull request #1051)
Coar CST-12709

Approved-by: Stefano Maffei
2023-11-23 09:01:23 +00:00
Alan Orth
727df563b8 Merge pull request #2652 from alexandrevryghem/deslect-metadata-fields-from-different-shemas_contribute-main
Deselect all metadata fields when switching between metadata schemas in registry
2023-11-23 07:57:17 +03:00
Tim Donohue
7e2f23aacd Merge pull request #2658 from tdonohue/quicker_redeploy
Redeploy demo/sandbox more quickly by only waiting for AMD64 Docker image to complete
2023-11-22 11:30:46 -06:00
Alisa Ismailati
dbdfcea06a [CST-12718] Added validations on LDN Service form 2023-11-22 17:58:11 +01:00
Tim Donohue
ff677a83d6 Redeploy demo/sandbox more quickly by only waiting for AMD64 image 2023-11-22 10:51:28 -06:00
Alisa Ismailati
75ac289ff6 [CST-12709] chages 2023-11-22 16:20:15 +01:00
Alisa Ismailati
df0f1920dc [CST-12709] unit tests and final fixes 2023-11-22 15:20:59 +01:00
Tim Donohue
4f340d8870 Merge pull request #2649 from tdonohue/update_gh_actions
GitHub Actions Updates: Redeploy Sandbox via Docker & Fix retry of Codecov upload
2023-11-21 13:41:40 -06:00
Tim Donohue
fd645452a5 Trigger redeploy of demo/sandbox from GitHub Actions after DockerHub image updated. 2023-11-21 12:15:42 -06:00
Alisa Ismailati
442426bb2e [CST-12709] partial implementation with mock data 2023-11-21 18:38:49 +01:00
Jean-François Morin
6d9768ec81 Updated to latest version from main branch 2023-11-21 09:36:34 -05:00
Alisa Ismailati
f8215e7b17 Merged in coar-CST-11884 (pull request #1038)
[CST-11884] added notify related information in the item details page

Approved-by: Stefano Maffei
2023-11-21 13:49:00 +00:00
Alisa Ismailati
d46248e99b [CST-11884] added notify related information in the item details page 2023-11-20 16:48:14 +01:00
Mattia Vianelli
596cd6f7e3 Merged in CST-12180 (pull request #950)
CST-12180 added div container

Approved-by: Stefano Maffei
2023-11-20 15:38:54 +00:00
Mattia Vianelli
a495a291ea CST-12180 2023-11-20 16:21:25 +01:00
Mattia Vianelli
d7db4bc79f Merged in CST-12498 (pull request #1037)
CST-12498 fix tests errors and lint errors

Approved-by: Stefano Maffei
2023-11-20 14:54:36 +00:00
Mattia Vianelli
53599491e1 CST-12498 lint fix 2023-11-20 15:43:29 +01:00
Mattia Vianelli
4a9bfb0346 CST-12498 rollback 2023-11-20 15:39:17 +01:00
Stefano Maffei
56becbcc2c Merge remote-tracking branch 'origin/CST-12498' into coar-notify-7 2023-11-20 15:17:28 +01:00
Stefano Maffei
b1d2b78b40 [CST-12700] added missing label 2023-11-20 15:17:14 +01:00
Mattia Vianelli
3453f2615d Merge remote-tracking branch 'origin/CST-12498' into CST-12498 2023-11-20 15:16:15 +01:00
Mattia Vianelli
aa08989827 CST-12498 rollback 2023-11-20 15:15:58 +01:00
Alisa Ismailati
cb8f45575e Merged in coar-CST-12700 (pull request #1035)
[CST-12700] fixes

Approved-by: Stefano Maffei
2023-11-20 13:47:54 +00:00
Alisa Ismailati
21d9785ca7 [CST-12700] fixes 2023-11-20 13:52:13 +01:00
Mattia Vianelli
d8e67874d9 Merged coar-notify-7 into CST-12498 2023-11-20 12:35:33 +00:00
Mattia Vianelli
532f4725b2 CST-12498 Final refactor 2023-11-20 13:29:44 +01:00
Mattia Vianelli
36778a09cb CST-12498 Added last javadocs and removed console.log and comments 2023-11-20 13:27:39 +01:00
Mattia Vianelli
f35e6ae0dc CST-12498 Fixed tests for directory, no more failing ones 2023-11-20 13:09:42 +01:00
Mattia Vianelli
80991e6501 CST-12498 Fixed create form labels alignment 2023-11-20 10:11:01 +01:00
Alisa Ismailati
2386bb86b9 Merged in coar-CST-11523 (pull request #1029)
[CST-11523] footer component unit tests

Approved-by: Stefano Maffei
2023-11-20 07:30:08 +00:00
Alisa Ismailati
3a6d8f3d1b Merged in coar-CST-12147 (pull request #1028)
[CST-12147] display serive name & link for notify-coar events

Approved-by: Stefano Maffei
2023-11-20 07:29:06 +00:00
Alexandre Vryghem
ecca8286b5 108587: Deselect all fields on component destruction 2023-11-18 15:28:09 +01:00
Mattia Vianelli
f7ca7a688d CST-12498 Added javadocs 2023-11-18 01:13:17 +01:00
Mattia Vianelli
d1ae9adaac CST-12498 Added javadocs 2023-11-18 01:09:52 +01:00
Mattia Vianelli
bdd2a8bcab CST-12498 Tests added, need to check TODOs in ldn-services-directory.component.spec.ts 2023-11-17 21:19:38 +01:00
Mattia Vianelli
92b791ed15 CST-12498 Added tests, still needs cleanup and check on one behaviour 2023-11-17 20:34:28 +01:00
Tim Donohue
0d7d9f157d Merge pull request #2650 from tdonohue/fix_docker_manifests_again
Follow-up Docker image build fix. Fix directory structure for `latest-dist` digests
2023-11-17 12:05:19 -06:00
Tim Donohue
c99fe3a6c1 Fix directory structure for -dist digests 2023-11-17 11:56:48 -06:00
Tim Donohue
9f12ca3265 Update GH actions to latest versions. Fix bug in codecov to ensure it retries on error. 2023-11-17 11:05:46 -06:00
Mattia Vianelli
3c96abb64f CST-12498 Provided working mock, finishing the tests files 2023-11-17 17:23:35 +01:00
Alisa Ismailati
53ba509ff9 [CST-11523] footer component unit tests 2023-11-17 17:18:16 +01:00
Tim Donohue
99228d95e2 Merge pull request #2648 from tdonohue/fix_docker_manifest
Fix bug in Docker image build process which caused tags to have improper manifests in DockerHub
2023-11-17 10:08:43 -06:00
Tim Donohue
41eba0dc2f Fix bug in Docker manifest. Each build must use a separate artifact to store digests. Other minor cleanup & comments added. 2023-11-17 09:31:09 -06:00
Alisa Ismailati
7726183939 [CST-12147] display serive name & link for notify-coar events 2023-11-17 15:20:00 +01:00
Mattia Vianelli
5a7965d14a Merged in CST-12455_coar-7_sync (pull request #1024)
CST-12455 coar 7 sync

Approved-by: Stefano Maffei
2023-11-17 09:08:18 +00:00
Michael Spalti
6fff475a5a File edit component updated to work for forms without access conditions.
Fixed unit test

Fixed accidental reformatting
2023-11-16 19:50:55 -08:00
Mattia Vianelli
6c710b709a CST-12455_coar-7sync Removed console.log 2023-11-17 00:14:47 +01:00
Mattia Vianelli
1405e23f9c CST-12455_coar-7sync Removed console.log 2023-11-17 00:09:11 +01:00
Tim Donohue
b0deccc1bd Merge pull request #2644 from misilot/gha-matrix-docker-build
Setup the Docker GH Action for Matrix Building
2023-11-16 16:02:26 -06:00
Mattia Vianelli
5203a0106c CST-12455_coar-7sync Fixed 2 code rows 2023-11-16 22:25:50 +01:00
Mattia Vianelli
1ba925dd8f CST-12455_coar-7sync Wrongly added file removal 2023-11-16 22:21:04 +01:00
Mattia Vianelli
9651c74753 CST-12455_coar-7sync Failing tests fixed. Ready to be merged 2023-11-16 22:15:27 +01:00
Mattia Vianelli
bb3cbadfa4 CST-12455_coar-7sync Last refactor and TODO removal 2023-11-16 22:03:23 +01:00
Mattia Vianelli
64d9aba08c CST-12455_coar-7sync Small fix 2023-11-16 19:40:54 +01:00
Mattia Vianelli
6303d4c7e6 CST-12455_coar-7sync Refactor after branch sync 2023-11-16 19:36:56 +01:00
Mattia Vianelli
18367bab58 CST-12455_coar-7sync Refactor after branch sync 2023-11-16 19:36:28 +01:00
Mattia Vianelli
c8ece659eb Merge remote-tracking branch 'remotes/origin/coar-notify-7' into HEAD 2023-11-16 19:29:46 +01:00
Thomas Misilo
394dc67747 Setup the Docker GH Action for Matrix Building
This change enables building of the amd64 and arm64 images simultaneously.

Once both images finish, the manifest is sent to Docker Hub, allowing for a single image that has both the amd64/arm64 images.
2023-11-16 11:36:06 -06:00
Mattia Vianelli
7831bcbfae CST-12455 Working dropdowns with scroll showing only 4 values + improvement for form input fields 2023-11-16 18:25:09 +01:00
Alisa Ismailati
0361dd27f5 Merged in coar-CST-12608 (pull request #1007)
[CST-12608] qaevents page improvements

Approved-by: Stefano Maffei
2023-11-16 16:33:26 +00:00
Mattia Vianelli
400d10d43c Merged in CST-12179 (pull request #980)
CST-12179

Approved-by: Stefano Maffei
2023-11-16 15:56:33 +00:00
Mattia Vianelli
3a6f57d34d CST-12455 Task Completed, cleanup needed 2023-11-15 18:44:24 +01:00
Mattia Vianelli
00bf0d34b1 CST-12455 Code refactor and new dropdowns implementation 2023-11-15 16:05:53 +01:00
Mattia Vianelli
d8e7487eff CST-12455 Create page logic is working now finishing up the styling for the page 2023-11-15 13:19:14 +01:00
Mattia Vianelli
778a06724b CST-12455 Edit page logic is working now finishing up the styling for the page 2023-11-15 12:48:53 +01:00
Mattia Vianelli
35c45850e7 CST-12455 Itemfilter dropdown for the edit inboundpatterns is working, now changing all remaining code and finalizing task 2023-11-15 11:57:14 +01:00
Mattia Vianelli
754a6822a7 CST-12455 Itemfilter dropdown first refactoring just for layout 2023-11-15 11:29:59 +01:00
Mattia Vianelli
cd5d509160 CST-12455 Changes for the page to be using an ngbdropdown logic are now working 2023-11-14 17:33:56 +01:00
Mattia Vianelli
721b80a0e6 CST-12455 Changes for the page to be using an ngbdropdown logic are still required and being worked on 2023-11-14 13:13:39 +01:00
Tim Donohue
cb701909e5 Merge pull request #2610 from alexandrevryghem/menu-section-improvements_contribute-7.6
Fixed menu not updating when a new sub section is added after rendering has already completed
2023-11-13 16:29:45 -06:00
Tim Donohue
7ad5a5bc2c Merge pull request #2633 from vNovski/edit-item-view-random-order-of-buttons-in-status-tab
Edit-item view: random order of buttons in status tab
2023-11-13 16:05:27 -06:00
Tim Donohue
e99fff8c5f Merge pull request #2632 from vNovski/DURACOM-202-some-item-edit-pages-are-accessible-by-anonymous-users
Fixes "some item edit pages are accessible by anonymous users"
2023-11-13 14:34:24 -06:00
Tim Donohue
c515cb20ef Merge pull request #2562 from alexandrevryghem/theme-fixes_contribute-main
Fix match theme by handle with canonical prefix https://hdl.handle.net/ not working
2023-11-13 14:20:26 -06:00
Tim Donohue
55435a22fe Merge pull request #2579 from paulo-graca/bugfix/accessstatus-2402
adding new access-status-list-element-badge css classes
2023-11-13 11:30:12 -06:00
Tim Donohue
4bda8f43cc Merge pull request #2630 from alexandrevryghem/fix-routes-not-relative-to-basehref_contribute-main
Fix handle redirect not working with custom nameSpace
2023-11-13 11:22:36 -06:00
Mattia Vianelli
72727479c8 Merged coar-notify-7 into CST-12455 2023-11-13 14:43:04 +00:00
Alisa Ismailati
786e2ee43e [CST-12608] qaevents page improvements 2023-11-13 15:17:34 +01:00
Vlad Nouski
fbbbc18844 refactor: code 2023-11-13 15:02:27 +01:00
Vlad Nouski
35f8b55f58 fix: random order of buttons in status tab 2023-11-13 13:11:11 +01:00
Alexandre Vryghem
b894dce3b0 Fix handle redirect not working with custom nameSpace 2023-11-11 22:20:42 +01:00
Alexandre Vryghem
7529ed8b35 107671: Fixed theme matching by handle not working in production mode 2023-11-11 02:22:07 +01:00
Tim Donohue
497ab09cfa Merge pull request #2620 from atmire/w2p-108045_repeatable-date-fields-label-fix_contribute-main
Fix for repeatable date field labels
2023-11-10 16:38:13 -06:00
Tim Donohue
dfb0bbd694 Merge pull request #2625 from DSpace/dependabot/npm_and_yarn/axios-1.6.0
Bump axios from 0.27.2 to 1.6.0
2023-11-10 16:23:26 -06:00
Tim Donohue
2105e70d14 Merge pull request #2545 from tdonohue/fix_9052
Fix "Edit Group" page always requests all member Subgroups & EPersons
2023-11-10 13:03:30 -06:00
Mattia Vianelli
075c53a1f4 CST-12179 Using json5 and error are logged under the various sections if the form is not valid we simply close the modal 2023-11-10 20:00:42 +01:00
Mattia Vianelli
3aaeaa6397 CST-12179 Removed comments 2023-11-10 19:41:11 +01:00
Vlad Nouski
71af428a16 [CST-12044] refactor: styles 2023-11-10 18:13:09 +01:00
Tim Donohue
d0294759eb Merge pull request #2607 from alexandrevryghem/added-skip-to-main-content-button_contribute-main
Added skip to main content button
2023-11-10 10:51:15 -06:00
dependabot[bot]
ef9f31d3c6 Bump axios from 0.27.2 to 1.6.0
Bumps [axios](https://github.com/axios/axios) from 0.27.2 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v1.6.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-10 16:44:04 +00:00
Vincenzo Mecca
2429c3660b [DURACOM-204][#2622] Makes forgot-password link removable 2023-11-10 16:49:06 +01:00
Paulo Graça
75b788d05b adding ngOnDestroy for dealing with unsubscribe 2023-11-10 15:19:36 +00:00
Tim Donohue
f336a8b745 Merge pull request #2611 from TAMULib/tamu-main-community-list-updates
Communities & Collections tree browser updates - Replaced #2597
2023-11-10 09:14:13 -06:00
Vincenzo Mecca
91b4d3dcd1 [DURACOM-204][#2622] Makes forgot-password link removable 2023-11-10 16:13:20 +01:00
Vlad Nouski
6f64db1645 [DURACOM-202] refactor: code 2023-11-10 15:11:09 +01:00
Vlad Nouski
b6d515ff09 [DURACOM-202] refactor: code 2023-11-10 15:09:02 +01:00
lotte
0d39fe4cbc Merge branch 'w2p-108045_repeatable-date-fields-label-fix_contribute-7.4' into w2p-108045_repeatable-date-fields-label-fix_contribute-main 2023-11-10 14:42:37 +01:00
lotte
a3181a0689 Fixed test 2023-11-10 14:42:03 +01:00
Vlad Nouski
ccf1cc4547 [DURACOM-202] feature: item edit pages are accessible by administrator 2023-11-10 14:38:56 +01:00
Alisa Ismailati
f99f5d2414 Merged in coar-CST-12535 (pull request #990)
[CST-12535] GET quality assurance topics by source/target

Approved-by: Andrea Bollini
2023-11-10 13:17:25 +00:00
Alisa Ismailati
96cbd7323a Merged in coar-CST-12500 (pull request #991)
[CST-12500] coar-notify panel - new label keys for the dropdowns

Approved-by: Andrea Bollini
2023-11-10 13:10:04 +00:00
Alisa Ismailati
6675f9cd6a [CST-12535] improvements 2023-11-10 11:20:33 +01:00
Mattia Vianelli
f4b8c7f1f1 CST-12179 Removed comments 2023-11-10 10:44:39 +01:00
lotte
6e6cfbb86e 108045: Fix for repeatable date field labels 2023-11-10 10:37:52 +01:00
Mattia Vianelli
221a43d211 CST-12179 Provided json5 values 2023-11-10 10:21:39 +01:00
Mattia Vianelli
92330953bf CST-12455 Provided json5 key values based on the string arrays 2023-11-10 10:04:32 +01:00
Paulo Graça
c7eae9242a remove replaceAll and use an object property 2023-11-10 00:10:55 +00:00
Paulo Graça
6378dbec4a new accessStatusClass atribute 2023-11-10 00:09:46 +00:00
Paulo Graça
3bf2eb1997 Create new access-status-badge.component.scss 2023-11-10 00:04:45 +00:00
Alexandre Vryghem
19ed3c88e3 Merge remote-tracking branch 'upstream/main' into added-skip-to-main-content-button_contribute-main
# Conflicts:
#	src/app/root/root.component.html
2023-11-10 00:18:41 +01:00
Alexandre Vryghem
4e54cca600 107671: Fixed bug where config property would still sometimes be undefined whey calling the ngOnDestroy in the ThemedComponent 2023-11-10 00:10:06 +01:00
Alexandre Vryghem
da8880e5ba 107671: Split Theme model & ThemeConfig classes in separate files to prevent circular dependencies 2023-11-10 00:10:06 +01:00
Alexandre Vryghem
a7faf7d449 107671: Fix handle theme not working with canonical prefix https://hdl.handle.net/ 2023-11-10 00:10:06 +01:00
Tim Donohue
684846ae36 Merge pull request #2594 from 4Science/DURACOM-180
Media viewer controls rendered behind DSpace header
2023-11-09 16:44:12 -06:00
Tim Donohue
8a651ef307 Merge pull request #2574 from mahnkong/main
Support type-bind of elements based on repeatable list type-bound element (CHECKBOX_GROUP)
2023-11-09 16:03:54 -06:00
Tim Donohue
9117ac005f Address feedback. Run empty search on init. Reorder sections to list current members before add members (for both eperson and groups) 2023-11-09 13:57:04 -06:00
Tim Donohue
d163db13f2 Remove seemingly unnecessary page reload after new search. 2023-11-09 13:56:39 -06:00
Tim Donohue
2eb1a17e4e Refactor subgroups-list component's "search()" to act same as member-list component's "search()". Avoids reloading the page as frequently. 2023-11-09 13:56:39 -06:00
Tim Donohue
8a10888d2a Refactor members-list and subgroups-list components to use new isNotMemberOf endpoints (via services) 2023-11-09 13:56:39 -06:00
Tim Donohue
64f968b246 Fix subgroups-list specs so they align with new members-list specs 2023-11-09 13:56:39 -06:00
Tim Donohue
b598f1b5ca Also remove unnecessary EpersonDtoModel from extending ReviewersListComponent. Remove "memberOfGroup" from EpersonDtoModel as it is no longer used 2023-11-09 13:56:39 -06:00
Tim Donohue
bffae54b10 Remove unnecessary EpersonDtoModel. Rework code and tests to use EPerson instead. 2023-11-09 13:56:39 -06:00
Tim Donohue
43d37196fb Remove "isMemberOfGroup()" from members-list component. 2023-11-09 13:56:39 -06:00
Tim Donohue
229236634a Fix bug where linked Community/Collection info was sometimes listed many times in a row 2023-11-09 13:56:39 -06:00
Tim Donohue
97479a2945 Remove isSubgroupOfGroup() functionality as it loads every subgroup at once. Bad peformance for large groups 2023-11-09 13:56:39 -06:00
Tim Donohue
0da7c15f2e Limit getMembers() and getSubgroups() to only fetching one object. These lists are only used to find the size of each 2023-11-09 13:56:39 -06:00
Alan Orth
642d577cea Merge pull request #2602 from atmire/w2p-108055_freetext-values-in-vocabulary
Support for freetext values in submission for vocabulary controlled `onebox` and `tag` types
2023-11-09 21:56:23 +03:00
Tim Donohue
9a3d330d98 Merge pull request #2595 from TAMULib/develop
Add UI nameSpace context path to Mirador viewer path
2023-11-09 11:45:02 -06:00
Tim Donohue
f116bdf5d6 Merge pull request #2596 from 4Science/fix-submission-cache-bug
Fix cache issue when depositing a submission
2023-11-09 11:31:34 -06:00
William Welling
dc2ef989e6 Filter expanded nodes by id
Co-Authored-By: Art Lowel <1567693+artlowel@users.noreply.github.com>
2023-11-09 11:24:31 -06:00
William Welling
11d3771e72 Update condition to render show more node
`loadingNode` ends up being the current `node` after clicking it preventing it from rendering when more pages available.

Update community list component spec

Make the show more flat node id unique

The nodes with same id are conflicting when added to the tree. Clicking on the second with same id places the show more button under the wrong branch and expands the wrong page.
2023-11-09 11:11:08 -06:00
Davide Negretti
c042cd8d11 [DURACOM-180] Prevent header from covering media viewer controls (base theme) 2023-11-09 17:23:44 +01:00
Davide Negretti
0208a78437 [DURACOM-180] Prevent header from covering media viewer controls (dspace theme) 2023-11-09 17:23:44 +01:00
lotte
4e2d6d109e 108045: Fix for repeatable date field labels 2023-11-09 15:46:57 +01:00
Tim Donohue
7cf26e4815 Merge pull request #2606 from DSpace/backport-2590-to-main
[Port main] fix(pt-BR.json5): fix and update the language file
2023-11-09 08:40:02 -06:00
Davide Negretti
a3e6d9b09a [DURACOM-195] Simplify vertical spacing in header and breadcrumbs 2023-11-09 12:59:15 +01:00
Jens Vannerum
496c93fa5f Merge remote-tracking branch 'dspaceMain/main' into w2p-108055_freetext-values-in-vocabulary 2023-11-09 11:04:19 +01:00
Marco Aurelio Cardoso
07516985af fix(pt-BR.json5): fix and update the language file and previous errors
Fix and update the pt-BR language file and the previous errors

(cherry picked from commit 8b48a0b118)
2023-11-08 22:49:53 +00:00
Marco Aurelio Cardoso
0bcbd2f329 fix(pt-BR.json5): fix and update the language file
Fix and update the pt-BR language file

(cherry picked from commit e1494c0518)
2023-11-08 22:49:53 +00:00
Alexandre Vryghem
c4205163f9 Added skip to main content button 2023-11-08 23:43:17 +01:00
Tim Donohue
3cb623de72 Merge pull request #2364 from 4Science/DURACOM-177
Utility gap-* classes for flexbox
2023-11-08 16:15:12 -06:00
Tim Donohue
0c332561be Merge pull request #2603 from tdonohue/testing_e2e_fix
Fix e2e tests by running in production mode & using a user-agent for statistics
2023-11-08 12:24:52 -06:00
Tim Donohue
72cda41731 Specify user agent to avoid being detected as a "bot" by backend 2023-11-08 10:08:49 -06:00
Alexandre Vryghem
1bbc053c00 107902: Created test case for 2f26e686cc 2023-11-08 15:01:29 +01:00
Alisa Ismailati
8e70af6a58 [CST-12500] re-write translation keys based on pattern names 2023-11-08 14:34:18 +01:00
Jens Vannerum
aac58e612d 108055: add user input to tag list 2023-11-08 12:00:46 +01:00
Jens Vannerum
0dcf6cb885 108055: fix issue 8686: unable to enter freetext values in the submission form for vocabulary 2023-11-08 10:25:27 +01:00
Alisa Ismailati
8f290c17c1 [CST-12500] coar-notify panel - new label keys for the dropdowns 2023-11-07 16:40:41 +01:00
Mykhaylo Boychuk
5589c76f54 [CST-12109] porting of box thatallows undo of QAEvent request 2023-11-07 16:17:49 +01:00
Alisa Ismailati
4bd80c9951 [CST-12535] GET quality assurance topics by source/target 2023-11-07 15:28:04 +01:00
Alisa Ismailati
546c892eb1 [CST-12500] Unit tests SubmissionSectionCoarNotifyComponent 2023-11-06 17:27:49 +01:00
Tim Donohue
7dcaae8465 Ensure e2e tests run in production mode 2023-11-06 10:25:08 -06:00
Davide Negretti
a35629536e [DURACOM-177] Use gap-* classes on navbar buttons 2023-11-06 11:39:48 +01:00
Alisa Ismailati
dd0dfbab99 Merged in CST-12490 (pull request #983)
[CST-12490] unit test fixes

Approved-by: Andrea Bollini
2023-11-06 10:20:44 +00:00
Alexandre Vryghem
fdbcfd01fe Merge branch 'menu-section-improvements-7.4' into menu-section-improvements-7.6
# Conflicts:
#	src/app/shared/menu/menu.service.ts
2023-11-05 16:51:40 +01:00
Giuseppe Digilio
f7420391c9 Merge branch 'CST-12043-primary-bitstream-flag' into CST-12044-visualize-the-primary-bitstream 2023-11-03 22:13:04 +01:00
Giuseppe Digilio
9ce9606c38 [CST-12043] Fix layout 2023-11-03 22:12:00 +01:00
Giuseppe Digilio
034d7ddbbc [CST-12043] Remove ng-deep styles 2023-11-03 22:11:26 +01:00
Giuseppe Digilio
41eb34b7f4 [CST-12043] Add class for element container 2023-11-03 22:10:10 +01:00
Alisa Ismailati
9ffc778b52 Merged in coar-CST-12145 (pull request #967)
Coar CST-12145

Approved-by: Andrea Bollini
2023-11-03 20:52:53 +00:00
Tim Donohue
e62832f9d3 Merge pull request #2593 from 4Science/DURACOM-194
Fix "Submission input type date doesn't work properly once a value has been set"
2023-11-03 15:15:18 -05:00
Mattia Vianelli
2790f1e624 Merged in CST-12481 (pull request #979)
CST-12481

Approved-by: Andrea Bollini
2023-11-03 18:43:45 +00:00
Alisa Ismailati
bc267d21f5 Merged in CST-11045 (pull request #978)
CST-11045

Approved-by: Andrea Bollini
2023-11-03 18:28:59 +00:00
Alisa Ismailati
f74efd62a5 [CST-12490] unit test fixes 2023-11-03 17:54:45 +01:00
Mykhaylo Boychuk
ef1bd9fa3d [CST-12109] improve code 2023-11-03 12:18:19 +01:00
Mattia Vianelli
7fcd82a7e8 CST-12179 removed console.log 2023-11-03 11:28:47 +01:00
Mykhaylo Boychuk
6dc3528ae8 [CTS-12109] refactoring 2023-11-03 00:36:40 +01:00
Alisa Ismailati
d31dc4d5fd [CST-12145] get qa-sources by target for item-page & refactor 2023-11-02 17:49:19 +01:00
Giuseppe Digilio
f992ff6671 [DURACOM-197] Fix cache issue when depositing a submission 2023-11-02 16:46:33 +01:00
Mattia Vianelli
cc6bf7c460 Merge remote-tracking branch 'origin/CST-12481' into CST-12481 2023-11-02 14:42:25 +01:00
Mattia Vianelli
d28e09a5ae CST-12481 Added missing i18n values 2023-11-02 14:42:19 +01:00
Mattia Vianelli
673a331ca1 CST-12455 Added missing i18n values 2023-11-02 14:41:37 +01:00
Mattia Vianelli
ea726a7dba CST-12455 Moved patterns to be using i18n, added id to represent the string to send to the rest when doing operations 2023-11-02 14:16:14 +01:00
Mattia Vianelli
3e0e8e51d5 Merge remote-tracking branch 'origin/CST-12179' into CST-12179 2023-11-02 13:16:41 +01:00
Mattia Vianelli
b2e150d3e8 CST-12471 Form error now shows failed to edit the service and redirect to the form instead of closing and returning to services directory 2023-11-02 13:16:20 +01:00
Mattia Vianelli
83ef02c150 CST-12179 Form error now shows failed to edit the service and redirect to the form instead of closing and returning to services directory 2023-11-02 13:13:36 +01:00
Mattia Vianelli
1904befe0e CST-12179 Added form handling for errors and new field score that only accepts a value between 0 and 1 2023-11-02 13:07:46 +01:00
Alisa Ismailati
bcf0b4c297 [CST-11045] code clean up 2023-11-02 13:06:04 +01:00
Alisa Ismailati
8616ef7461 [CST-11045] rollback changes on ldn-service-coar-patterns.ts 2023-11-02 12:51:54 +01:00
Alisa Ismailati
5a8bc4e465 [CST-11045] small change 2023-11-02 12:48:35 +01:00
Alisa Ismailati
83761f1697 [CST-11045] Handle coar-notify section errors 2023-11-02 12:48:04 +01:00
William Welling
3228c457a3 Add UI nameSpace context path to Mirador viewer path 2023-11-01 14:39:08 -05:00
Alisa Ismailati
5b3bcf4271 Merged in coar-CST-12454 (pull request #974)
[CST-12454]  getting inbox url from rest for notify info page

Approved-by: Andrea Bollini
2023-11-01 17:48:38 +00:00
Andrea Bollini
7117bc3644 CST-12454 rename the placeholder to ldn inbox url 2023-11-01 18:48:03 +01:00
Alisa Ismailati
5dcd2d0ff7 [CST-11045] Save & update coar-notify-data (submission form) 2023-11-01 18:35:55 +01:00
Mykhaylo Boychuk
0b391970e0 [CST-12109] added new labels 2023-11-01 14:40:46 +01:00
Mykhaylo Boychuk
9094d8233f [CST-12109] implemented withdrawn-reinstate requests 2023-11-01 14:29:26 +01:00
Alisa Ismailati
d4d0fe0cbe Unit test fixes 2023-11-01 09:32:11 +01:00
Vlad Nouski
0f1f4e82e6 [CST-12043] feature: add label position 2023-10-31 12:45:09 +01:00
Mattia Vianelli
e14ca89802 Merge remote-tracking branch 'origin/CST-11045' into CST-11045 2023-10-31 11:59:34 +01:00
Mattia Vianelli
bbbeeb6b71 CST-11045 Changed names and styling 2023-10-31 11:56:25 +01:00
Alisa Ismailati
efd2d4c97c [CST-12454] getting inbox url from rest for notify info page 2023-10-31 11:49:35 +01:00
Alisa Ismailati
efcd4d335d Merged in coar-CST-12235 (pull request #951)
Coar CST-12235

Approved-by: Andrea Bollini
2023-10-31 09:27:01 +00:00
Mattia Vianelli
dff25d5de3 Merged in CST-10639 (pull request #908)
CST-10639

Approved-by: Andrea Bollini
2023-10-31 08:18:34 +00:00
Davide Negretti
930a381e4a [DURACOM-177] gap-* classes 2023-10-30 17:36:37 +01:00
Alisa Ismailati
c412c1fa13 [DURACOM-194] fixed year input value on input type date 2023-10-30 16:14:13 +01:00
Mattia Vianelli
b927761df3 CST-11045 Dropdown behavior improved, working on multiple dropdowns 2023-10-30 15:33:12 +01:00
Vlad Nouski
823f728eaa Merge remote-tracking branch '4cience-origin/CST-12043-primary-bitstream-flag' into CST-12043-primary-bitstream-flag 2023-10-30 12:52:41 +01:00
Vlad Nouski
ca64baca43 [CST-12043] feature: add title 2023-10-30 12:46:05 +01:00
Alisa Ismailati
143916c604 [CST-11178][CST-11179] fixed pagination for relationship management 2023-10-30 12:29:08 +01:00
Mykhaylo
1a525dfbe7 [CST-11179] Fixed breadcrumb & refresh & labels & redirection from /full page 2023-10-30 12:28:40 +01:00
Giuseppe Digilio
ac7cf5b4ba [CST-11178] Fix issue while retrieving the id 2023-10-30 12:27:35 +01:00
Giuseppe Digilio
b907c816b6 [CST-11178] Fix issue with link 2023-10-30 12:27:21 +01:00
Mykhaylo
5f63e3051e [CST-11178] Fix issue with correction type routing 2023-10-30 12:26:33 +01:00
Giuseppe Digilio
8157c477d3 [CST-11178] Fix correction-type-menu.component visualization 2023-10-30 12:25:22 +01:00
Alisa Ismailati
f76e224626 [CST-11178]CST-11179] small fix 2023-10-30 12:25:05 +01:00
Alisa Ismailati
c50dd72d92 [CST-11178][CST-11179] allow access for authorized user only 2023-10-30 12:24:40 +01:00
Alisa Ismailati
d7857a6036 [CST-11179][CST-11178] fix 2023-10-30 12:24:27 +01:00
Mykhaylo
eeefd867d4 [CST-11179][CST-11178] Fixes 2023-10-30 12:24:10 +01:00
Mykhaylo
d49185e449 [CST-11178][CST-11179] POST action of correction suggestion 2023-10-30 12:23:01 +01:00
Mykhaylo
bf67fee89b [CST-11178][CST-11179] page to select the target item for "relation" correction suggestion 2023-10-30 12:21:38 +01:00
Mykhaylo
752cb4d4b0 [CST-11178] show allowed correction types 2023-10-30 12:19:44 +01:00
Alisa Ismailati
543b4ad576 Merged in DSC-106 (pull request #643)
[DSC-106] Date input usable via keyboard using tab

Approved-by: Vincenzo Mecca
2023-10-30 11:31:35 +01:00
Alisa Ismailati
8e0af9bcee [CST-12145] commented code 2023-10-30 10:57:04 +01:00
Giuseppe Digilio
9322129c7d Merge branch 'CST-12043-primary-bitstream-flag' into CST-12044-visualize-the-primary-bitstream 2023-10-27 19:30:49 +02:00
Alisa Ismailati
e87c12cca3 [CST-12145] implementation of item-page & y dspace coar-notify notifications 2023-10-27 18:28:52 +02:00
Giuseppe Digilio
027a6b09e8 Merged cdl7 into CST-12043-primary-bitstream-flag 2023-10-27 15:44:00 +00:00
Tim Donohue
e1e2941983 Merge pull request #2580 from uniba-ub/issue2450-cssrules-exception
Check cssRules before css variables are read from stylesheet (again)
2023-10-27 10:27:52 -05:00
Mattia Vianelli
92d66c911a CST-11045 CSS refactor + dropdown data correctly populated 2023-10-27 16:24:17 +02:00
Mattia Vianelli
e9dd340e71 CST-11045 Added jsona5 text for coar submission section 2023-10-27 16:23:01 +02:00
Alexandre Vryghem
ab171648b6 New themed components & minor CSS fixes (#2442)
* 100839: Created themeable BrowseByComponent

* 100839: Added themed BrowseByComponent to custom theme

* 100839: Added themed BrowseEntryListElementComponent to custom theme

* Added PersonComponent to custom theme

* Themed LogInComponent

* Fix focus on navbar using different color

* Fix ccLicense checkbox margin

* Fix long search facets name not displaying correctly

* Removed RecentItemListComponent's unnecessary float causing alignment issues when adding components underneath it

* Themed RegisterEmailFormComponent
2023-10-26 16:04:44 -05:00
Tim Donohue
be59255623 Merge pull request #2441 from alexandrevryghem/header-and-navbar-css-fixes_contribute-7.6
Minor css variables fixes for header & navbar
2023-10-26 14:02:52 -05:00
Tim Donohue
1d7147e2af Merge pull request #2551 from alexandrevryghem/split-eperson-administration-page_contribute-main
[Port main] Created separate ePerson pages for create/edit
2023-10-26 13:23:44 -05:00
Alisa Ismailati
b102ef7165 [CST-12235] changed property name to ldn.notify.inbox 2023-10-26 19:28:44 +02:00
Alisa Ismailati
4fd475f6b7 [CST-12145] parial commit 2023-10-26 19:26:33 +02:00
Tim Donohue
8206e62df0 Merge pull request #2582 from DSpace/backport-2464-to-main
[Port main] i18n production improvements
2023-10-26 12:05:04 -05:00
Tim Donohue
0170949c51 Merge pull request #2506 from saschaszott/saschaszott-patch-3
allow insertion of multi-line scope notes in MD field registry
2023-10-26 11:56:26 -05:00
Mattia Vianelli
5ed9f46096 CST-11045 Improved css and remove console log and interpolation, added small notify regarding service compatibility 2023-10-26 18:47:25 +02:00
Alexandre Vryghem
f261264beb Implemented i18n cache busting
(cherry picked from commit 07a2e333ca)
2023-10-26 16:22:54 +00:00
Tim Donohue
6828fa38ce Merge pull request #2542 from 4Science/DURACOM-190
Fix i18n labels and alignment in vocabulary-treeview
2023-10-26 10:35:17 -05:00
Gantner, Florian Klaus
4dd334f2e7 more error-prone check of cssRules existence before css variables are get from stylesheet
check the existence off cssRules property before the variables are readed from this stylesheet
https://github.com/DSpace/dspace-angular/issues/2450
2023-10-26 15:56:52 +02:00
Paulo Graça
e847e4ef51 adding new access-status-list-element-badge css classes 2023-10-26 14:14:22 +01:00
Vlad Nouski
462aafb487 [CST-12044] feature: add translation for badge 2023-10-26 12:51:01 +02:00
Tim Donohue
81e3b36d7d Merge pull request #2423 from atmire/fix-inherit-policies-at-item-move
Fix inherit policies at item move (Angular)
2023-10-25 16:31:54 -05:00
Mattia Vianelli
0682b7b45f CST-11045 Dynamically creating dropdowns based on the coarconfig 2023-10-25 23:03:24 +02:00
Tim Donohue
3058a4bc86 Merge pull request #2362 from Leano1998/making_user-menu-component_themeable
Making user menu component themeable
2023-10-25 15:10:48 -05:00
Mattia Vianelli
12c60b7fad CST-12174 Removed console log 2023-10-25 21:44:41 +02:00
Mattia Vianelli
90652b62ce CST-12174 Added filters for empty string patterns removal 2023-10-25 21:41:11 +02:00
Tim Donohue
daef3f5161 Merge pull request #2530 from alexandrevryghem/fix-display-order-authentication-methods_contribute-7.6
Fix display LogInComponent turning blank when entering wrong username/password combination
2023-10-25 14:24:18 -05:00
Mattia Vianelli
7ed4d1457d CST-11045 Provided coarnotify section logic and dataservices for configs 2023-10-25 21:09:07 +02:00
Vlad Nouski
c644cc5102 [CST-12044] feature: add code coverage flag 2023-10-25 18:30:54 +02:00
Vlad Nouski
7cee04dd2d [CST-12044] feature: badge for primary bitstream 2023-10-25 18:29:48 +02:00
Vlad Nouski
f006bb3dff [CST-12043] fix: dependencies for unit test 2023-10-25 18:01:27 +02:00
Andreas Mahnke
09aaa46875 Support type-bind of elements based on repeatable list type-bound element (CHECKBOX_GROUP) 2023-10-25 16:34:51 +02:00
Kristof De Langhe
2f26e686cc 107685: menu-component re-render section on store update 2023-10-25 15:30:51 +02:00
Alan Orth
6affb06b26 Merge pull request #2553 from imilos/main
Added Serbian Cyrillic translation and corrected Serbian Latin translаtion
2023-10-24 21:02:24 +03:00
Mattia Vianelli
54fc1f81f7 Merged coar-notify-7 into CST-11045 2023-10-24 14:54:01 +00:00
Mattia Vianelli
c93c2a6234 CST-12174 fixed red selected form section both for inbound and outbound pattens 2023-10-24 16:52:03 +02:00
Mattia Vianelli
494295cd97 CST-11045 added sections parts 2023-10-24 15:26:23 +02:00
Alisa Ismailati
422a223c40 [CST-12235] support for the LDN inbox for root & /home & /item/:uuid pages 2023-10-24 15:14:55 +02:00
Mattia Vianelli
b995f9ea22 Merged coar-notify-7 into CST-11045 2023-10-24 12:20:00 +00:00
Mattia Vianelli
b3262ec290 CST-12180 added div container 2023-10-24 14:11:00 +02:00
Mattia Vianelli
d877d6f882 CST-12174 small layout fix 2023-10-24 14:05:50 +02:00
Mattia Vianelli
ce3ba35866 CST-12174 added isNew Check for patterns deeltion 2023-10-24 13:44:15 +02:00
Mattia Vianelli
4ecaeabe40 CST-12174 removed not needed guards 2023-10-24 13:27:46 +02:00
Mattia Vianelli
e2ad9fe707 CST-12174 Updated delete services to mark for deletion on outboundpatterns as well 2023-10-24 13:27:27 +02:00
Mattia Vianelli
0963a32081 CST-12174 Updated delete services to mark for deletion 2023-10-24 12:59:21 +02:00
Mattia Vianelli
5cca15daec CST-12174 Updated delete services to mark for deletion 2023-10-24 12:56:31 +02:00
Vlad Nouski
7c723caa15 [CST-12043] fix: redundant space 2023-10-24 12:52:46 +02:00
Vlad Nouski
6fc75a57e6 [CST-12043] ref: add and update documentation 2023-10-24 12:06:14 +02:00
Vlad Nouski
ae93cc0076 [CST-12043] fix: translation for custom switch 2023-10-24 12:05:12 +02:00
Alisa Ismailati
318f057ca4 [CST-12145] partial commit 2023-10-24 11:06:38 +02:00
Eike Löhden
7606f47688 Merge branch 'main' into making_user-menu-component_themeable 2023-10-24 07:53:08 +02:00
Tim Donohue
7c0fdcbe67 Merge pull request #2527 from alexandrevryghem/add-display-none-to-hidden-navbar-buttons_contribute-7.6
Minor header button improvements
2023-10-23 12:50:26 -05:00
Vlad Nouski
16327955a6 Merge commit 'bbc8b6ac8a15bdd619d4f7dac90655ab52dbad2c' into CST-12043-primary-bitstream-flag 2023-10-23 19:33:39 +02:00
Vlad Nouski
bbc8b6ac8a [CST-12043] unit tests 2023-10-23 18:34:13 +02:00
Tim Donohue
a9d7ee3617 Merge pull request #2561 from alexandrevryghem/fix-failing-request-service-test
Fix RequestService test sometimes failing in CICD
2023-10-23 08:55:47 -05:00
Alexandre Vryghem
b8eaf90b03 Merge branch 'fix-display-order-authentication-methods_contribute-7.4' into fix-display-order-authentication-methods_contribute-7.6
# Conflicts:
#	src/app/shared/log-in/log-in.component.html
#	src/app/shared/log-in/log-in.component.ts
2023-10-21 18:12:01 +02:00
Alexandre Vryghem
51b13fb78c Fix AuthorizationDataService.isAuthorized throwing a console error when the searchByObject request doesn't succeed 2023-10-21 18:08:02 +02:00
Alexandre Vryghem
987a92f529 Filter out all the AuthMethods who don't have a component to render before rendering LogInContainerComponent 2023-10-21 18:07:16 +02:00
Alexandre Vryghem
fb315335c9 Fix RequestService test failing because of different lastUpdated time 2023-10-21 14:45:37 +02:00
Tim Donohue
bf8519cc94 Merge pull request #2439 from amgciadev/fix-rac-grant-b
Fix Request-a-Copy grant form optional message not optional in form validation
2023-10-20 15:12:43 -05:00
Tim Donohue
1ffeae1c98 Merge pull request #2558 from tdonohue/revert_2454
Revert 2454 "Check cssRules before css variables are read from stylesheet"
2023-10-20 12:06:53 -05:00
Tim Donohue
6f73b65d53 Revert "Check cssRules before css variables are read from stylesheet (#2454)"
This reverts commit fa79c358c0.
2023-10-20 10:34:29 -05:00
Vlad Nouski
ffdeca69f4 [CST-12043] feature: add primary bitstream switch 2023-10-20 16:07:31 +02:00
Vlad Nouski
a55eb97a6a [CST-12043] fix: normalization for boolean 2023-10-20 16:04:40 +02:00
imilos
aa9e12dcfe Added Serbian cyrilic translation and corrected Serbian latin translation. 2023-10-20 11:27:22 +02:00
imilos
ad12e5a7f2 Added Serbian cyrilic translation and corrected Serbian latin translation. 2023-10-20 11:00:11 +02:00
Tim Donohue
1b109efc2c Merge pull request #2549 from DSpace/dependabot/npm_and_yarn/babel/traverse-7.23.2
Bump @babel/traverse from 7.21.4 to 7.23.2
2023-10-19 16:45:40 -05:00
Alexandre Vryghem
5151a2620c Merge remote-tracking branch 'upstream/main' into split-eperson-administration-page_contribute-main
# Conflicts:
#	src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html
2023-10-19 23:18:42 +02:00
Alexandre Vryghem
2e571767ea 107664: Normalized ePerson & group edit url and moved error message to translation file 2023-10-19 23:01:24 +02:00
Tim Donohue
3bbfcc9168 Merge pull request #2550 from DSpace/backport-2504-to-main
[Port main] Fix Angular SSR menu issues
2023-10-19 12:05:44 -05:00
Yana De Pauw
e0a3f15c00 106974: Angular SSR menu issues
(cherry picked from commit 3e8d180f1b)
2023-10-19 15:19:48 +00:00
dependabot[bot]
97f7a5e82a Bump @babel/traverse from 7.21.4 to 7.23.2
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.21.4 to 7.23.2.
- [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.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-19 01:56:32 +00:00
Mattia Vianelli
2eb71604ee CST-12174 created new remotadata service for itemfilters, pages are updated in real time when editing/creating/deleting services related stuff, added modals to confirm/reset 2023-10-18 18:46:06 +02:00
Alisa Ismailati
6d794cd51f [CST-12235] LDN inbox mechanism for home (partial commit) 2023-10-18 09:29:52 +02:00
Flo
fa79c358c0 Check cssRules before css variables are read from stylesheet (#2454)
Check cssRules existence before css variables are get from stylesheet
2023-10-17 22:17:28 +03:00
Mattia Vianelli
ba0b110647 CST-12174 added resetForm and leave edit for edit-form and overview page 2023-10-17 17:46:47 +02:00
Mattia Vianelli
1c376b2964 CST-12174 Implemented data-service for the api calls 2023-10-17 16:28:36 +02:00
Alan Orth
5fd2782e7b Merge pull request #2534 from DSpace/dependabot/npm_and_yarn/postcss-8.4.31
Bump postcss from 8.4.23 to 8.4.31
2023-10-16 10:36:07 +03:00
Davide Negretti
feb2b2be53 [DURACOM-190] Fix alignment in vocabulary-treeview 2023-10-13 11:05:14 +02:00
Alan Orth
287ec5a2ab Merge pull request #2544 from DSpace/backport-2536-to-main
[Port main] Issue 2535 hide add more button submission if no disabled sections 7.x
2023-10-12 12:03:09 +03:00
Marie Verdonck
0d376a6607 Issue#2535: Hide add more button in submission if no disabled sections
(cherry picked from commit 709848ee25)
2023-10-12 06:46:34 +00:00
Alan Orth
312b0aacf6 Merge pull request #2531 from atmire/main-fix-browse-by-visual-bug
Fix browse by visual bug
2023-10-11 22:04:00 +03:00
Mattia Vianelli
8de0e76b72 CST-12174 Ldn services are now retrieved with the remotadata object 2023-10-11 15:35:28 +02:00
Davide Negretti
b321d6f727 [DURACOM-190] Fix i18n labels in vocabulary-treeview 2023-10-11 13:02:13 +02:00
Mattia Vianelli
9216073b5b CST-12174 Coar form pages are now sized correctly, finishing up save button and alias for api call 2023-10-11 12:34:48 +02:00
Pascal-Nicolas Becker
a57bcf0dc7 Merge pull request #2537 from mugraph/main
Translate community as 'Bereich' in de.json5
2023-10-10 22:39:46 +02:00
Mattia Vianelli
8f63b54d71 CST-11045 Located component, working on adding new accordion 2023-10-09 15:55:24 +02:00
Janne Jensen
0139670371 translate community as 'Bereich' in de.json5 2023-10-07 10:43:09 +02:00
Mattia Vianelli
71adb5b7c8 CST-10639 Fixed field in the item-view (Endorsement) 2023-10-06 15:17:57 +02:00
Mattia Vianelli
4dd339235f CST-10639 Fixed fields in the item-view (Endorsed Reviewed and Dataset) 2023-10-06 15:13:58 +02:00
Mattia Vianelli
2bf565392a CST-10639 Added new fields in the item-view (Endorsed Reviewed and Dataset) 2023-10-06 14:46:27 +02:00
Mattia Vianelli
9ab62d21b7 Merged in CST-11048 (pull request #905)
CST-11048

Approved-by: Andrea Bollini
2023-10-06 11:40:14 +00:00
Mattia Vianelli
c75f61840f CST-11048 Spaced form updated error behaviour 2023-10-06 13:04:52 +02:00
Sondissimo
f893c6f2f3 CST-11048 Implemented enabled slider in edit 2023-10-06 12:25:23 +02:00
Sondissimo
11f9258d40 CST-11048 Form Styling 2023-10-06 01:02:49 +02:00
dependabot[bot]
3c5079e9ce Bump postcss from 8.4.23 to 8.4.31
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.23 to 8.4.31.
- [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.23...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-04 00:34:17 +00:00
Alexandre Vryghem
fa56d5dfb7 Fixed invalid html structure the ExpandableNavbarSectionComponent had an ul tag containing non-li tags 2023-10-02 21:31:42 +02:00
Alexandre Vryghem
58d31dd73f Applied same gap between header icons in the dspace theme and made the search field non-focusable when collapsed 2023-09-29 22:44:29 +02:00
Alexandre Vryghem
f9b4460e70 Themed LangSwitchComponent 2023-09-29 22:43:47 +02:00
Alexandre Vryghem
9f2a1d048b Use gap instead of individual paddings for header icons 2023-09-29 22:43:47 +02:00
Alexandre Vryghem
2ca2a3881f Added new variables for the expandable navbar section 2023-09-29 21:46:11 +02:00
Jens Vannerum
d0b4e15db4 Fix browse by visual bug 2023-09-29 15:54:34 +02:00
Enea Jahollari
3cf3cc8850 small fixes 2023-09-28 20:22:28 +02:00
Enea Jahollari
e232a7d8cb fix BitstreamAuthorizationsComponent tests 2023-09-28 20:03:05 +02:00
Enea Jahollari
bfe529e42a fix ProcessDetailComponent tests 2023-09-28 19:58:23 +02:00
Enea Jahollari
a353ac1188 fix ProjectSearchResultListElementComponent tests 2023-09-28 19:51:56 +02:00
Enea Jahollari
4c2c5af3ad fix CommunityListComponent tests 2023-09-28 19:48:52 +02:00
Enea Jahollari
840bad06bd fix SubmissionFormCollectionComponent tests 2023-09-28 19:44:34 +02:00
Enea Jahollari
5ba3bb2c9b fix MetadataRepresentationListComponent tests 2023-09-28 19:43:07 +02:00
Enea Jahollari
7447b64e61 fix JournalIssueComponent tests 2023-09-28 19:41:35 +02:00
Enea Jahollari
4374f284d9 fix ThumbnailComponent tests 2023-09-28 19:36:37 +02:00
Enea Jahollari
01c28a30a8 fix ItemPageAuthorFieldComponent tests 2023-09-28 19:34:03 +02:00
Enea Jahollari
35e967e8fb fix MetadataFieldFormComponent tests 2023-09-28 19:30:30 +02:00
Enea Jahollari
380658401b fix NumberPickerComponent tests 2023-09-28 19:09:58 +02:00
Enea Jahollari
08c22f90d4 fix SubmissionSectionSherpaPoliciesComponent tests 2023-09-28 18:46:09 +02:00
Enea Jahollari
f86de229d5 fix ItemGridElementComponent tests 2023-09-28 18:22:15 +02:00
Enea Jahollari
69e5b03015 fix JournalVolumeSearchResultListElementComponent tests 2023-09-28 18:18:09 +02:00
Enea Jahollari
7d96c90db9 fix CollectionAuthorizationsComponent tests 2023-09-28 18:15:42 +02:00
Enea Jahollari
35aadf7524 fix JournalVolumeListElementComponent tests 2023-09-28 18:14:17 +02:00
Enea Jahollari
18ee7513ba fix DsDynamicLookupRelationExternalSourceTabComponent tests 2023-09-28 17:49:49 +02:00
Enea Jahollari
2a88c8754c fix BrowseByTaxonomyPageComponent tests 2023-09-28 17:44:25 +02:00
Enea Jahollari
9e2842ff91 fix ItemAuthorizationsComponent tests 2023-09-28 17:40:52 +02:00
Enea Jahollari
945491b815 fix CollectionCurateComponent tests 2023-09-28 15:15:16 +02:00
Enea Jahollari
63fcd40a16 fix JournalSearchResultListElementComponent tests 2023-09-28 15:08:31 +02:00
Enea Jahollari
52da1e0329 fix OrcidSyncSettingsComponent tests 2023-09-28 14:56:37 +02:00
Enea Jahollari
c133ff7ebb fix CollectionSourceComponent tests 2023-09-28 14:48:12 +02:00
Enea Jahollari
5dfd812d81 fix ItemPageComponent tests 2023-09-28 14:46:36 +02:00
Enea Jahollari
5086eaa11f fix ProfileClaimItemModalComponent tests 2023-09-28 14:43:24 +02:00
Enea Jahollari
ca4fdae297 fix ItemEditBitstreamComponent tests 2023-09-28 14:41:53 +02:00
Enea Jahollari
444c35d7a1 fix GroupFormComponent tests 2023-09-28 14:35:08 +02:00
Enea Jahollari
995752a890 fix JournalVolumeSearchResultListElementComponent tests 2023-09-28 14:33:17 +02:00
Enea Jahollari
f112671b04 fix ContextHelpWrapperComponent tests 2023-09-28 14:30:13 +02:00
Enea Jahollari
38e2478442 fix ProfilePageComponent tests 2023-09-28 12:26:32 +02:00
Enea Jahollari
c449997d96 fix DsoEditMetadataFieldValuesComponent tests 2023-09-28 12:16:19 +02:00
Enea Jahollari
188320b6e2 fix SearchFilterComponent tests 2023-09-28 12:10:01 +02:00
Enea Jahollari
995ea5245c fix JournalIssueSearchResultListElementComponent tests 2023-09-28 12:06:21 +02:00
Enea Jahollari
2b9c3eafac fix ProfilePageMetadataFormComponent tests 2023-09-28 11:27:07 +02:00
Enea Jahollari
84e1254560 fix ItemVersionsComponent tests 2023-09-28 10:56:24 +02:00
Enea Jahollari
ae0bc1ab34 fix MyDSpaceReloadableActionsComponent tests 2023-09-28 10:04:44 +02:00
Enea Jahollari
91fc698147 fix ItemPageComponent tests 2023-09-28 09:53:01 +02:00
Enea Jahollari
e7b5528ad3 fix SubmissionSectionUploadFileComponent tests 2023-09-28 09:50:41 +02:00
Enea Jahollari
0f1944584a fix UntypedItemComponent tests 2023-09-27 16:48:01 +02:00
Enea Jahollari
f7968fe256 fix WorkspaceItemSearchResultListElementComponent tests 2023-09-27 16:46:21 +02:00
Enea Jahollari
add3dc63d1 fix WorkflowItemSearchResultListElementComponent tests 2023-09-27 16:45:04 +02:00
Enea Jahollari
f9d1b07ff6 fix OrgUnitSearchResultListSubmissionElementComponent tests 2023-09-27 16:43:26 +02:00
Enea Jahollari
fd34b008ad fix RelatedItemsComponent tests 2023-09-27 16:39:48 +02:00
Enea Jahollari
663da179c7 fix PublicationComponent tests 2023-09-27 16:37:42 +02:00
Enea Jahollari
3e1b05bfa6 fix PrivacyContentComponent tests 2023-09-27 16:36:06 +02:00
Enea Jahollari
ef11bd6990 fix ItemMyDSpaceResultListElementComponent tests 2023-09-27 16:25:59 +02:00
Enea Jahollari
f3a0d2e227 fix ItemSearchResultListElementComponent tests 2023-09-27 16:24:48 +02:00
Enea Jahollari
585827b970 fix ItemDeleteComponent tests 2023-09-27 16:19:18 +02:00
Enea Jahollari
2ce2ac7290 fix CommunityAdminSearchResultListElementComponent tests 2023-09-27 16:17:08 +02:00
Enea Jahollari
1a9c90fefe fix CollectionAdminSearchResultListElementComponent tests 2023-09-27 16:15:46 +02:00
Enea Jahollari
2d246e7473 fix ProcessOverviewComponent tests 2023-09-27 16:10:41 +02:00
Enea Jahollari
30f2a45d59 fix ResourcePolicyEditComponent tests 2023-09-27 16:08:08 +02:00
Enea Jahollari
d7626e22c9 fix ItemListPreviewComponent tests 2023-09-27 16:06:41 +02:00
Enea Jahollari
9dfa809cd1 fix BrowseByTitlePageComponent tests 2023-09-27 16:02:11 +02:00
Enea Jahollari
be86fea039 fix FileDownloadLinkComponent tests 2023-09-27 15:29:00 +02:00
Enea Jahollari
96928e7417 fix ItemGridElementComponent tests 2023-09-27 15:22:53 +02:00
Enea Jahollari
276402449e fix OrgUnitSearchResultListElementComponent tests 2023-09-27 15:19:37 +02:00
Enea Jahollari
924f1f61d2 fix SearchComponent tests 2023-09-27 15:16:48 +02:00
Enea Jahollari
a7d3df0269 fix DSOSelectorComponent tests 2023-09-27 15:14:20 +02:00
Enea Jahollari
a370433ab7 fix EPersonFormComponent tests 2023-09-27 15:10:13 +02:00
Enea Jahollari
42f404c38c fix EPeopleRegistryComponent tests 2023-09-27 15:08:13 +02:00
Enea Jahollari
78c98c1675 fix JournalComponent tests 2023-09-27 15:06:29 +02:00
Enea Jahollari
f574d93574 fix BulkAccessSettingsComponent tests 2023-09-27 14:59:16 +02:00
Enea Jahollari
d1cdcbde78 fix ExportMetadataSelectorComponent tests 2023-09-27 14:56:32 +02:00
Enea Jahollari
1186415bf1 fix BitstreamFormatsComponent tests 2023-09-27 14:49:49 +02:00
Enea Jahollari
e3abb75a56 fix DsDynamicListComponent tests 2023-09-27 13:37:56 +02:00
Enea Jahollari
b2f04ca379 fix CreateItemParentSelectorComponent tests 2023-09-27 13:22:42 +02:00
Enea Jahollari
b2ffc87831 fix JournalIssueGridElementComponent tests 2023-09-27 13:21:17 +02:00
Alan Orth
8045e4cda8 Merge pull request #2529 from atmire/fix-mutliple-api-calls-on-route-change-8.0.0-next
[Port main] Fix multiple api calls on route change
2023-09-27 14:03:41 +03:00
Enea Jahollari
d905e71013 fix AuthorizedCollectionSelectorComponent tests 2023-09-27 12:45:08 +02:00
Enea Jahollari
8257057ad0 fix ContextHelpDirective tests 2023-09-27 12:43:30 +02:00
Enea Jahollari
ec938f7fb8 fix CreateCommunityParentSelectorComponent tests 2023-09-27 12:41:01 +02:00
Enea Jahollari
48a16d29e7 fix CreateProfileComponent tests 2023-09-27 12:39:42 +02:00
Enea Jahollari
7c3eccf3f4 fix CreateCollectionParentSelectorComponent tests 2023-09-27 12:37:59 +02:00
Enea Jahollari
0a05118323 fix ComColFormComponent tests 2023-09-27 12:33:57 +02:00
Art Lowel
623fc6ede8 Merge branch 'fix-mutliple-api-calls-on-route-change-7.6' into fix-mutliple-api-calls-on-route-change-8.0.0-next 2023-09-27 11:58:54 +02:00
Enea Jahollari
45354dba2b fix ThumbnailComponent tests 2023-09-27 11:47:43 +02:00
Enea Jahollari
fdd08d2a22 fix ItemAdminSearchResultListElementComponent tests 2023-09-27 11:42:30 +02:00
Enea Jahollari
9cb379c979 fix HeaderComponent tests 2023-09-27 11:41:03 +02:00
Enea Jahollari
0692f6e28b fix ExistingRelationListElementComponent tests 2023-09-27 11:30:07 +02:00
Enea Jahollari
169b7a847a fix DsDynamicLookupRelationExternalSourceTabComponent tests 2023-09-27 10:45:39 +02:00
Enea Jahollari
a03fe7bf32 fix ClaimedSearchResultListElementComponent tests 2023-09-27 10:11:58 +02:00
Enea Jahollari
16e8a0a03b fix ClaimedDeclinedTaskSearchResultListElementComponent tests 2023-09-27 10:01:20 +02:00
Enea Jahollari
a273aaa9d7 fix ClaimedDeclinedSearchResultListElementComponent tests 2023-09-27 10:00:20 +02:00
Enea Jahollari
2ac4b43e58 fix ClaimedApprovedSearchResultListElementComponent tests 2023-09-27 09:59:20 +02:00
Enea Jahollari
87af5e311c fix ProfilePageSecurityFormComponent tests 2023-09-27 09:57:34 +02:00
Alan Orth
4491e6c49d Merge pull request #2385 from atmire/move-subscription-button-to-dso-edit-menu
Move subscription button to DSO edit menu
2023-09-27 10:02:03 +03:00
Alan Orth
ab3835f795 Merge pull request #2525 from 4Science/DURACOM-185
Fix mouse cursor on language dropdown menu
2023-09-26 21:39:54 +03:00
Enea Jahollari
68300eb9b6 fix broken tests 2023-09-26 18:17:46 +02:00
Enea Jahollari
32aa3354dd feat: fix SupervisionOrderGroupSelectorComponent test 2023-09-26 16:59:18 +02:00
Art Lowel
6a99185214 roll back unintended change to the responseMsToLive for RootDataservice 2023-09-26 16:56:07 +02:00
Enea Jahollari
59e765bb12 feat: fix AlertComponent test 2023-09-26 16:52:00 +02:00
Enea Jahollari
d61b197129 feat: fix DsDynamicTagComponent test 2023-09-26 16:48:31 +02:00
Enea Jahollari
a933bf52a0 feat: fix SubmissionImportExternalComponent test 2023-09-26 16:12:14 +02:00
Enea Jahollari
0ddaca8535 feat: fix ItemSearchResultListElementComponent test 2023-09-26 15:45:22 +02:00
Enea Jahollari
3bc02b8477 feat: fix ForgotPasswordFormComponent test 2023-09-26 15:23:37 +02:00
Enea Jahollari
03f334ff61 feat: fix SubmissionFormComponent test 2023-09-26 15:17:12 +02:00
Art Lowel
32fc28ec54 fix dev mode issue where retrieving the login options fails 2023-09-26 14:44:13 +02:00
Enea Jahollari
0d1a472c14 feat: fix FullFileSectionComponent test 2023-09-26 14:04:56 +02:00
Enea Jahollari
9f5dbef490 feat: fix ItemVersionHistoryComponent test 2023-09-26 13:15:55 +02:00
Enea Jahollari
815c09b7d9 feat: fix EditRelationshipComponent test 2023-09-26 13:04:57 +02:00
Enea Jahollari
555aebc77e feat: fix GroupsRegistryComponent test 2023-09-26 12:31:07 +02:00
Enea Jahollari
537926334c feat: fix SubmissionEditComponent test 2023-09-26 12:17:18 +02:00
Enea Jahollari
ba0caffbbc feat: fix CollectionSearchResultGridElementComponent test 2023-09-26 12:00:02 +02:00
Enea Jahollari
df9bd92f28 feat: fix ItemVersionsNoticeComponent test 2023-09-26 11:53:09 +02:00
Enea Jahollari
89994b74af feat: fix OrcidPageComponent test 2023-09-26 11:49:42 +02:00
Sondissimo
1c9fbd4629 CST-11048 FInished the angular implementation for the ldn inbox, needs cleanup 2023-09-26 11:12:19 +02:00
Enea Jahollari
cb41f40c50 small change 2023-09-26 10:52:17 +02:00
Enea Jahollari
5fff8679d5 feat: fix ClaimedTaskActionsComponent test 2023-09-26 10:08:58 +02:00
Enea Jahollari
8b9fc0044e feat: fix LogInPasswordComponent test 2023-09-26 10:04:50 +02:00
Enea Jahollari
4955d4c23a feat: fix LogInPasswordComponent test 2023-09-26 09:59:46 +02:00
Enea Jahollari
30b5550cff feat: fix SubmissionSectionFormComponent test 2023-09-26 09:54:42 +02:00
Enea Jahollari
79cbe691e5 feat: fix OrcidAuthComponent test 2023-09-26 09:51:21 +02:00
Enea Jahollari
65e786bd1f feat: fix CollectionDropdownComponent test 2023-09-26 09:45:03 +02:00
Enea Jahollari
b0af3e7b8d feat: fix DsoEditMenuSectionComponent test 2023-09-26 09:41:33 +02:00
AndreaBarbasso
f53c77be8e Fix missing or wrong Italian translations (#2518)
* [DURACOM-184] fix missing or wrong Italian translations

---------

Co-authored-by: Andrea Barbasso <´andrea.barbasso@4science.com´>
2023-09-26 10:22:30 +03:00
Alan Orth
f48764d397 Merge pull request #2473 from alexandrevryghem/fix-routes-not-relative-to-basehref_main-7.6
Fix routes not working with baseHref
2023-09-26 10:06:58 +03:00
Milos Ivanovic
cae831bd41 Serbian (Latin) translation (#2508)
Serbian (latin) translation added.
2023-09-26 08:29:17 +03:00
Hrafn Malmquist
e92b58eb9e Merge pull request #2519 from DSpace/backport-2502-to-main
[Port main] Don't capitalize metadata values in search, merging without approval as original PR was tested and approved by 2
2023-09-26 00:26:10 +01:00
Alan Orth
c225067127 src/app/shared/search: don't capitalize metadata values
Don't capitalize metadata values for display purposes.

(cherry picked from commit 47029c0a78)
2023-09-25 23:19:29 +00:00
Davide Negretti
6b5708cffd [DURACOM-185] Fix pointer on language dropdown menu 2023-09-25 23:51:48 +02:00
Enea Jahollari
c96201f1d4 feat: fix SubmissionImportExternalCollectionComponent test 2023-09-25 22:11:20 +02:00
Enea Jahollari
5e09fe6bf0 feat: fix services lazy loading 2023-09-25 21:35:50 +02:00
Enea Jahollari
54614c21f0 feat: migrate everything to standalone and fix build errors 2023-09-25 12:43:29 +02:00
reetagithub
85deeaab6a Update fi.json5 (#2422)
* Update fi.json5

Two last translations to the Finnish file.
2023-09-25 11:59:26 +03:00
Sascha Szott
77d6212e46 remove obsolete label element in metadata-schema.component.html (#2505)
* remove obsolete label element in metadata-schema.component.html
2023-09-22 20:57:36 +03:00
Art Lowel
8fbe8c16dc fix issue where invalidateRootCache didn't happen when the page first loaded 2023-09-22 12:00:59 +02:00
Art Lowel
5ad621b27e fix issue where more than one api call was made on every route change 2023-09-22 10:23:07 +02:00
Sascha Szott
5bc5dd859e allow to insert multi-line scope notes in MD field registry 2023-09-19 17:51:58 +02:00
Sondissimo
b040f9c2e7 CST-11048 sync branch with coar-notify-7 2023-09-18 14:28:28 +02:00
Sondissimo
4805b5061a CST-11048 First commit +fix for admin routing 2023-09-18 13:36:57 +02:00
Sondissimo
24a0af1e9a CST-11048 First commit 2023-09-18 13:35:44 +02:00
Mattia Vianelli
9a0f6fd12d Merged in CST-11012-demo (pull request #778)
CST-11012 demo
2023-09-18 11:05:53 +00:00
Mark H. Wood
faa2089d5c restore stock yarn.lock 2023-09-14 15:19:58 -04:00
Mark H. Wood
ed5267820e Update dependencies for Node v20. 2023-09-14 13:03:03 -04:00
Tim Donohue
4171ccc8f3 Merge pull request #2451 from mwoodiupui/graceful
Graceful shutdown on SIGINT (e.g. from 'pm2 stop').
2023-09-14 11:07:17 -05:00
Sondissimo
0d79dd0e8f Merge branch 'coar-notify-7' of bitbucket.org:4Science/dspace-angular into CST-11012-demo 2023-09-14 17:40:08 +02:00
Tim Donohue
957e0a8d1d Merge pull request #2493 from tdonohue/fix_2321
Correct text of help info on edit group page
2023-09-14 10:06:58 -05:00
Sondissimo
f9e4044e81 CST-11012 fixed conflicts 2023-09-14 17:06:14 +02:00
Eike Martin Löhden
cfa70ecb8e Removed default value from inExpandableNavbar. 2023-09-14 16:28:21 +02:00
Tim Donohue
49247430e5 Correct text of help info on edit group page 2023-09-12 13:57:26 -05:00
Tim Donohue
055b2322bd Merge pull request #2414 from alexandrevryghem/fix-display-order-authentication-methods_contribute-7.6
Made it possible to reorder the login methods
2023-09-11 16:39:55 -05:00
Alexandre Vryghem
0905a53db5 Fix innerText still being undefined in ssr mode 2023-09-08 22:35:27 +02:00
Alan Orth
e9b94f8e02 Merge pull request #2445 from hugo-escire/issue/1081
Fix to Metadata Registry create new metadata schema doesn't appear without reload #1081
2023-09-08 22:49:42 +03:00
Tim Donohue
f7ce1a5391 Merge pull request #2421 from hugo-escire/issue/2105
🚸remove thumbnail from file-upload section and show bitstream format …
2023-09-08 14:33:35 -05:00
Tim Donohue
01c8a4d9c3 Update workspaceitem-section-upload-file.model.ts
Fix code comment
2023-09-08 14:28:23 -05:00
Hugo Dominguez
13e4052c4d 🎨revert unnecessary format 2023-09-08 11:51:42 -06:00
Hugo Dominguez
9bb2e8a47f Merge branch 'main' into issue/2105 2023-09-08 11:40:42 -06:00
Hugo Dominguez
3e5524de69 🎨 revert format 2023-09-08 11:31:26 -06:00
Hugo Daniel Dominguez de la Cruz
4075d4989b Merge branch 'DSpace:main' into issue/1081 2023-09-08 10:33:29 -06:00
Tim Donohue
45b0f1607a Merge pull request #2412 from saschaszott/patch-9
remove redundant cache default values from server.ts
2023-09-08 10:44:02 -05:00
Tim Donohue
499463f47e Merge pull request #2349 from alexandrevryghem/fix-setStaleByHrefSubtring-not-emitting-after-all-requests-were-stale_contribute-main
Fix `setStaleByHrefSubstring` not emitting true when all requests are stale
2023-09-07 14:39:30 -05:00
Tim Donohue
1824507c06 Merge pull request #2376 from alexandrevryghem/w2p-104312_pass-query-to-external-search-tabs_contribute-main
Search query is not set by default when opening an external sources tab
2023-09-07 12:49:21 -05:00
Tim Donohue
11341bf7d7 Merge pull request #2470 from universidadeaveiro/dspace_7_6_pt-PT_i18n_fix
Minor pt-PT translation fixes
2023-09-07 10:34:40 -05:00
Koen Pauwels
11e98f7e20 Fix lint issue. 2023-09-07 11:36:52 +02:00
Koen Pauwels
8175a9d014 Merge remote-tracking branch 'dspace/main' into feature-process_polling 2023-09-07 11:02:10 +02:00
Koen Pauwels
9b5001e1d9 104938 Cleanup 2023-09-07 10:19:33 +02:00
Tim Donohue
f5424618a0 Merge pull request #2366 from atmire/CSV-export-fixes
CSV export fixes
2023-09-06 14:52:39 -05:00
Tim Donohue
ba70a96552 Merge pull request #2361 from nona-luypaert/issue-2337_fix-vocabulary-treeview-not-updating
Fix tree not updating when switching between "Browse by Vocabulary" pages
2023-09-05 17:14:23 -05:00
Tim Donohue
65a215e486 Merge pull request #2457 from alanorth/fix-example-config-syntax
config/config.example.yml: fix example syntax
2023-09-05 16:24:00 -05:00
Alan Orth
813710704d Merge pull request #2432 from hugo-escire/issue/2145
Fix to Value of dropdown changes automatically on item submission page
2023-09-05 20:15:30 +03:00
Alan Orth
191c0399b9 Merge pull request #2444 from hugo-escire/issue/2372
Fix to Mobile navbar hamburger menu doesn't work in Firefox/Safari #2372
2023-09-05 12:00:07 +03:00
Sondissimo
eb82628164 CST-11012 needed path in app-routing-paths.ts 2023-09-03 17:42:26 +02:00
Sondissimo
18aa0bbd96 CST-11012 removed not needed path in app-routing-paths.ts 2023-09-03 14:03:25 +02:00
Sondissimo
1f5f0555dd CST-11012 removed not needed path in app-routing-paths.ts 2023-09-03 14:02:47 +02:00
Sondissimo
07527e1170 CST-11012 removed extra comment 2023-09-03 13:44:47 +02:00
Alexandre Vryghem
18febff7a6 Fix routes not working with baseHref 2023-09-01 21:34:27 +02:00
Hardy Pottinger
8b1827ee17 Merge pull request #2448 from arvoConsultores/spanish-translation-update
Spanish translation updated to 7.6
2023-09-01 14:04:45 -05:00
Koen Pauwels
53b0af100d 104938 Fix ProcessDetailComponent tests 2023-09-01 16:11:32 +02:00
Koen Pauwels
a59776d5a0 Failed attempt at fixing process-detail.component.spec.ts tests 2023-09-01 15:10:39 +02:00
José Carvalho
a6c1120700 Minor pt-PT translation fixes 2023-08-30 15:52:34 +01:00
Alan Orth
02f81f1a19 Merge pull request #2468 from tdonohue/cypress_accessibility_testing
Minor Accessibility Fixes & Enable accessibility scan on more pages
2023-08-30 17:40:30 +03:00
Tim Donohue
276d80895e Fix heading order accessibility issue in search filters/facets 2023-08-29 15:10:12 -05:00
Tim Donohue
70a7bbe3cb Minor fixes to cypress tests 2023-08-29 15:01:51 -05:00
Tim Donohue
2a881791ba Fix accessibility of date sliders by adding aria-labels 2023-08-29 15:01:21 -05:00
Tim Donohue
91d8b7e4f7 Update ng2-nouislider and nouislider to latest versions 2023-08-29 14:58:49 -05:00
Tim Donohue
ba244bf6b1 Fix heading order issue with item page & update accessibility tests to prove it now passes 2023-08-29 11:44:03 -05:00
Tim Donohue
339ed63734 Enable excessibility checking of login menu, and remove unnecessary exclusion from header 2023-08-29 11:43:36 -05:00
Tim Donohue
158ebb0e32 Reenable accessibility check fixed in #2251 2023-08-29 11:41:03 -05:00
Art Lowel
3be90ebe46 rewrite notifyOnCompletion as autoRefreshUntilCompletion, fix ProcessDetailComponent, and the ProcessDataService tests 2023-08-29 18:39:39 +02:00
Sondissimo
7676478600 CST-11012 Finalized Coar text +logo (centered with itself) 2023-08-29 13:54:21 +02:00
Sondissimo
2ba86ed6d9 CST-11012 Finalized Coar text +logo 2023-08-29 13:42:26 +02:00
Sondissimo
d3fb5bf686 CST-11012 Dynamically retrieving rest URL for the coar-notify-page 2023-08-29 13:32:03 +02:00
Sondissimo
fc4a44cc83 CST-11012 Fixed breadcrumb text and COAR Notify text in footer (3) 2023-08-29 12:49:22 +02:00
Sondissimo
eebaa0a213 CST-11012 Fixed breadcrumb text and COAR Notify text in footer (2) 2023-08-29 12:36:05 +02:00
Sondissimo
44e28c9033 CST-11012 Fixed breadcrumb text and COAR Notify text in footer 2023-08-29 12:32:54 +02:00
Nona Luypaert
64e9f778ae Merge branch 'main' into issue-2337_fix-vocabulary-treeview-not-updating 2023-08-28 22:27:59 +02:00
Tim Donohue
50899f1d1b Update to latest axe-core 2023-08-28 13:55:05 -05:00
Tim Donohue
68a3323fca Update to latest cypress 2023-08-28 13:53:19 -05:00
Alan Orth
d38402b1fb Merge pull request #2399 from mirkoscherf/2398-i18n_curation-task.task.registerdoi.label
fix(i18n): curation-task.task.registerdoi.label
2023-08-28 21:02:08 +03:00
Alan Orth
6c4cfe5199 Merge pull request #2447 from J4bbi/2251-accessibility-issues-on-the-community-list-page
2251 accessibility issues on the community list page
2023-08-28 17:43:56 +03:00
Alan Orth
eaeda318d9 Merge pull request #2463 from hugo-escire/issue/2159
Fix to Pagination position is retained between searches #2159
2023-08-28 17:33:11 +03:00
Hugo Dominguez
044230209c add first page condition when search submit 2023-08-25 13:20:37 -06:00
Hugo Dominguez
88a7088b47 🐛 go to first page after submit search 2023-08-25 13:18:10 -06:00
Alan Orth
da13913cb6 Merge pull request #2395 from alexandrevryghem/themed-component-fixes-main
Fix themed components duplicating themself when switching themes
2023-08-25 20:13:12 +03:00
Koen Pauwels
bd6648703c 104938 Add flush to process polling tests 2023-08-25 11:17:36 +02:00
Alan Orth
25febb160d Merge pull request #2434 from alexandrevryghem/w2p-105221_fix-value-pair-ids-not-being-unique-input-type-list_contribute-7.6
Input type list doesn't work correctly if multiple value-pairs have the same pair value
2023-08-25 09:16:58 +03:00
Hardy Pottinger
4fea34c6ba Merge pull request #2449 from michdyk/polish-translation-update-2383
Update pl.json5
2023-08-24 14:58:20 -05:00
Alan Orth
9e46b5310b config/config.example.yml: fix example syntax
As of DSpace Angular 7.2 the syntax has changed from TypeScript to
YAML.
2023-08-24 15:01:37 +03:00
Koen Pauwels
c4d57770c7 ProcessDataService.notifyOnCompletion tests (WIP) 2023-08-24 10:00:21 +02:00
Tim Donohue
9b88da7f33 Merge pull request #2456 from tdonohue/skip_merge_commits_in_port_pr
[GitHub Actions] When porting a PR, skip any merge commits
2023-08-23 17:07:59 -05:00
Tim Donohue
596006de82 Enable new skip merge commit feature 2023-08-23 17:05:42 -05:00
Tim Donohue
fbb04a0383 Merge pull request #2438 from hardyoyo/2437-fix-commented-out-paths-in-components
2437 Correct and clarify commented-out code in several custom components
2023-08-23 14:50:04 -05:00
Hardy Pottinger
e47586f2e5 Merge pull request #2452 from tdonohue/fix_demo_urls
Replace mentions of demo7.dspace.org and api7.dspace.org with new demo URLs
2023-08-22 17:25:20 -05:00
Tim Donohue
b51fd46536 Replace mentions of demo7.dspace.org and api7.dspace.org with demo or sandbox 2023-08-22 16:32:55 -05:00
Mark H. Wood
bf9b2b82e1 Document a modified method as required by PR guidelines. 2023-08-22 17:07:20 -04:00
Mark H. Wood
4449737aed Properly await termination. 2023-08-22 15:46:55 -04:00
Mark H. Wood
6709c3bb5f Graceful shutdown on SIGINT (e.g. from 'pm2 stop'). 2023-08-22 14:38:04 -04:00
Michał Dykas
f58a7a2e9b Update pl.json5 fix test 2 spaces instead of 1 2023-08-22 16:18:36 +02:00
Michał Dykas
93299ec83d Update pl.json5 fix issues from tests 2023-08-22 15:50:37 +02:00
Michał Dykas
cfd753f928 Update pl.json5
Translation update of 2 spaces instead of 3
2023-08-22 15:09:09 +02:00
Michał Dykas
b439ab4484 Update pl.json5
Update from 7.4 to 7.6 version
2023-08-22 13:32:16 +02:00
Sergio Fernández Celorio
1885638ba6 Some lint errors fixed 2023-08-22 12:57:08 +02:00
Sergio Fernández Celorio
4cc4192e93 Spanish translation updated to 7.6 2023-08-22 11:59:54 +02:00
Francesco Bacchelli
9b556fd703 Merge branch CST-5249_suggestion of https://github.com/4Science/DSpace into CST-11299 2023-08-22 11:16:30 +02:00
Hrafn Malmquist
e59913acab Reorder instance method to come after member declaration 2023-08-22 04:59:23 +01:00
Hrafn Malmquist
d9b6e9d81f Improve documentation 2023-08-22 04:29:01 +01:00
Hrafn Malmquist
5f71de885b Add trackby function so cdktree can differentiate between new and old nodes 2023-08-22 04:28:28 +01:00
Hrafn Malmquist
05c53ad1d4 Replace h2 with a h1 2023-08-22 04:27:23 +01:00
Hrafn Malmquist
5ef4a827f5 Replace h5 with a span 2023-08-22 04:26:53 +01:00
Hugo Daniel Dominguez de la Cruz
004f505509 Merge branch 'DSpace:main' into issue/1081 2023-08-19 16:02:14 -06:00
Hugo Dominguez
9fb9e5848c 🐛 fix bug of caching when add new schema 2023-08-19 15:59:33 -06:00
Hugo Dominguez
2dc9fd44d7 ♻️ refactor chain of observables to avoid async issues 2023-08-19 14:40:28 -06:00
Hugo Daniel Dominguez de la Cruz
df722f87a8 Merge branch 'DSpace:main' into issue/2372 2023-08-18 13:23:36 -06:00
Hugo Dominguez
60706720e4 🐛 fix when navbar expands on firefox 2023-08-18 13:20:19 -06:00
Koen Pauwels
78d0bdd336 104938 Implementation and tests (WIP) 2023-08-18 14:18:41 +02:00
Sondissimo
8271803f0a CST-11012 Notify info component has been completed with working guard and service 2023-08-17 15:57:34 +02:00
Alexandre Vryghem
6c48238fa2 Fixed breadcrumb padding using incorrect syntax 2023-08-17 14:03:28 +02:00
Alexandre Vryghem
14b1ce5e50 Fixed header bg color not being set in default (no) theme 2023-08-16 15:16:17 +02:00
Alexandre Vryghem
f6649e1c38 Added support for changing the color of the navbar 2023-08-16 15:15:51 +02:00
Hardy Pottinger
518cc714f2 fix lint error in workflow-item-send-back.component.ts 2023-08-15 10:26:38 -05:00
Hardy Pottinger
0906234a29 2437 Correct and clarify commented-out code in several custom components
- Correct the path in commented-out code in the custom theme's
  components
- Clarify that the workflow-item-sen-back.component.scss file is a stub
- It has no corresponding SCSS file in the base theme
2023-08-15 10:16:50 -05:00
Agustina Martinez
94c756d52d Update email-request-copy.component.html
Message is optional: remove req in [disabled]
2023-08-15 16:53:30 +02:00
Alexandre Vryghem
a426eba846 Merge branch 'w2p-105221_fix-value-pair-ids-not-being-unique-input-type-list_contribute-7.2' into w2p-105221_fix-value-pair-ids-not-being-unique-input-type-list_contribute-7.6 2023-08-14 16:48:46 +02:00
Alexandre Vryghem
4c419e1eee 105265: Made the DsDynamicListComponent's checkbox & radio buttons ids unique for each metadata field to prevent other value-pairs having the same id 2023-08-14 16:48:06 +02:00
Hugo Daniel Dominguez de la Cruz
539edfae9c Merge branch 'DSpace:main' into issue/2145 2023-08-13 12:15:31 -06:00
Hugo Dominguez
25479e1794 change test event, click by mousedown on dynamic-scrollable-dropdown.component.spec.ts 2023-08-13 12:12:05 -06:00
Hugo Dominguez
651305952d 🐛 Fix Value of dropdown changes automatically on item submission page 2023-08-13 12:10:25 -06:00
Sondissimo
2f9089d013 CST-11012 notify info component updated with guards and services (needs cleanup) 2023-08-11 15:26:20 +02:00
Francesco Bacchelli
f31d4d5276 Merge branch 'CST-5249_suggestion' of https://github.com/4Science/dspace-angular into CST-11299 2023-08-10 11:35:20 +02:00
Koen Pauwels
1f68fd63b2 Fix lint issue. 2023-08-09 10:28:26 +02:00
Sondissimo
36dfb69670 CST-11012 notify info component updated 2023-08-09 10:10:00 +02:00
Tim Donohue
77e994dcd6 Merge pull request #2409 from DSpace/dependabot/npm_and_yarn/word-wrap-1.2.5
Bump word-wrap from 1.2.3 to 1.2.5
2023-08-08 16:41:36 -05:00
Tim Donohue
60a9b01c86 Merge pull request #2426 from DSpace/revert-2424-test-fix-for-backport-action
Revert "Enable early release of backport-action to test korthout/backport-action#372"
2023-08-08 15:43:49 -05:00
Tim Donohue
765b5c8531 Revert "Enable early release of backport-action to test korthout/backport-action#372" 2023-08-08 15:35:17 -05:00
Tim Donohue
dc34cbebbd Merge pull request #2408 from atmire/ProcessDetailComponent-test-improvemen
ProcessDetailComponent test improvement
2023-08-08 15:00:26 -05:00
Tim Donohue
aaff0f02ed Merge pull request #2424 from tdonohue/test-fix-for-backport-action
Enable early release of backport-action to test korthout/backport-action#372
2023-08-08 14:59:00 -05:00
Tim Donohue
419868bd8d Enable early release of backport-action to test korthout/backport-action#372 2023-08-08 14:44:01 -05:00
Koen Pauwels
6791f6e311 103818 Adjusted "Inherit policies" tooltip 2023-08-08 14:58:31 +02:00
Koen Pauwels
95abcc9ce8 103818 Add warning tooltip to "Inherit policies" checkbox on item move page 2023-08-08 14:57:51 +02:00
Francesco Bacchelli
ec82139256 CST-11298 COAR: Update the first community PRs for DSpace target 8.0-SNAPSHOT 2023-08-07 14:26:42 +02:00
Alexandre Vryghem
9340d4be92 Merge branch 'fix-ngonchanges-not-working-for-themed-components_contribute-7.6' into themed-component-fixes-main
# Conflicts:
#	src/app/shared/theme-support/themed.component.ts
2023-08-06 20:05:27 +02:00
Alexandre Vryghem
ff0c1a256d Merge branch 'fix-ngonchanges-not-working-for-themed-components_contribute-7.4' into fix-ngonchanges-not-working-for-themed-components_contribute-7.6 2023-08-06 20:02:43 +02:00
Alexandre Vryghem
a7bef6ffe2 Merge branch 'fix-ngonchanges-not-working-for-themed-components_contribute-7.2' into fix-ngonchanges-not-working-for-themed-components_contribute-7.4
# Conflicts:
#	src/app/app.component.ts
2023-08-06 19:59:49 +02:00
Alexandre Vryghem
e4b386b815 Fix createHeadTags infinite loop when your themes don't override the headTags property 2023-08-06 19:57:05 +02:00
Hugo Dominguez
4c8ec8a4f2 🚸remove thumbnail from file-upload section and show bitstream format and checksum 2023-08-04 12:44:13 -06:00
Tim Donohue
4abbd376ac Merge pull request #2350 from alexandrevryghem/fix-collection-form-bugs_contribute-main
Fix minor collection form bugs
2023-08-04 12:47:34 -05:00
Tim Donohue
7bcac03541 Merge pull request #2388 from mirkoscherf/2387-i18n_for_status_badges_and_status_search_filter_entries
fix(i18n): add and update missing status strings
2023-08-04 11:15:19 -05:00
Mirko Scherf
cf79cccfd9 Merge branch 'main' into 2387-i18n_for_status_badges_and_status_search_filter_entries 2023-08-04 16:09:11 +02:00
Alan Orth
2cfd19f339 Merge pull request #2378 from dataquest-dev/show_password_error
Show error message from the password validation response
2023-08-04 15:21:08 +03:00
milanmajchrak
e6546b4499 Show error message from the error response 2023-08-04 11:04:48 +02:00
Tim Donohue
0fca4e040e Merge pull request #2397 from alexandrevryghem/fix-mathjax-displaying-twiced-in-ui_contribute-main
Fixed MathJax code being displayed twice by `dsMarkdown` pipe
2023-08-03 16:09:11 -05:00
Tim Donohue
4f445fa558 Merge pull request #2344 from the-library-code/handle_failed_related_item_responses
Catch and handle unsuccessful "convert rels to items" responses
2023-08-03 15:03:10 -05:00
Tim Donohue
deb82acf25 Merge pull request #2417 from DSpace/backport-2347-to-main
[Port main] src/assets/i18n: update Finnish language strings
2023-08-03 14:10:04 -05:00
Alan Orth
4c93b4728b src/assets/i18n: update Finnish language strings
Contributed by Reeta Kuukoski from the National Library of Finland.

(cherry picked from commit 1b9656b135)
2023-08-03 17:56:40 +00:00
Tim Donohue
102310cfa5 Merge pull request #2381 from mirkoscherf/2380_typo_in_alert_type_ts
refactor: rename aletr-type.ts to alert-type.ts
2023-08-03 11:48:57 -05:00
Tim Donohue
e50d0941f4 Merge pull request #2363 from DSpace/dependabot/npm_and_yarn/semver-5.7.2
Bump semver from 5.7.1 to 5.7.2
2023-08-02 15:06:21 -05:00
Alexandre Vryghem
3dc73f9021 Properly handle AuthMethod subscription in LogInComponent 2023-08-02 21:25:58 +02:00
Alexandre Vryghem
94ceee9080 Merge remote-tracking branch 'templates/dspace-7.6' into fix-display-order-authentication-methods_contribute-7.6
# Conflicts:
#	src/app/shared/log-in/log-in.component.html
#	src/app/shared/log-in/methods/log-in-external-provider/log-in-external-provider.component.spec.ts
#	src/app/shared/log-in/methods/oidc/log-in-oidc.component.html
#	src/app/shared/log-in/methods/oidc/log-in-oidc.component.spec.ts
#	src/app/shared/log-in/methods/orcid/log-in-orcid.component.html
#	src/app/shared/log-in/methods/password/log-in-password.component.ts
#	src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.html
#	src/app/shared/log-in/methods/shibboleth/log-in-shibboleth.component.spec.ts
2023-08-02 21:01:36 +02:00
Sascha Szott
e53abcb69e remove redundant cache default values from server.ts 2023-08-02 18:00:03 +02:00
Alexandre Vryghem
71cf66ecf4 Fix display order of authentication methods 2023-08-02 10:00:43 +02:00
dependabot[bot]
2fec33e70a Bump word-wrap from 1.2.3 to 1.2.5
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.5.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.5)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-01 21:28:22 +00:00
dependabot[bot]
0b0c60e38c Bump semver from 5.7.1 to 5.7.2
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-01 21:23:09 +00:00
Francesco Bacchelli
37121b001b Merge branch 'main' of https://github.com/DSpace/dspace-angular into CST-11298 2023-08-01 15:51:02 +02:00
Kristof De Langhe
63e6e79750 Merge branch 'main' into ProcessDetailComponent-test-improvemen 2023-08-01 10:50:08 +02:00
Yury Bondarenko
18b7a9c7de Update DSO edit menu resolver tests
- Abstract away the different "subsections" ~ DSO type (the tests should not care about this)
  Instead, retrieve sections of interest by ID & assert whether they're there & how they should look
- Test separately for Communities, Collections & Items
- Test newly added menu section
2023-07-31 15:43:15 +02:00
Alexandre Vryghem
ea677bda74 Reset to base theme when no default theme is set and leaving UUID/handle theme 2023-07-30 20:08:33 +02:00
Alexandre Vryghem
c4bff4d9c7 Fix test without it's/it's not running because they are in a waitForAsync function 2023-07-29 19:18:58 +02:00
Tim Donohue
1223e3c438 Merge pull request #2406 from tdonohue/update_actions
Add GitHub action to automatically create a port PR (based on label) & minor bug fixes
2023-07-28 15:11:08 -05:00
Tim Donohue
d75d12b423 Minor update to label_merge_conflicts to ignore any errors (seem random at this time) 2023-07-28 14:11:08 -05:00
Tim Donohue
338b63ebb8 Add action to automatically create a port PR when specified 2023-07-28 14:10:44 -05:00
Sondissimo
c8a75fa3b1 CST-11012 Added feature id 2023-07-28 15:38:37 +02:00
Alexandre Vryghem
ebaccc055e Merge branch 'fix-collection-form-bugs_contribute-7.4' into fix-collection-form-bugs_contribute-7.6
# Conflicts:
#	src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html
2023-07-28 00:14:58 +02:00
Alexandre Vryghem
273be5bd81 Merge remote-tracking branch 'contributions/fix-setStaleByHrefSubtring-not-emitting-after-all-requests-were-stale_contribute-7.4' into fix-setStaleByHrefSubtring-not-emitting-after-all-requests-were-stale_contribute-7.6
# Conflicts:
#	src/app/core/data/request.service.ts
2023-07-27 23:57:18 +02:00
Mirko Scherf
81e6ecc5c4 Merge branch 'DSpace:main' into 2380_typo_in_alert_type_ts 2023-07-27 12:54:42 +02:00
Mirko Scherf
889eb388af Merge branch 'DSpace:main' into 2387-i18n_for_status_badges_and_status_search_filter_entries 2023-07-27 12:54:27 +02:00
Sondissimo
969e006e29 CST-11012 Created the footer new icon+text, new json5 texts added, routed the newly created component notify-info.component 2023-07-26 18:27:26 +02:00
Mirko Scherf
0ec72f679b fix(i18m): curation-task.task.registerdoi.label
changed curation-task.task.register-doi.label back to
curation-task.task.registerdoi.label and added German translation
2023-07-26 13:04:04 +02:00
Alexandre Vryghem
5062e46433 104312: Add missing query @Input() to ThemedDynamicLookupRelationExternalSourceTabComponent 2023-07-25 18:26:47 +02:00
Alexandre Vryghem
9b1d18bd32 Merge tag 'dspace-7.6' into w2p-104312_pass-query-to-external-search-tabs_contribute-7.6 2023-07-25 18:26:18 +02:00
Alexandre Vryghem
a3663831cc Merge branch 'w2p-103176_fix-themed-component-vcr-not-defined-yet-in-init_contribute-7.2' into themed-component-fixes-main 2023-07-25 00:06:45 +02:00
Tim Donohue
44b197a15a Merge pull request #2394 from tdonohue/docker_version_fixes
Replace all old docker "dspace-7_x" tags with "latest" on main
2023-07-24 16:53:58 -05:00
Alexandre Vryghem
887eacbfa8 Merge branch 'fix-ngonchanges-not-working-for-themed-components_contribute-7.2' into fix-ngonchanges-not-working-for-themed-components_contribute-7.4 2023-07-24 23:43:10 +02:00
Alexandre Vryghem
873a8e16a3 101577: Ensure the component is always destroyed before rendering the new component 2023-07-24 23:42:55 +02:00
Tim Donohue
08ae7bfdba Replace all old docker "dspace-7_x" tags with latest 2023-07-24 16:10:23 -05:00
Alexandre Vryghem
15656b03ce Fix mathjax code being displayed twice
This was due to sanitizeHtml rendering the mjx-assistive-mml tag as text. This tag is used by screen readers and therefor we should allow it to be rendered
2023-07-21 23:18:40 +02:00
Alexandre Vryghem
75ec046bba Hide the delete EPerson/Group buttons when the user can't delete the EPerson/Group 2023-07-21 18:34:42 +02:00
Alexandre Vryghem
998e1fac8d Fix spacing issues for EPerson form buttons 2023-07-21 18:34:42 +02:00
Alexandre Vryghem
9ac19d40fc Cleanup access-control components
- Use the same methods to retrieve the access-control urls
- Fix EPersonDataService.startEditingNewEPerson returning the incorrect link
2023-07-21 18:34:41 +02:00
Alexandre Vryghem
2a35180a1b Created separate pages for edit & create EPersons 2023-07-21 17:15:10 +02:00
Mirko Scherf
b1df125166 fix(i18n): add and update missing status strings
New strings for status filter entries:
search.filters.namedresourcetype.*
Refactored strings introduced with #2068 (refactor badged),
e.g. mydspace.status.archived -> mydspace.status.mydspaceArchived
2023-07-21 10:43:13 +02:00
Yury Bondarenko
c9558167b2 Move subscription button to DSO edit menu 2023-07-20 18:19:48 +02:00
Mirko Scherf
8bf4ba8126 refactor: rename aletr-type.ts to alert-type.ts 2023-07-20 14:53:28 +02:00
Alexandre Vryghem
648925f3e1 104312: DsDynamicLookupRelationExternalSourceTabComponent should have the form value already filled in the search input 2023-07-19 14:04:34 +02:00
Alexandre Vryghem
776a44c321 Merge remote-tracking branch 'contributions/w2p-102415_Remote-lookup-entity-on-admin-edit_contribute-7.4' into dspace-7.6 2023-07-18 13:40:23 +02:00
Alexandre Vryghem
155cd2be6f Merge branch 'w2p-102415_Remote-lookup-entity-on-admin-edit_contribute-7.2' into dspace-7.4
# Conflicts:
#	src/app/shared/form/builder/ds-dynamic-form-ui/relation-lookup-modal/dynamic-lookup-relation-modal.component.ts
2023-07-18 12:53:56 +02:00
Marie Verdonck
cf9179d800 102415: Fix for import modal i18n labels 2023-07-18 12:01:32 +02:00
Marie Verdonck
a9c5c20c5a 102415: Provide all external sources of correct entity type in edit relationships ({item-page}/edit/relationship) 2023-07-18 12:01:32 +02:00
Kristof De Langhe
03e1f677b6 104126: ProcessDetailComponent test improvement 2023-07-17 15:12:42 +02:00
Alexandre Vryghem
8d84efb1a9 Fix AuthInterceptor no expectation tests
- Removed duplicate request
- Added expect().nothing() because httpMock.expectNone will throw an error when a request is made
2023-07-16 14:58:22 +02:00
Alexandre Vryghem
b8b9da2525 Fix BrowseByComponent no expectation tests
- Because the custom theme is disabled by default I replaced it with the dspace theme who is still enabled by default
- Because the child components use a themed component the changes need to be detected twice, this is a side effect from the component being created dynamically
2023-07-15 14:24:14 +02:00
Alexandre Vryghem
430b3b2e36 Fix TopLevelCommunityListComponent, CommunityPageSubCollectionListComponent, CommunityPageSubCommunityListComponent no expectation tests
Because their child components use a themed component the changes need to be detected twice, this is a side effect from the component being created dynamically
2023-07-15 10:25:53 +02:00
Alexandre Vryghem
4b1a8a90a6 Fix ObjectUpdatesEffects no expectation tests
Because of the switch from BehaviourSubject to Subject the tests always failed because of a timout because the previous state is not kept with Subject and should be manually triggered during the tests
2023-07-15 08:25:25 +02:00
Kristof De Langhe
45ad5f7316 104189: CSV export add fixedFilter 2023-07-12 16:32:56 +02:00
Kristof De Langhe
cac1407f08 104189: Allow CSV export on related entity search 2023-07-12 15:11:38 +02:00
Francesco Pio Scognamiglio
3c27e44429 Merged in CST-9779 (pull request #620)
CST-9779 Provided changes to Title, repository name, added introduction text with links, as well added the small icon for coar-notify inside the navbar.

Approved-by: Andrea Bollini
2023-07-11 07:40:02 +00:00
Eike Martin Löhden
e3f57dae44 Included user-menu component in custom theme. 2023-07-10 15:54:09 +02:00
Eike Martin Löhden
ed84f45132 Replaced tags for ds-user-menu. 2023-07-10 15:38:32 +02:00
Eike Martin Löhden
63582cfa0d Corrected missing semicolon. 2023-07-10 15:34:25 +02:00
Eike Martin Löhden
23aefb7385 Added themed-user-menu component. 2023-07-10 15:29:14 +02:00
Alexandre Vryghem
b7dfe0f23d Fix EPersonDataService no expectation tests
- The store dispatch was not called through so the store was never updated
- To initialize the store using provideMockStore I rewrote the test to work through injection
2023-07-09 00:16:28 +02:00
Nona Luypaert
b5a70e8f95 Fix VocabularyTreeview not updating + i18n for nsi 2023-07-08 13:21:36 +02:00
Tim Donohue
db6edbf184 Merge pull request #2359 from tdonohue/docker_actions
GitHub Actions: Split Docker images into separate jobs to run in parallel.  Enable 'latest' tag. (for dspace-angular)
2023-07-07 12:48:59 -05:00
Tim Donohue
3a83946278 Merge pull request #2358 from tdonohue/update_actions
Update GitHub Actions for 7.x maintenance (for dspace-angular)
2023-07-07 12:14:31 -05:00
Tim Donohue
effe181696 Split docker images into separate jobs to run in parallel. Ensure 'main' codebase is tagged as 'latest' 2023-07-07 12:05:56 -05:00
Tim Donohue
15be060665 Ensure codescan and label_merge_conflicts run on maintenance branches 2023-07-07 11:56:54 -05:00
Tim Donohue
5208008f95 Enable Pull Request Opened action to assign PRs to their creator 2023-07-07 11:56:47 -05:00
Yury Bondarenko
c0f9042a9c Merge remote-tracking branch 'origin/main' into more-eslint 2023-07-07 17:30:46 +02:00
Tim Donohue
9de30d5612 Merge pull request #2355 from alanorth/request-copy-component-path-main
src/app: fix path to deny-request-copy component
2023-07-06 14:31:25 -05:00
Alan Orth
8ac1e9a283 src/app: fix path to deny-request-copy component
The themed-deny-request-copy.component erroneously includes the cus-
tom theme's deny-request-copy component instead of its own.

Closes: https://github.com/DSpace/dspace-angular/issues/2351
2023-07-06 20:46:32 +03:00
Alexandre Vryghem
c2010b5d5e Fix SubscriptionsDataService no expectation tests
The test's searchData & the searchData from the SubscriptionsDataService were not linked
2023-07-04 13:12:03 +02:00
Alexandre Vryghem
c1875cade3 Add expect().nothing() to reducer tests without expectations, who only exist to check if an error is thrown 2023-07-04 13:12:03 +02:00
Alexandre Vryghem
b4a3882dcf Fix findSuccessfulAccordingTo no expectation tests
- The isWithdrawn was set twice to mockItem1
- The expected observable was not correctly compared
2023-07-04 13:12:03 +02:00
Alexandre Vryghem
b83a0b3f9b Fix SubmissionSectionUploadFileComponent no expectation tests
The fixture.whenStable() was not awaited
2023-07-04 13:12:03 +02:00
Alexandre Vryghem
a524805faf Fix CommunitySearchResultListElementComponent no expectation tests
- Wrong css class was used
- The check to see if the offset was defined did not exist
2023-07-04 13:12:03 +02:00
Alexandre Vryghem
9d71171741 Fix ItemVersionsComponent no expectation tests
- The buttons are now not disabled anymore but hidden when you don't have the correct authorization
- Wrong css class was used for the edit buttons
2023-07-04 13:12:02 +02:00
Alexandre Vryghem
7ef3a914f9 Fix ItemComponent no expectation tests
- renamed `showBackButton` to `showBackButton$` because it's an observable
- `showBackButton$` only emits `true` when the back button should be shown, it does not emit `false` when it should be hidden, it just did nothing
2023-07-01 20:38:17 +02:00
Alexandre Vryghem
063c54e93f Fix SearchHierarchyFilterComponent no expectation tests
Reverted the way you detect when an item is selected by using observables again instead of promises. The reason why it didn't work without the old observable approach was because `VocabularyTreeviewModalComponent` didn't have a `select` `@Output` like `VocabularyTreeviewComponent`
2023-07-01 20:38:17 +02:00
Alexandre Vryghem
e0db4284b3 Fix CollectionSearchResultListElementComponent no expectation tests
- Wrong css class was used
- The check to see if the offset was defined did not exist
2023-07-01 20:38:17 +02:00
Alexandre Vryghem
abc8640b5e Fix MetadataRepresentationListElementComponent no expectation tests
Also fixed incorrect regex in isLink()
2023-07-01 20:38:16 +02:00
Alexandre Vryghem
3640d75e10 Fix objectUpdatesReducer no expectation tests
Added a expect().nothing() to the test who don't expect anything but just ensure that no error is thrown
2023-07-01 20:38:16 +02:00
Alexandre Vryghem
bfaea741d5 Throw error for tests without expectations 2023-07-01 13:31:51 +02:00
Alexandre Vryghem
cf77726866 Fix enter not submitting collection form correctly
Fixed it for communities, collections, ePersons & groups
2023-07-01 01:01:57 +02:00
Alexandre Vryghem
b2b1782cd8 Hide entity field in collection form when entities aren't initialized 2023-06-30 22:43:46 +02:00
Tim Donohue
c4342d3238 Update version tag for development of next major release 2023-06-30 14:57:09 -05:00
Alexandre Vryghem
02a20c8862 103236: Added tests for setStaleByHrefSubstring 2023-06-30 17:01:25 +02:00
Art Lowel
ae6b183fae 103236: fix issue where setStaleByHrefSubtring wouldn't emit after all requests were stale 2023-06-30 16:56:15 +02:00
Kim Shepherd
a35b7d8356 catch and handle unsuccessful "convert rels to items" responses 2023-06-29 16:33:42 +02:00
Yury Bondarenko
c0f43bc585 ESLint: fix rxjs/no-implicit-any-catch
In most cases we can deal with the untyped errors by introducing an explicit instanceof check

DspaceRestService includes an unsafe catch/rethrow → made it explicitly typed as `any`, but it should be revisited at some point
2023-06-28 16:30:38 +02:00
Yury Bondarenko
07259ca342 ESLint/TypeScript: enforce no-implicit-any-catch
TypeScript try/catch and RxJs catchError are not type-safe.

https://typescript-eslint.io/rules/no-implicit-any-catch/
https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#unknown-on-catch
https://github.com/cartant/eslint-plugin-rxjs/blob/main/docs/rules/no-implicit-any-catch.md
2023-06-28 12:51:47 +02:00
Yury Bondarenko
2cad56c51f Fix tests
PaginationComponent: expected arguments of updateRoute were out of sync with (fixed) types
GroupFormComponent:  groupBeingEdited is never set to null
2023-06-28 09:42:49 +02:00
Yury Bondarenko
0633460107 ESLint: fix object-curly-spacing 2023-06-27 17:30:02 +02:00
Yury Bondarenko
436a4d7645 ESLint: enforce object-curly-spacing
https://eslint.org/docs/latest/rules/object-curly-spacing
2023-06-27 17:30:01 +02:00
Yury Bondarenko
a11be65f95 ESlint: fix imports 2023-06-27 17:20:06 +02:00
Yury Bondarenko
fa404fff84 ESLint: enforce import formatting
Mainly to reduce merge conflict noise

- Order by origin & alphabetically
- Imports should be at the top of the file
- Newline between imports & the rest of the file
- Disallow duplicate imports
- Enforce single-line when only importing one object
- Enforce multi-line when importing two or more
2023-06-27 17:20:00 +02:00
Yury Bondarenko
917c36dbe7 ESLint: fix dangling commas 2023-06-27 17:19:58 +02:00
Yury Bondarenko
725dbc3743 ESLint: enforce dangling commas
https://eslint.org/docs/latest/rules/comma-dangle

Reduces merge conflict noise
2023-06-27 17:19:55 +02:00
Yury Bondarenko
0690a201dc ESLint: fix indentation 2023-06-27 17:19:55 +02:00
Yury Bondarenko
b505cbc690 ESLint: enforce indentation
https://eslint.org/docs/latest/rules/indent
2023-06-27 17:19:54 +02:00
Yury Bondarenko
1b717e1a5c ESLint: fix RxJs rule violations (pt. 1) 2023-06-27 17:19:53 +02:00
Yury Bondarenko
218ce832a9 ESLint: enforce low-impact recommended RxJs rules
https://github.com/cartant/eslint-plugin-rxjs#rules

Mainly to steer us away from unsafe patterns

Skipping two recommended rules for now:
rxjs/no-implicit-any-catch → not immediately clear how this should be addressed
rxjs/no-nested-subscribe   → there are quite a few cases, would take some effort to fix all of them
2023-06-27 17:19:53 +02:00
Yury Bondarenko
8a650a2fb5 ESlint: fix eqeqeq violations 2023-06-27 17:19:45 +02:00
Yury Bondarenko
3ce98cdbac ESLint: enforce Angular HTML eqeqeq
https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin-template/docs/rules/eqeqeq.md
2023-06-27 17:19:20 +02:00
Yury Bondarenko
741c6009f4 ESLint: fix no-negated-async violations 2023-06-27 17:19:01 +02:00
Yury Bondarenko
fa2c22d240 ESLint: enforce Angular HTML no-negated-async
https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin-template/docs/rules/no-negated-async.md

Prevents us from writing conditions that may pass/fail before an observable has emitted (this is likely to cause unexpected behaviour)
2023-06-27 16:47:09 +02:00
Yury Bondarenko
74e9e5fa14 ESLint: fix ban-types violations 2023-06-27 16:47:09 +02:00
Yury Bondarenko
80c9dae417 ESLint: enforce ban-types
https://typescript-eslint.io/rules/ban-types/
2023-06-27 16:47:09 +02:00
Yury Bondarenko
6f0c1e003d ESLint: fix no-extra-boolean-cast violations 2023-06-27 16:47:09 +02:00
Yury Bondarenko
ad4205073c ESLint: enforce no-extra-boolean-cast
https://eslint.org/docs/latest/rules/no-extra-boolean-cast
2023-06-27 16:47:09 +02:00
Yury Bondarenko
0f1ebfba62 ESLint: fix no-case-declaration violations 2023-06-27 16:47:09 +02:00
Yury Bondarenko
f1a8920d86 ESLint: enforce no-case-declarations
https://eslint.org/docs/latest/rules/no-case-declarations
2023-06-27 16:47:09 +02:00
Yury Bondarenko
95d3ff6569 ESLint: fix prefer-const violations 2023-06-27 16:47:09 +02:00
Yury Bondarenko
702246be38 ESLint: enforce prefer-const where possible
https://eslint.org/docs/latest/rules/prefer-const

In our tests we use this pattern a lot:
```
let something;

beforeEach(() => {
  something = ...;
});
```

This is not valid ~ prefer-const, but can be "let through" with some configuration.
However, this would make the rule too loose for the non-test codebase.

Therefore, we enable it overall but turn it off for tests only -- the `*.spec.ts` overrides apply to test files only and take precedence over `*.ts`
2023-06-27 16:47:09 +02:00
Alexandre Vryghem
928d7a45a3 103176: Fix vcr not being defined yet in OnInit hook 2023-06-27 15:26:49 +02:00
Alexandre Vryghem
7998ef489c 101731: Make ConfirmationModalComponent not dependent on DSpaceObject 2023-06-23 23:38:06 +02:00
Alexandre Vryghem
e5c142ef04 Merge remote-tracking branch 'upstream/main' into improve-confirmation-modal_contribute-main
# Conflicts:
#	src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts
2023-06-23 23:17:42 +02:00
Tim Donohue
884aa07430 Update version tag for development of next release 2023-06-23 13:04:19 -05:00
jeffmorin
de83b35d6c Merge branch 'DSpace:main' into main 2023-05-25 10:52:34 -04:00
Sondissimo
632ef08905 CST-9779 Provided changes to the home component text as required (removed the 2 unwanted paragraphs) 2023-05-24 15:19:56 +02:00
Alexandre Vryghem
822db5120a 101731: Make ConfirmationModalComponent not dependent on DSpaceObject 2023-05-22 17:08:47 +02:00
Sondissimo
06e5c8871b CST-9779 Provided changes to the home component text as required 2023-05-19 11:43:30 +02:00
Sondissimo
83a4165200 CST-9779 Provided changes to Title, repository name, added introduction text with links, as well added the small icon for coar-notify inside the navbar. 2023-05-17 17:42:31 +02:00
jeffmorin
0e2b767c6c Merge branch 'DSpace:main' into main 2023-05-01 08:49:46 -04:00
Jean-François Morin
1cdbfc5bd0 Resolved conflicts 2023-04-20 11:37:51 -04:00
jeffmorin
75ca560587 Merge branch 'DSpace:main' into main 2023-04-11 23:33:41 -04:00
jeffmorin
5a01670260 Merge branch 'DSpace:main' into main 2023-04-05 10:33:19 -04:00
Jean-François Morin
2c0e0ab991 Fixed conflicts and added strings for Content Reports 2023-03-24 11:58:45 -04:00
Jean-François Morin
a105dd5c46 Fixed conflicts 2023-03-24 11:40:35 -04:00
Yury Bondarenko
38a058d7af Add configuration option to disable inlined CSS in SSR HTML
When inlining CSS, Angular Universal needs to extract critical styles.
This seems to take up a significant chunk of processing time.

However, loading may appear less smooth when this feature is disabled.

Added to the configuration to make it easier to A/B test this without a full re-build.
2023-02-01 17:09:22 +01:00
Giuseppe Digilio
ad9bdd6e45 Merge branch 'main' into CST-5337
# Conflicts:
#	src/assets/i18n/en.json5
2023-01-20 08:29:28 +01:00
jeffmorin
2b8c377512 Merge branch 'DSpace:main' into main 2023-01-18 08:34:57 -05:00
jeffmorin
18f913e35f Merge branch 'DSpace:main' into main 2023-01-16 11:11:38 -05:00
jeffmorin
97c6bf5386 Merge branch 'DSpace:main' into main 2023-01-12 14:10:57 -05:00
Jean-François Morin
9677de735c Fixed code style errors 2023-01-12 13:56:47 -05:00
Jean-François Morin
9533713a3d Synchronized API update with REST layer + i18n update in Greek and Ukrainian files 2023-01-12 11:37:45 -05:00
Jean-François Morin
bc730394c6 Merge branch 'main' of github.com:jeffmorin/dspace-angular 2023-01-12 08:59:08 -05:00
Jean-François Morin
be97cad421 Resolved conflict on pl.json5 2023-01-09 09:38:05 -05:00
Jean-François Morin
1d1c713b70 Fixed merge conflict 2022-12-15 13:06:53 -05:00
Jean-François Morin
f37122b23c Content Reports 2022-11-30 15:41:56 -05:00
Giuseppe Digilio
625409cbb0 [CST-5537] Rename rest services to data services 2022-11-17 11:45:59 +01:00
Giuseppe Digilio
b8880091e6 [CST-5537] Fix lint 2022-11-11 14:41:50 +01:00
Giuseppe Digilio
79cd69fb94 [CST-5537] hide the export button from project search 2022-11-11 13:07:52 +01:00
Giuseppe Digilio
9fd4a1feee [CST-5537] Add flag to hide the export button from search results when needed 2022-11-11 13:07:21 +01:00
Giuseppe Digilio
3188374800 [CST-5537] Fix issues and refactoring in order to remove nested subscriptions 2022-11-11 12:56:57 +01:00
Giuseppe Digilio
58db3c7b0e [CST-5337] remove deprecated @Effect decorators 2022-11-10 18:28:10 +01:00
Giuseppe Digilio
5efe3296c6 [CST-5337] Refactoring in order to comply with new data service 2022-11-09 19:05:20 +01:00
Luca Giamminonni
3a70b880cb [CST-5337] Renamed qa endpoints 2022-11-09 15:13:09 +01:00
Luca Giamminonni
50f74cdaf7 Merge remote-tracking branch '4Science-bitbucket/main' into CST-5337 2022-11-09 12:04:55 +01:00
Luca Giamminonni
d8e96d8090 Merge branch 'CST-5337' into CST-5249_suggestion 2022-07-08 18:38:03 +02:00
Luca Giamminonni
7808f85a2d [CST-5337] Moved compute PID href on rest side 2022-07-08 18:30:33 +02:00
Luca Giamminonni
bb357df738 [CST-5337] Added missing typedocs 2022-07-08 18:04:42 +02:00
Luca Giamminonni
de6b533287 Merge remote-tracking branch '4Science-github/CST-5337' into CST-5249_suggestion 2022-07-07 17:41:44 +02:00
Giuseppe Digilio
0942a1e474 [CST-5337] Improve layout 2022-07-07 17:26:58 +02:00
Giuseppe Digilio
92b1ce2d17 [CST-5337] Fix issue with list that wasn't updated after last element has been processed 2022-07-07 17:25:56 +02:00
Giuseppe Digilio
5266194d84 [CST-5337] Update state name 2022-07-07 17:20:17 +02:00
Luca Giamminonni
63bba677bb [CST-5249] Removed Openaire prefix on Suggestion classes 2022-07-07 17:09:48 +02:00
Luca Giamminonni
708cfd1577 Merge branch 'CST-5337' into CST-5249_suggestion 2022-07-07 16:06:49 +02:00
Luca Giamminonni
530db26587 [CST-5337] Fixed lint error 2022-07-06 18:12:15 +02:00
Luca Giamminonni
f2400a714a [CST-5337] Fixed lint and tests 2022-07-06 18:06:11 +02:00
Luca Giamminonni
4a996b492a [CST-5249] Renamed notifications module to suggestion-notifications 2022-07-06 17:57:17 +02:00
Luca Giamminonni
a7d2278d99 [CST-5249] Renamed nbevent and nbtopics 2022-07-06 17:21:34 +02:00
Giuseppe Digilio
a355a15459 [CST-5249] Fix deprecated selector creation 2022-07-06 17:05:03 +02:00
Luca Giamminonni
d5c1b11d77 [CST-5337] Replace Notifications broker with Quality assurance 2022-07-06 17:04:11 +02:00
Luca Giamminonni
df93058881 Merge remote-tracking branch '4Science-bitbucket/main' into CST-5337 2022-07-06 16:39:38 +02:00
Luca Giamminonni
a798deddf2 Merge remote-tracking branch '4Science-bitbucket/main' into CST-5249_suggestion 2022-05-04 15:28:52 +02:00
Luca Giamminonni
11d25e6699 [CST-5249] Fixed suggestion page 2022-05-04 13:16:13 +02:00
Luca Giamminonni
166d77ff82 Merge branch 'CST-5307' into CST-5249_suggestion 2022-05-03 17:50:12 +02:00
Luca Giamminonni
b4d6fbc390 [CST-5249] Fixed compilation issues 2022-05-02 17:40:51 +02:00
Luca Giamminonni
f754b59476 Merge branch 'CST-5307' into CST-5249_suggestion 2022-05-02 10:27:33 +02:00
Luca Giamminonni
9420a12707 Merge remote-tracking branch '4Science-bitbucket/main' into CST-5249_suggestion 2022-05-02 10:25:28 +02:00
Luca Giamminonni
5d8d3e35d5 [CST-5249] Added suggestions from openaire 2022-03-29 16:16:22 +02:00
Luca Giamminonni
a4718b6086 Merge branch 'CST-5337' of https://bitbucket.org/4Science/dspace-angular into CST-5337 2022-03-17 17:01:40 +01:00
Luca Giamminonni
6dfeb1a06b [CST-5337] Fixed notifications labels 2022-03-17 16:53:59 +01:00
Luca Giamminonni
c400026f60 Merge remote-tracking branch '4Science-bitbucket/main' into CST-5337 2022-03-17 16:26:58 +01:00
Pratik Rajkotiya
d63bf55458 [CST-5337] change end point. 2022-03-10 11:56:39 +05:30
Pratik Rajkotiya
00f7fa97f1 [CST-5337] test cases done. 2022-03-10 11:49:12 +05:30
Pratik Rajkotiya
8ce3148dea [CST-5337] OAIRE-ELD correction service should support multiple providers. 2022-03-03 12:11:50 +05:30
Luca Giamminonni
4ca51387d1 [CST-5246] Correction service should support multiple providers 2022-02-18 19:01:30 +01:00
Luca Giamminonni
b8e9f620b6 [CST-5249] Migration of OPENAIRE correction service 2022-02-18 17:26:59 +01:00
3238 changed files with 131151 additions and 54945 deletions

View File

@@ -8,7 +8,10 @@
"eslint-plugin-deprecation", "eslint-plugin-deprecation",
"unused-imports", "unused-imports",
"eslint-plugin-lodash", "eslint-plugin-lodash",
"eslint-plugin-jsonc" "eslint-plugin-jsonc",
"eslint-plugin-rxjs",
"eslint-plugin-simple-import-sort",
"eslint-plugin-import-newlines"
], ],
"overrides": [ "overrides": [
{ {
@@ -27,17 +30,29 @@
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking", "plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:@angular-eslint/recommended", "plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates" "plugin:@angular-eslint/template/process-inline-templates",
"plugin:rxjs/recommended"
], ],
"rules": { "rules": {
"indent": [
"error",
2,
{
"SwitchCase": 1
}
],
"max-classes-per-file": [ "max-classes-per-file": [
"error", "error",
1 1
], ],
"comma-dangle": [ "comma-dangle": [
"off", "error",
"always-multiline" "always-multiline"
], ],
"object-curly-spacing": [
"error",
"always"
],
"eol-last": [ "eol-last": [
"error", "error",
"always" "always"
@@ -104,15 +119,13 @@
"allowTernary": true "allowTernary": true
} }
], ],
"prefer-const": "off", // todo: re-enable & fix errors (more strict than it used to be in TSLint) "prefer-const": "error",
"no-case-declarations": "error",
"no-extra-boolean-cast": "error",
"prefer-spread": "off", "prefer-spread": "off",
"no-underscore-dangle": "off", "no-underscore-dangle": "off",
// todo: disabled rules from eslint:recommended, consider re-enabling & fixing
"no-prototype-builtins": "off", "no-prototype-builtins": "off",
"no-useless-escape": "off", "no-useless-escape": "off",
"no-case-declarations": "off",
"no-extra-boolean-cast": "off",
"@angular-eslint/directive-selector": [ "@angular-eslint/directive-selector": [
"error", "error",
@@ -139,7 +152,6 @@
} }
], ],
"@angular-eslint/no-attribute-decorator": "error", "@angular-eslint/no-attribute-decorator": "error",
"@angular-eslint/no-forward-ref": "error",
"@angular-eslint/no-output-native": "warn", "@angular-eslint/no-output-native": "warn",
"@angular-eslint/no-output-on-prefix": "warn", "@angular-eslint/no-output-on-prefix": "warn",
"@angular-eslint/no-conflicting-lifecycle": "warn", "@angular-eslint/no-conflicting-lifecycle": "warn",
@@ -183,7 +195,7 @@
], ],
"@typescript-eslint/type-annotation-spacing": "error", "@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unified-signatures": "error", "@typescript-eslint/unified-signatures": "error",
"@typescript-eslint/ban-types": "warn", // todo: deal with {} type issues & re-enable "@typescript-eslint/ban-types": "error",
"@typescript-eslint/no-floating-promises": "warn", "@typescript-eslint/no-floating-promises": "warn",
"@typescript-eslint/no-misused-promises": "warn", "@typescript-eslint/no-misused-promises": "warn",
"@typescript-eslint/restrict-plus-operands": "warn", "@typescript-eslint/restrict-plus-operands": "warn",
@@ -203,14 +215,45 @@
"deprecation/deprecation": "warn", "deprecation/deprecation": "warn",
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"import/order": "off", "import/order": "off",
"import/first": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"import/no-deprecated": "warn", "import/no-deprecated": "warn",
"import/no-namespace": "error", "import/no-namespace": "error",
"import-newlines/enforce": [
"error",
{
"items": 1,
"semi": true,
"forceSingleLine": true
}
],
"unused-imports/no-unused-imports": "error", "unused-imports/no-unused-imports": "error",
"lodash/import-scope": [ "lodash/import-scope": [
"error", "error",
"method" "method"
] ],
"rxjs/no-nested-subscribe": "off" // todo: go over _all_ cases
}
},
{
"files": [
"*.spec.ts"
],
"parserOptions": {
"project": [
"./tsconfig.json",
"./cypress/tsconfig.json"
],
"createDefaultProgram": true
},
"rules": {
"prefer-const": "off"
} }
}, },
{ {
@@ -219,12 +262,7 @@
], ],
"extends": [ "extends": [
"plugin:@angular-eslint/template/recommended" "plugin:@angular-eslint/template/recommended"
], ]
"rules": {
// todo: re-enable & fix errors
"@angular-eslint/template/no-negated-async": "off",
"@angular-eslint/template/eqeqeq": "off"
}
}, },
{ {
"files": [ "files": [

View File

@@ -1,26 +0,0 @@
# This workflow runs whenever a new pull request is created
# TEMPORARILY DISABLED. Unfortunately this doesn't work for PRs created from forked repositories (which is how we tend to create PRs).
# There is no known workaround yet. See https://github.community/t/how-to-use-github-token-for-prs-from-forks/16818
name: Pull Request opened
# Only run for newly opened PRs against the "main" branch
on:
pull_request:
types: [opened]
branches:
- main
jobs:
automation:
runs-on: ubuntu-latest
steps:
# Assign the PR to whomever created it. This is useful for visualizing assignments on project boards
# See https://github.com/marketplace/actions/pull-request-assigner
- name: Assign PR to creator
uses: thomaseizinger/assign-pr-creator-action@v1.0.0
# Note, this authentication token is created automatically
# See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Ignore errors. It is possible the PR was created by someone who cannot be assigned
continue-on-error: true

View File

@@ -33,21 +33,23 @@ jobs:
#CHROME_VERSION: "90.0.4430.212-1" #CHROME_VERSION: "90.0.4430.212-1"
# Bump Node heap size (OOM in CI after upgrading to Angular 15) # Bump Node heap size (OOM in CI after upgrading to Angular 15)
NODE_OPTIONS: '--max-old-space-size=4096' NODE_OPTIONS: '--max-old-space-size=4096'
# Project name to use when running "docker compose" prior to e2e tests
COMPOSE_PROJECT_NAME: 'ci'
strategy: strategy:
# Create a matrix of Node versions to test against (in parallel) # Create a matrix of Node versions to test against (in parallel)
matrix: matrix:
node-version: [16.x, 18.x] node-version: [18.x, 20.x]
# Do NOT exit immediately if one matrix job fails # Do NOT exit immediately if one matrix job fails
fail-fast: false fail-fast: false
# These are the actual CI steps to perform per job # These are the actual CI steps to perform per job
steps: steps:
# https://github.com/actions/checkout # https://github.com/actions/checkout
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v3 uses: actions/checkout@v4
# https://github.com/actions/setup-node # https://github.com/actions/setup-node
- name: Install Node.js ${{ matrix.node-version }} - name: Install Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
@@ -106,19 +108,19 @@ jobs:
path: 'coverage/dspace-angular/lcov.info' path: 'coverage/dspace-angular/lcov.info'
retention-days: 14 retention-days: 14
# Using docker-compose start backend using CI configuration # Using "docker compose" start backend using CI configuration
# and load assetstore from a cached copy # and load assetstore from a cached copy
- name: Start DSpace REST Backend via Docker (for e2e tests) - name: Start DSpace REST Backend via Docker (for e2e tests)
run: | run: |
docker-compose -f ./docker/docker-compose-ci.yml up -d docker compose -f ./docker/docker-compose-ci.yml up -d
docker-compose -f ./docker/cli.yml -f ./docker/cli.assetstore.yml run --rm dspace-cli docker compose -f ./docker/cli.yml -f ./docker/cli.assetstore.yml run --rm dspace-cli
docker container ls docker container ls
# Run integration tests via Cypress.io # Run integration tests via Cypress.io
# https://github.com/cypress-io/github-action # https://github.com/cypress-io/github-action
# (NOTE: to run these e2e tests locally, just use 'ng e2e') # (NOTE: to run these e2e tests locally, just use 'ng e2e')
- name: Run e2e tests (integration tests) - name: Run e2e tests (integration tests)
uses: cypress-io/github-action@v5 uses: cypress-io/github-action@v6
with: with:
# Run tests in Chrome, headless mode (default) # Run tests in Chrome, headless mode (default)
browser: chrome browser: chrome
@@ -180,7 +182,7 @@ jobs:
run: kill -9 $(lsof -t -i:4000) run: kill -9 $(lsof -t -i:4000)
- name: Shutdown Docker containers - name: Shutdown Docker containers
run: docker-compose -f ./docker/docker-compose-ci.yml down run: docker compose -f ./docker/docker-compose-ci.yml down
# Codecov upload is a separate job in order to allow us to restart this separate from the entire build/test # Codecov upload is a separate job in order to allow us to restart this separate from the entire build/test
# job above. This is necessary because Codecov uploads seem to randomly fail at times. # job above. This is necessary because Codecov uploads seem to randomly fail at times.
@@ -191,7 +193,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
# Download artifacts from previous 'tests' job # Download artifacts from previous 'tests' job
- name: Download coverage artifacts - name: Download coverage artifacts
@@ -203,10 +205,14 @@ jobs:
# Retry action: https://github.com/marketplace/actions/retry-action # Retry action: https://github.com/marketplace/actions/retry-action
# Codecov action: https://github.com/codecov/codecov-action # Codecov action: https://github.com/codecov/codecov-action
- name: Upload coverage to Codecov.io - name: Upload coverage to Codecov.io
uses: Wandalen/wretry.action@v1.0.36 uses: Wandalen/wretry.action@v1.3.0
with: with:
action: codecov/codecov-action@v3 action: codecov/codecov-action@v3
# Try upload 5 times max # Ensure codecov-action throws an error when it fails to upload
# This allows us to auto-restart the action if an error is thrown
with: |
fail_ci_if_error: true
# Try re-running action 5 times max
attempt_limit: 5 attempt_limit: 5
# Run again in 30 seconds # Run again in 30 seconds
attempt_delay: 30000 attempt_delay: 30000

View File

@@ -5,12 +5,16 @@
# because CodeQL requires a fresh build with all tests *disabled*. # because CodeQL requires a fresh build with all tests *disabled*.
name: "Code Scanning" name: "Code Scanning"
# Run this code scan for all pushes / PRs to main branch. Also run once a week. # Run this code scan for all pushes / PRs to main or maintenance branches. Also run once a week.
on: on:
push: push:
branches: [ main ] branches:
- main
- 'dspace-**'
pull_request: pull_request:
branches: [ main ] branches:
- main
- 'dspace-**'
# Don't run if PR is only updating static documentation # Don't run if PR is only updating static documentation
paths-ignore: paths-ignore:
- '**/*.md' - '**/*.md'
@@ -31,7 +35,7 @@ jobs:
steps: steps:
# https://github.com/actions/checkout # https://github.com/actions/checkout
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
# https://github.com/github/codeql-action # https://github.com/github/codeql-action

View File

@@ -3,6 +3,9 @@ name: Docker images
# Run this Build for all pushes to 'main' or maintenance branches, or tagged releases. # Run this Build for all pushes to 'main' or maintenance branches, or tagged releases.
# Also run for PRs to ensure PR doesn't break Docker build process # Also run for PRs to ensure PR doesn't break Docker build process
# NOTE: uses "reusable-docker-build.yml" in DSpace/DSpace to actually build each of the Docker images
# https://github.com/DSpace/DSpace/blob/main/.github/workflows/reusable-docker-build.yml
#
on: on:
push: push:
branches: branches:
@@ -16,105 +19,41 @@ permissions:
contents: read # to fetch code (actions/checkout) contents: read # to fetch code (actions/checkout)
jobs: jobs:
docker: #############################################################
# Build/Push the 'dspace/dspace-angular' image
#############################################################
dspace-angular:
# Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace-angular' # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace-angular'
if: github.repository == 'dspace/dspace-angular' if: github.repository == 'dspace/dspace-angular'
runs-on: ubuntu-latest # Use the reusable-docker-build.yml script from DSpace/DSpace repo to build our Docker image
env: uses: DSpace/DSpace/.github/workflows/reusable-docker-build.yml@main
# Define tags to use for Docker images based on Git tags/branches (for docker/metadata-action)
# For a new commit on default branch (main), use the literal tag 'dspace-7_x' on Docker image.
# For a new commit on other branches, use the branch name as the tag for Docker image.
# For a new tag, copy that tag name as the tag for Docker image.
IMAGE_TAGS: |
type=raw,value=dspace-7_x,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=branch,enable=${{ !endsWith(github.ref, github.event.repository.default_branch) }}
type=ref,event=tag
# Define default tag "flavor" for docker/metadata-action per
# https://github.com/docker/metadata-action#flavor-input
# We turn off 'latest' tag by default.
TAGS_FLAVOR: |
latest=false
# Architectures / Platforms for which we will build Docker images
# If this is a PR, we ONLY build for AMD64. For PRs we only do a sanity check test to ensure Docker builds work.
# If this is NOT a PR (e.g. a tag or merge commit), also build for ARM64.
PLATFORMS: linux/amd64${{ github.event_name != 'pull_request' && ', linux/arm64' || '' }}
steps:
# https://github.com/actions/checkout
- name: Checkout codebase
uses: actions/checkout@v3
# https://github.com/docker/setup-buildx-action
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
# https://github.com/docker/setup-qemu-action
- name: Set up QEMU emulation to build for multiple architectures
uses: docker/setup-qemu-action@v2
# https://github.com/docker/login-action
- name: Login to DockerHub
# Only login if not a PR, as PRs only trigger a Docker build and not a push
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_USERNAME }} build_id: dspace-angular
password: ${{ secrets.DOCKER_ACCESS_TOKEN }} image_name: dspace/dspace-angular
dockerfile_path: ./Dockerfile
secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
############################################### #############################################################
# Build/Push the 'dspace/dspace-angular' image
###############################################
# https://github.com/docker/metadata-action
# Get Metadata for docker_build step below
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-angular' image
id: meta_build
uses: docker/metadata-action@v4
with:
images: dspace/dspace-angular
tags: ${{ env.IMAGE_TAGS }}
flavor: ${{ env.TAGS_FLAVOR }}
# https://github.com/docker/build-push-action
- name: Build and push 'dspace-angular' image
id: docker_build
uses: docker/build-push-action@v3
with:
context: .
file: ./Dockerfile
platforms: ${{ env.PLATFORMS }}
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build.outputs.tags }}
labels: ${{ steps.meta_build.outputs.labels }}
#####################################################
# Build/Push the 'dspace/dspace-angular' image ('-dist' tag) # Build/Push the 'dspace/dspace-angular' image ('-dist' tag)
##################################################### #############################################################
# https://github.com/docker/metadata-action dspace-angular-dist:
# Get Metadata for docker_build_dist step below # Ensure this job never runs on forked repos. It's only executed for 'dspace/dspace-angular'
- name: Sync metadata (tags, labels) from GitHub to Docker for 'dspace-angular-dist' image if: github.repository == 'dspace/dspace-angular'
id: meta_build_dist # Use the reusable-docker-build.yml script from DSpace/DSpace repo to build our Docker image
uses: docker/metadata-action@v4 uses: DSpace/DSpace/.github/workflows/reusable-docker-build.yml@main
with: with:
images: dspace/dspace-angular build_id: dspace-angular-dist
tags: ${{ env.IMAGE_TAGS }} image_name: dspace/dspace-angular
dockerfile_path: ./Dockerfile.dist
# As this is a "dist" image, its tags are all suffixed with "-dist". Otherwise, it uses the same # As this is a "dist" image, its tags are all suffixed with "-dist". Otherwise, it uses the same
# tagging logic as the primary 'dspace/dspace-angular' image above. # tagging logic as the primary 'dspace/dspace-angular' image above.
flavor: ${{ env.TAGS_FLAVOR }} tags_flavor: suffix=-dist
suffix=-dist secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
- name: Build and push 'dspace-angular-dist' image DOCKER_ACCESS_TOKEN: ${{ secrets.DOCKER_ACCESS_TOKEN }}
id: docker_build_dist # Enable redeploy of sandbox & demo if the branch for this image matches the deployment branch of
uses: docker/build-push-action@v3 # these sites as specified in reusable-docker-build.xml
with: REDEPLOY_SANDBOX_URL: ${{ secrets.REDEPLOY_SANDBOX_URL }}
context: . REDEPLOY_DEMO_URL: ${{ secrets.REDEPLOY_DEMO_URL }}
file: ./Dockerfile.dist
platforms: ${{ env.PLATFORMS }}
# For pull requests, we run the Docker build (to ensure no PR changes break the build),
# but we ONLY do an image push to DockerHub if it's NOT a PR
push: ${{ github.event_name != 'pull_request' }}
# Use tags / labels provided by 'docker/metadata-action' above
tags: ${{ steps.meta_build_dist.outputs.tags }}
labels: ${{ steps.meta_build_dist.outputs.labels }}

View File

@@ -1,11 +1,12 @@
# This workflow checks open PRs for merge conflicts and labels them when conflicts are found # This workflow checks open PRs for merge conflicts and labels them when conflicts are found
name: Check for merge conflicts name: Check for merge conflicts
# Run whenever the "main" branch is updated # Run this for all pushes (i.e. merges) to 'main' or maintenance branches
# NOTE: This means merge conflicts are only checked for when a PR is merged to main.
on: on:
push: push:
branches: [ main ] branches:
- main
- 'dspace-**'
# So that the `conflict_label_name` is removed if conflicts are resolved, # So that the `conflict_label_name` is removed if conflicts are resolved,
# we allow this to run for `pull_request_target` so that github secrets are available. # we allow this to run for `pull_request_target` so that github secrets are available.
pull_request_target: pull_request_target:
@@ -24,6 +25,8 @@ jobs:
# See: https://github.com/prince-chrismc/label-merge-conflicts-action # See: https://github.com/prince-chrismc/label-merge-conflicts-action
- name: Auto-label PRs with merge conflicts - name: Auto-label PRs with merge conflicts
uses: prince-chrismc/label-merge-conflicts-action@v3 uses: prince-chrismc/label-merge-conflicts-action@v3
# Ignore any failures -- may occur (randomly?) for older, outdated PRs.
continue-on-error: true
# Add "merge conflict" label if a merge conflict is detected. Remove it when resolved. # Add "merge conflict" label if a merge conflict is detected. Remove it when resolved.
# Note, the authentication token is created automatically # Note, the authentication token is created automatically
# See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token # See: https://docs.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token

View File

@@ -0,0 +1,46 @@
# This workflow will attempt to port a merged pull request to
# the branch specified in a "port to" label (if exists)
name: Port merged Pull Request
# Only run for merged PRs against the "main" or maintenance branches
# We allow this to run for `pull_request_target` so that github secrets are available
# (This is required when the PR comes from a forked repo)
on:
pull_request_target:
types: [ closed ]
branches:
- main
- 'dspace-**'
permissions:
contents: write # so action can add comments
pull-requests: write # so action can create pull requests
jobs:
port_pr:
runs-on: ubuntu-latest
# Don't run on closed *unmerged* pull requests
if: github.event.pull_request.merged
steps:
# Checkout code
- uses: actions/checkout@v4
# Port PR to other branch (ONLY if labeled with "port to")
# See https://github.com/korthout/backport-action
- name: Create backport pull requests
uses: korthout/backport-action@v2
with:
# Trigger based on a "port to [branch]" label on PR
# (This label must specify the branch name to port to)
label_pattern: '^port to ([^ ]+)$'
# Title to add to the (newly created) port PR
pull_title: '[Port ${target_branch}] ${pull_title}'
# Description to add to the (newly created) port PR
pull_description: 'Port of #${pull_number} by @${pull_author} to `${target_branch}`.'
# Copy all labels from original PR to (newly created) port PR
# NOTE: The labels matching 'label_pattern' are automatically excluded
copy_labels_pattern: '.*'
# Skip any merge commits in the ported PR. This means only non-merge commits are cherry-picked to the new PR
merge_commits: 'skip'
# Use a personal access token (PAT) to create PR as 'dspace-bot' user.
# A PAT is required in order for the new PR to trigger its own actions (for CI checks)
github_token: ${{ secrets.PR_PORT_TOKEN }}

View File

@@ -0,0 +1,24 @@
# This workflow runs whenever a new pull request is created
name: Pull Request opened
# Only run for newly opened PRs against the "main" or maintenance branches
# We allow this to run for `pull_request_target` so that github secrets are available
# (This is required to assign a PR back to the creator when the PR comes from a forked repo)
on:
pull_request_target:
types: [ opened ]
branches:
- main
- 'dspace-**'
permissions:
pull-requests: write
jobs:
automation:
runs-on: ubuntu-latest
steps:
# Assign the PR to whomever created it. This is useful for visualizing assignments on project boards
# See https://github.com/toshimaru/auto-author-assign
- name: Assign PR to creator
uses: toshimaru/auto-author-assign@v2.0.1

View File

@@ -2,7 +2,7 @@
# See https://github.com/DSpace/dspace-angular/tree/main/docker for usage details # See https://github.com/DSpace/dspace-angular/tree/main/docker for usage details
# Test build: # Test build:
# docker build -f Dockerfile.dist -t dspace/dspace-angular:dspace-7_x-dist . # docker build -f Dockerfile.dist -t dspace/dspace-angular:latest-dist .
FROM node:18-alpine as build FROM node:18-alpine as build

View File

@@ -157,8 +157,8 @@ DSPACE_UI_SSL => DSPACE_SSL
The same settings can also be overwritten by setting system environment variables instead, E.g.: The same settings can also be overwritten by setting system environment variables instead, E.g.:
```bash ```bash
export DSPACE_HOST=api7.dspace.org export DSPACE_HOST=demo.dspace.org
export DSPACE_UI_PORT=4200 export DSPACE_UI_PORT=4000
``` ```
The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `DSPACE_APP_CONFIG_PATH` overrides **`config.(prod or dev).yml`** The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `DSPACE_APP_CONFIG_PATH` overrides **`config.(prod or dev).yml`**
@@ -288,7 +288,7 @@ E2E tests (aka integration tests) use [Cypress.io](https://www.cypress.io/). Con
The test files can be found in the `./cypress/integration/` folder. The test files can be found in the `./cypress/integration/` folder.
Before you can run e2e tests, two things are REQUIRED: Before you can run e2e tests, two things are REQUIRED:
1. You MUST be running the DSpace backend (i.e. REST API) locally. The e2e tests will *NOT* succeed if run against our demo REST API (https://api7.dspace.org/server/), as that server is uncontrolled and may have content added/removed at any time. 1. You MUST be running the DSpace backend (i.e. REST API) locally. The e2e tests will *NOT* succeed if run against our demo/sandbox REST API (https://demo.dspace.org/server/ or https://sandbox.dspace.org/server/), as those sites may have content added/removed at any time.
* After starting up your backend on localhost, make sure either your `config.prod.yml` or `config.dev.yml` has its `rest` settings defined to use that localhost backend. * After starting up your backend on localhost, make sure either your `config.prod.yml` or `config.dev.yml` has its `rest` settings defined to use that localhost backend.
* If you'd prefer, you may instead use environment variables as described at [Configuring](#configuring). For example: * If you'd prefer, you may instead use environment variables as described at [Configuring](#configuring). For example:
``` ```

View File

@@ -17,12 +17,19 @@ ui:
# Trust X-FORWARDED-* headers from proxies (default = true) # Trust X-FORWARDED-* headers from proxies (default = true)
useProxies: true useProxies: true
universal:
# Whether to inline "critical" styles into the server-side rendered HTML.
# Determining which styles are critical is a relatively expensive operation;
# this option can be disabled to boost server performance at the expense of
# loading smoothness.
inlineCriticalCss: true
# The REST API server settings # The REST API server settings
# NOTE: these settings define which (publicly available) REST API to use. They are usually # NOTE: these settings define which (publicly available) REST API to use. They are usually
# 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. # 'synced' with the 'dspace.server.url' setting in your backend's local.cfg.
rest: rest:
ssl: true ssl: true
host: api7.dspace.org host: sandbox.dspace.org
port: 443 port: 443
# NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript # NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
nameSpace: /server nameSpace: /server
@@ -131,12 +138,16 @@ submission:
# NOTE: after how many time (milliseconds) submission is saved automatically # NOTE: after how many time (milliseconds) submission is saved automatically
# eg. timer: 5 * (1000 * 60); // 5 minutes # eg. timer: 5 * (1000 * 60); // 5 minutes
timer: 0 timer: 0
# Always show the duplicate detection section if enabled, even if there are no potential duplicates detected
# (a message will be displayed to indicate no matches were found)
duplicateDetection:
alwaysShowSection: false
icons: icons:
metadata: metadata:
# NOTE: example of configuration # NOTE: example of configuration
# # NOTE: metadata name # # NOTE: metadata name
# - name: dc.author # - name: dc.author
# # NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used # # NOTE: fontawesome (v6.x) icon classes and bootstrap utility classes can be used
# style: fas fa-user # style: fas fa-user
- name: dc.author - name: dc.author
style: fas fa-user style: fas fa-user
@@ -147,18 +158,40 @@ submission:
confidence: confidence:
# NOTE: example of configuration # NOTE: example of configuration
# # NOTE: confidence value # # NOTE: confidence value
# - name: dc.author # - value: 600
# # NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used # # NOTE: fontawesome (v6.x) icon classes and bootstrap utility classes can be used
# style: fa-user # style: text-success
# icon: fa-circle-check
# # NOTE: the class configured in property style is used by default, the icon property could be used in component
# configured to use a 'icon mode' display (mainly in edit-item page)
- value: 600 - value: 600
style: text-success style: text-success
icon: fa-circle-check
- value: 500 - value: 500
style: text-info style: text-info
icon: fa-gear
- value: 400 - value: 400
style: text-warning style: text-warning
icon: fa-circle-question
- value: 300
style: text-muted
icon: fa-thumbs-down
- value: 200
style: text-muted
icon: fa-circle-exclamation
- value: 100
style: text-muted
icon: fa-circle-stop
- value: 0
style: text-muted
icon: fa-ban
- value: -1
style: text-muted
icon: fa-circle-xmark
# default configuration # default configuration
- value: default - value: default
style: text-muted style: text-muted
icon: fa-circle-xmark
# Default Language in which the UI will be rendered if the user's browser language is not an active language # Default Language in which the UI will be rendered if the user's browser language is not an active language
defaultLanguage: en defaultLanguage: en
@@ -208,6 +241,9 @@ languages:
- code: pt-BR - code: pt-BR
label: Português do Brasil label: Português do Brasil
active: true active: true
- code: sr-lat
label: Srpski (lat)
active: true
- code: fi - code: fi
label: Suomi label: Suomi
active: true active: true
@@ -232,6 +268,9 @@ languages:
- code: el - code: el
label: Ελληνικά label: Ελληνικά
active: true active: true
- code: sr-cyr
label: Српски
active: true
- code: uk - code: uk
label: раї́нська label: раї́нська
active: true active: true
@@ -266,6 +305,8 @@ homePage:
# No. of communities to list per page on the home page # No. of communities to list per page on the home page
# This will always round to the nearest number from the list of page sizes. e.g. if you set it to 7 it'll use 10 # This will always round to the nearest number from the list of page sizes. e.g. if you set it to 7 it'll use 10
pageSize: 5 pageSize: 5
# Enable or disable the Discover filters on the homepage
showDiscoverFilters: false
# Item Config # Item Config
item: item:
@@ -279,8 +320,17 @@ item:
# settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. # settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'.
pageSize: 5 pageSize: 5
# Community Page Config
community:
# Search tab config
searchSection:
showSidebar: true
# Collection Page Config # Collection Page Config
collection: collection:
# Search tab config
searchSection:
showSidebar: true
edit: edit:
undoTimeout: 10000 # 10 seconds undoTimeout: 10000 # 10 seconds
@@ -292,33 +342,33 @@ themes:
# #
# # A theme with a handle property will match the community, collection or item with the given # # A theme with a handle property will match the community, collection or item with the given
# # handle, and all collections and/or items within it # # handle, and all collections and/or items within it
# - name: 'custom', # - name: custom
# handle: '10673/1233' # handle: 10673/1233
# #
# # A theme with a regex property will match the route using a regular expression. If it # # A theme with a regex property will match the route using a regular expression. If it
# # matches the route for a community or collection it will also apply to all collections # # matches the route for a community or collection it will also apply to all collections
# # and/or items within it # # and/or items within it
# - name: 'custom', # - name: custom
# regex: 'collections\/e8043bc2.*' # regex: collections\/e8043bc2.*
# #
# # A theme with a uuid property will match the community, collection or item with the given # # A theme with a uuid property will match the community, collection or item with the given
# # ID, and all collections and/or items within it # # ID, and all collections and/or items within it
# - name: 'custom', # - name: custom
# uuid: '0958c910-2037-42a9-81c7-dca80e3892b4' # uuid: 0958c910-2037-42a9-81c7-dca80e3892b4
# #
# # The extends property specifies an ancestor theme (by name). Whenever a themed component is not found # # The extends property specifies an ancestor theme (by name). Whenever a themed component is not found
# # in the current theme, its ancestor theme(s) will be checked recursively before falling back to default. # # in the current theme, its ancestor theme(s) will be checked recursively before falling back to default.
# - name: 'custom-A', # - name: custom-A
# extends: 'custom-B', # extends: custom-B
# # Any of the matching properties above can be used # # Any of the matching properties above can be used
# handle: '10673/34' # handle: 10673/34
# #
# - name: 'custom-B', # - name: custom-B
# extends: 'custom', # extends: custom
# handle: '10673/12' # handle: 10673/12
# #
# # A theme with only a name will match every route # # A theme with only a name will match every route
# name: 'custom' # name: custom
# #
# # This theme will use the default bootstrap styling for DSpace components # # This theme will use the default bootstrap styling for DSpace components
# - name: BASE_THEME_NAME # - name: BASE_THEME_NAME
@@ -380,3 +430,75 @@ vocabularies:
comcolSelectionSort: comcolSelectionSort:
sortField: 'dc.title' sortField: 'dc.title'
sortDirection: 'ASC' sortDirection: 'ASC'
# Example of fallback collection for suggestions import
# suggestion:
# - collectionId: 8f7df5ca-f9c2-47a4-81ec-8a6393d6e5af
# source: "openaire"
# Search settings
search:
# Settings to enable/disable or configure advanced search filters.
advancedFilters:
enabled: false
# List of filters to enable in "Advanced Search" dropdown
filter: [ 'title', 'author', 'subject', 'entityType' ]
# Notify metrics
# Configuration for Notify Admin Dashboard for metrics visualization
notifyMetrics:
# Configuration for received messages
- title: 'admin-notify-dashboard.received-ldn'
boxes:
- color: '#B8DAFF'
title: 'admin-notify-dashboard.NOTIFY.incoming.accepted'
config: 'NOTIFY.incoming.accepted'
description: 'admin-notify-dashboard.NOTIFY.incoming.accepted.description'
- color: '#D4EDDA'
title: 'admin-notify-dashboard.NOTIFY.incoming.processed'
config: 'NOTIFY.incoming.processed'
description: 'admin-notify-dashboard.NOTIFY.incoming.processed.description'
- color: '#FDBBC7'
title: 'admin-notify-dashboard.NOTIFY.incoming.failure'
config: 'NOTIFY.incoming.failure'
description: 'admin-notify-dashboard.NOTIFY.incoming.failure.description'
- color: '#FDBBC7'
title: 'admin-notify-dashboard.NOTIFY.incoming.untrusted'
config: 'NOTIFY.incoming.untrusted'
description: 'admin-notify-dashboard.NOTIFY.incoming.untrusted.description'
- color: '#43515F'
title: 'admin-notify-dashboard.NOTIFY.incoming.involvedItems'
textColor: '#fff'
config: 'NOTIFY.incoming.involvedItems'
description: 'admin-notify-dashboard.NOTIFY.incoming.involvedItems.description'
# Configuration for outgoing messages
- title: 'admin-notify-dashboard.generated-ldn'
boxes:
- color: '#B8DAFF'
title: 'admin-notify-dashboard.NOTIFY.outgoing.queued'
config: 'NOTIFY.outgoing.queued'
description: 'admin-notify-dashboard.NOTIFY.outgoing.queued.description'
- color: '#FDEEBB'
title: 'admin-notify-dashboard.NOTIFY.outgoing.queued_for_retry'
config: 'NOTIFY.outgoing.queued_for_retry'
description: 'admin-notify-dashboard.NOTIFY.outgoing.queued_for_retry.description'
- color: '#FDBBC7'
title: 'admin-notify-dashboard.NOTIFY.outgoing.failure'
config: 'NOTIFY.outgoing.failure'
description: 'admin-notify-dashboard.NOTIFY.outgoing.failure.description'
- color: '#43515F'
title: 'admin-notify-dashboard.NOTIFY.outgoing.involvedItems'
textColor: '#fff'
config: 'NOTIFY.outgoing.involvedItems'
description: 'admin-notify-dashboard.NOTIFY.outgoing.involvedItems.description'
- color: '#D4EDDA'
title: 'admin-notify-dashboard.NOTIFY.outgoing.delivered'
config: 'NOTIFY.outgoing.delivered'
description: 'admin-notify-dashboard.NOTIFY.outgoing.delivered.description'

View File

@@ -1,5 +1,5 @@
rest: rest:
ssl: true ssl: true
host: api7.dspace.org host: sandbox.dspace.org
port: 443 port: 443
nameSpace: /server nameSpace: /server

View File

@@ -9,8 +9,9 @@ export default defineConfig({
openMode: 0, openMode: 0,
}, },
env: { env: {
// Global constants used in DSpace e2e tests (see also ./cypress/support/e2e.ts) // Global DSpace environment variables used in all our Cypress e2e tests
// May be overridden in our cypress.json config file using specified environment variables. // May be modified in this config, or overridden in a variety of ways.
// See Cypress environment variable docs: https://docs.cypress.io/guides/guides/environment-variables
// Default values listed here are all valid for the Demo Entities Data set available at // Default values listed here are all valid for the Demo Entities Data set available at
// https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data // https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data
// (This is the data set used in our CI environment) // (This is the data set used in our CI environment)
@@ -21,12 +22,14 @@ export default defineConfig({
// Community/collection/publication used for view/edit tests // Community/collection/publication used for view/edit tests
DSPACE_TEST_COMMUNITY: '0958c910-2037-42a9-81c7-dca80e3892b4', DSPACE_TEST_COMMUNITY: '0958c910-2037-42a9-81c7-dca80e3892b4',
DSPACE_TEST_COLLECTION: '282164f5-d325-4740-8dd1-fa4d6d3e7200', DSPACE_TEST_COLLECTION: '282164f5-d325-4740-8dd1-fa4d6d3e7200',
DSPACE_TEST_ENTITY_PUBLICATION: 'e98b0f27-5c19-49a0-960d-eb6ad5287067', DSPACE_TEST_ENTITY_PUBLICATION: '6160810f-1e53-40db-81ef-f6621a727398',
// Search term (should return results) used in search tests // Search term (should return results) used in search tests
DSPACE_TEST_SEARCH_TERM: 'test', DSPACE_TEST_SEARCH_TERM: 'test',
// Collection used for submission tests // Main Collection used for submission tests. Should be able to accept normal Item objects
DSPACE_TEST_SUBMIT_COLLECTION_NAME: 'Sample Collection', DSPACE_TEST_SUBMIT_COLLECTION_NAME: 'Sample Collection',
DSPACE_TEST_SUBMIT_COLLECTION_UUID: '9d8334e9-25d3-4a67-9cea-3dffdef80144', DSPACE_TEST_SUBMIT_COLLECTION_UUID: '9d8334e9-25d3-4a67-9cea-3dffdef80144',
// Collection used for Person entity submission tests. MUST be configured with EntityType=Person.
DSPACE_TEST_SUBMIT_PERSON_COLLECTION_NAME: 'People',
// Account used to test basic submission process // Account used to test basic submission process
DSPACE_TEST_SUBMIT_USER: 'dspacedemo+submit@gmail.com', DSPACE_TEST_SUBMIT_USER: 'dspacedemo+submit@gmail.com',
DSPACE_TEST_SUBMIT_USER_PASSWORD: 'dspace', DSPACE_TEST_SUBMIT_USER_PASSWORD: 'dspace',

View File

@@ -0,0 +1,28 @@
import { testA11y } from 'cypress/support/utils';
import { Options } from 'cypress-axe';
describe('Admin Sidebar', () => {
beforeEach(() => {
// Must login as an Admin for sidebar to appear
cy.visit('/login');
cy.loginViaForm(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD'));
});
it('should be pinnable and pass accessibility tests', () => {
// Pin the sidebar open
cy.get('#sidebar-collapse-toggle').click();
// Click on every expandable section to open all menus
cy.get('ds-expandable-admin-sidebar-section').click({ multiple: true });
// Analyze <ds-admin-sidebar> for accessibility
testA11y('ds-admin-sidebar',
{
rules: {
// Currently all expandable sections have nested interactive elements
// See https://github.com/DSpace/dspace-angular/issues/2178
'nested-interactive': { enabled: false },
},
} as Options);
});
});

View File

@@ -1,10 +1,9 @@
import { TEST_ENTITY_PUBLICATION } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
describe('Breadcrumbs', () => { describe('Breadcrumbs', () => {
it('should pass accessibility tests', () => { it('should pass accessibility tests', () => {
// Visit an Item, as those have more breadcrumbs // Visit an Item, as those have more breadcrumbs
cy.visit('/entities/publication/'.concat(TEST_ENTITY_PUBLICATION)); cy.visit('/entities/publication/'.concat(Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION')));
// Wait for breadcrumbs to be visible // Wait for breadcrumbs to be visible
cy.get('ds-breadcrumbs').should('be.visible'); cy.get('ds-breadcrumbs').should('be.visible');

View File

@@ -5,9 +5,9 @@ describe('Browse By Author', () => {
cy.visit('/browse/author'); cy.visit('/browse/author');
// Wait for <ds-browse-by-metadata-page> to be visible // Wait for <ds-browse-by-metadata-page> to be visible
cy.get('ds-browse-by-metadata-page').should('be.visible'); cy.get('ds-browse-by-metadata').should('be.visible');
// Analyze <ds-browse-by-metadata-page> for accessibility // Analyze <ds-browse-by-metadata-page> for accessibility
testA11y('ds-browse-by-metadata-page'); testA11y('ds-browse-by-metadata');
}); });
}); });

View File

@@ -5,9 +5,9 @@ describe('Browse By Date Issued', () => {
cy.visit('/browse/dateissued'); cy.visit('/browse/dateissued');
// Wait for <ds-browse-by-date-page> to be visible // Wait for <ds-browse-by-date-page> to be visible
cy.get('ds-browse-by-date-page').should('be.visible'); cy.get('ds-browse-by-date').should('be.visible');
// Analyze <ds-browse-by-date-page> for accessibility // Analyze <ds-browse-by-date-page> for accessibility
testA11y('ds-browse-by-date-page'); testA11y('ds-browse-by-date');
}); });
}); });

View File

@@ -5,9 +5,9 @@ describe('Browse By Subject', () => {
cy.visit('/browse/subject'); cy.visit('/browse/subject');
// Wait for <ds-browse-by-metadata-page> to be visible // Wait for <ds-browse-by-metadata-page> to be visible
cy.get('ds-browse-by-metadata-page').should('be.visible'); cy.get('ds-browse-by-metadata').should('be.visible');
// Analyze <ds-browse-by-metadata-page> for accessibility // Analyze <ds-browse-by-metadata-page> for accessibility
testA11y('ds-browse-by-metadata-page'); testA11y('ds-browse-by-metadata');
}); });
}); });

View File

@@ -5,9 +5,9 @@ describe('Browse By Title', () => {
cy.visit('/browse/title'); cy.visit('/browse/title');
// Wait for <ds-browse-by-title-page> to be visible // Wait for <ds-browse-by-title-page> to be visible
cy.get('ds-browse-by-title-page').should('be.visible'); cy.get('ds-browse-by-title').should('be.visible');
// Analyze <ds-browse-by-title-page> for accessibility // Analyze <ds-browse-by-title-page> for accessibility
testA11y('ds-browse-by-title-page'); testA11y('ds-browse-by-title');
}); });
}); });

View File

@@ -0,0 +1,128 @@
import { testA11y } from 'cypress/support/utils';
const COLLECTION_EDIT_PAGE = '/collections/'.concat(Cypress.env('DSPACE_TEST_COLLECTION')).concat('/edit');
beforeEach(() => {
// All tests start with visiting the Edit Collection Page
cy.visit(COLLECTION_EDIT_PAGE);
// This page is restricted, so we will be shown the login form. Fill it out & submit.
cy.loginViaForm(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD'));
});
describe('Edit Collection > Edit Metadata tab', () => {
it('should pass accessibility tests', () => {
// <ds-edit-collection> tag must be loaded
cy.get('ds-edit-collection').should('be.visible');
// Analyze <ds-edit-collection> for accessibility issues
testA11y('ds-edit-collection');
});
});
describe('Edit Collection > Assign Roles tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="roles"]').click();
// <ds-collection-roles> tag must be loaded
cy.get('ds-collection-roles').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-collection-roles');
});
});
describe('Edit Collection > Content Source tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="source"]').click();
// <ds-collection-source> tag must be loaded
cy.get('ds-collection-source').should('be.visible');
// Check the external source checkbox (to display all fields on the page)
cy.get('#externalSourceCheck').check();
// Wait for the source controls to appear
// cy.get('ds-collection-source-controls').should('be.visible');
// Analyze entire page for accessibility issues
testA11y('ds-collection-source');
});
});
describe('Edit Collection > Curate tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="curate"]').click();
// <ds-collection-curate> tag must be loaded
cy.get('ds-collection-curate').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-collection-curate');
});
});
describe('Edit Collection > Access Control tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="access-control"]').click();
// <ds-collection-access-control> tag must be loaded
cy.get('ds-collection-access-control').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-collection-access-control');
});
});
describe('Edit Collection > Authorizations tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="authorizations"]').click();
// <ds-collection-authorizations> tag must be loaded
cy.get('ds-collection-authorizations').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-collection-authorizations');
});
});
describe('Edit Collection > Item Mapper tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="mapper"]').click();
// <ds-collection-item-mapper> tag must be loaded
cy.get('ds-collection-item-mapper').should('be.visible');
// Analyze entire page for accessibility issues
testA11y('ds-collection-item-mapper');
// Click on the "Map new Items" tab
cy.get('li[data-test="mapTab"] a').click();
// Make sure search form is now visible
cy.get('ds-search-form').should('be.visible');
// Analyze entire page (again) for accessibility issues
testA11y('ds-collection-item-mapper');
});
});
describe('Edit Collection > Delete page', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="delete-button"]').click();
// <ds-delete-collection> tag must be loaded
cy.get('ds-delete-collection').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-delete-collection');
});
});

View File

@@ -1,10 +1,9 @@
import { TEST_COLLECTION } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
describe('Collection Page', () => { describe('Collection Page', () => {
it('should pass accessibility tests', () => { it('should pass accessibility tests', () => {
cy.visit('/collections/'.concat(TEST_COLLECTION)); cy.visit('/collections/'.concat(Cypress.env('DSPACE_TEST_COLLECTION')));
// <ds-collection-page> tag must be loaded // <ds-collection-page> tag must be loaded
cy.get('ds-collection-page').should('be.visible'); cy.get('ds-collection-page').should('be.visible');

View File

@@ -1,12 +1,12 @@
import { REGEX_MATCH_NON_EMPTY_TEXT, TEST_COLLECTION } from 'cypress/support/e2e'; import { REGEX_MATCH_NON_EMPTY_TEXT } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
describe('Collection Statistics Page', () => { describe('Collection Statistics Page', () => {
const COLLECTIONSTATISTICSPAGE = '/statistics/collections/'.concat(TEST_COLLECTION); const COLLECTIONSTATISTICSPAGE = '/statistics/collections/'.concat(Cypress.env('DSPACE_TEST_COLLECTION'));
it('should load if you click on "Statistics" from a Collection page', () => { it('should load if you click on "Statistics" from a Collection page', () => {
cy.visit('/collections/'.concat(TEST_COLLECTION)); cy.visit('/collections/'.concat(Cypress.env('DSPACE_TEST_COLLECTION')));
cy.get('ds-navbar ds-link-menu-item a[title="Statistics"]').click(); cy.get('ds-navbar ds-link-menu-item a[data-test="link-menu-item.menu.section.statistics"]').click();
cy.location('pathname').should('eq', COLLECTIONSTATISTICSPAGE); cy.location('pathname').should('eq', COLLECTIONSTATISTICSPAGE);
}); });
@@ -18,7 +18,7 @@ describe('Collection Statistics Page', () => {
it('should contain a "Total visits per month" section', () => { it('should contain a "Total visits per month" section', () => {
cy.visit(COLLECTIONSTATISTICSPAGE); cy.visit(COLLECTIONSTATISTICSPAGE);
// Check just for existence because this table is empty in CI environment as it's historical data // Check just for existence because this table is empty in CI environment as it's historical data
cy.get('.'.concat(TEST_COLLECTION).concat('_TotalVisitsPerMonth')).should('exist'); cy.get('.'.concat(Cypress.env('DSPACE_TEST_COLLECTION')).concat('_TotalVisitsPerMonth')).should('exist');
}); });
it('should pass accessibility tests', () => { it('should pass accessibility tests', () => {

View File

@@ -0,0 +1,86 @@
import { testA11y } from 'cypress/support/utils';
const COMMUNITY_EDIT_PAGE = '/communities/'.concat(Cypress.env('DSPACE_TEST_COMMUNITY')).concat('/edit');
beforeEach(() => {
// All tests start with visiting the Edit Community Page
cy.visit(COMMUNITY_EDIT_PAGE);
// This page is restricted, so we will be shown the login form. Fill it out & submit.
cy.loginViaForm(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD'));
});
describe('Edit Community > Edit Metadata tab', () => {
it('should pass accessibility tests', () => {
// <ds-edit-community> tag must be loaded
cy.get('ds-edit-community').should('be.visible');
// Analyze <ds-edit-community> for accessibility issues
testA11y('ds-edit-community');
});
});
describe('Edit Community > Assign Roles tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="roles"]').click();
// <ds-community-roles> tag must be loaded
cy.get('ds-community-roles').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-community-roles');
});
});
describe('Edit Community > Curate tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="curate"]').click();
// <ds-community-curate> tag must be loaded
cy.get('ds-community-curate').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-community-curate');
});
});
describe('Edit Community > Access Control tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="access-control"]').click();
// <ds-community-access-control> tag must be loaded
cy.get('ds-community-access-control').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-community-access-control');
});
});
describe('Edit Community > Authorizations tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="authorizations"]').click();
// <ds-community-authorizations> tag must be loaded
cy.get('ds-community-authorizations').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-community-authorizations');
});
});
describe('Edit Community > Delete page', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="delete-button"]').click();
// <ds-delete-community> tag must be loaded
cy.get('ds-delete-community').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-delete-community');
});
});

View File

@@ -1,4 +1,3 @@
import { Options } from 'cypress-axe';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
describe('Community List Page', () => { describe('Community List Page', () => {
@@ -13,13 +12,6 @@ describe('Community List Page', () => {
cy.get('[data-test="expand-button"]').click({ multiple: true }); cy.get('[data-test="expand-button"]').click({ multiple: true });
// Analyze <ds-community-list-page> for accessibility issues // Analyze <ds-community-list-page> for accessibility issues
// Disable heading-order checks until it is fixed testA11y('ds-community-list-page');
testA11y('ds-community-list-page',
{
rules: {
'heading-order': { enabled: false }
}
} as Options
);
}); });
}); });

View File

@@ -1,15 +1,14 @@
import { TEST_COMMUNITY } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
describe('Community Page', () => { describe('Community Page', () => {
it('should pass accessibility tests', () => { it('should pass accessibility tests', () => {
cy.visit('/communities/'.concat(TEST_COMMUNITY)); cy.visit('/communities/'.concat(Cypress.env('DSPACE_TEST_COMMUNITY')));
// <ds-community-page> tag must be loaded // <ds-community-page> tag must be loaded
cy.get('ds-community-page').should('be.visible'); cy.get('ds-community-page').should('be.visible');
// Analyze <ds-community-page> for accessibility issues // Analyze <ds-community-page> for accessibility issues
testA11y('ds-community-page',); testA11y('ds-community-page');
}); });
}); });

View File

@@ -1,12 +1,12 @@
import { REGEX_MATCH_NON_EMPTY_TEXT, TEST_COMMUNITY } from 'cypress/support/e2e'; import { REGEX_MATCH_NON_EMPTY_TEXT } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
describe('Community Statistics Page', () => { describe('Community Statistics Page', () => {
const COMMUNITYSTATISTICSPAGE = '/statistics/communities/'.concat(TEST_COMMUNITY); const COMMUNITYSTATISTICSPAGE = '/statistics/communities/'.concat(Cypress.env('DSPACE_TEST_COMMUNITY'));
it('should load if you click on "Statistics" from a Community page', () => { it('should load if you click on "Statistics" from a Community page', () => {
cy.visit('/communities/'.concat(TEST_COMMUNITY)); cy.visit('/communities/'.concat(Cypress.env('DSPACE_TEST_COMMUNITY')));
cy.get('ds-navbar ds-link-menu-item a[title="Statistics"]').click(); cy.get('ds-navbar ds-link-menu-item a[data-test="link-menu-item.menu.section.statistics"]').click();
cy.location('pathname').should('eq', COMMUNITYSTATISTICSPAGE); cy.location('pathname').should('eq', COMMUNITYSTATISTICSPAGE);
}); });
@@ -18,7 +18,7 @@ describe('Community Statistics Page', () => {
it('should contain a "Total visits per month" section', () => { it('should contain a "Total visits per month" section', () => {
cy.visit(COMMUNITYSTATISTICSPAGE); cy.visit(COMMUNITYSTATISTICSPAGE);
// Check just for existence because this table is empty in CI environment as it's historical data // Check just for existence because this table is empty in CI environment as it's historical data
cy.get('.'.concat(TEST_COMMUNITY).concat('_TotalVisitsPerMonth')).should('exist'); cy.get('.'.concat(Cypress.env('DSPACE_TEST_COMMUNITY')).concat('_TotalVisitsPerMonth')).should('exist');
}); });
it('should pass accessibility tests', () => { it('should pass accessibility tests', () => {

View File

@@ -8,12 +8,6 @@ describe('Header', () => {
cy.get('ds-header').should('be.visible'); cy.get('ds-header').should('be.visible');
// Analyze <ds-header> for accessibility // Analyze <ds-header> for accessibility
testA11y({ testA11y('ds-header');
include: ['ds-header'],
exclude: [
['#search-navbar-container'], // search in navbar has duplicative ID. Will be fixed in #1174
['.dropdownLogin'] // "Log in" link has color contrast issues. Will be fixed in #1149
],
});
}); });
}); });

View File

@@ -1,18 +1,19 @@
import { REGEX_MATCH_NON_EMPTY_TEXT, TEST_ENTITY_PUBLICATION } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils';
import '../support/commands'; import '../support/commands';
import { REGEX_MATCH_NON_EMPTY_TEXT } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils';
describe('Site Statistics Page', () => { describe('Site Statistics Page', () => {
it('should load if you click on "Statistics" from homepage', () => { it('should load if you click on "Statistics" from homepage', () => {
cy.visit('/'); cy.visit('/');
cy.get('ds-navbar ds-link-menu-item a[title="Statistics"]').click(); cy.get('ds-navbar ds-link-menu-item a[data-test="link-menu-item.menu.section.statistics"]').click();
cy.location('pathname').should('eq', '/statistics'); cy.location('pathname').should('eq', '/statistics');
}); });
it('should pass accessibility tests', () => { it('should pass accessibility tests', () => {
// generate 2 view events on an Item's page // generate 2 view events on an Item's page
cy.generateViewEvent(TEST_ENTITY_PUBLICATION, 'item'); cy.generateViewEvent(Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION'), 'item');
cy.generateViewEvent(TEST_ENTITY_PUBLICATION, 'item'); cy.generateViewEvent(Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION'), 'item');
cy.visit('/statistics'); cy.visit('/statistics');

135
cypress/e2e/item-edit.cy.ts Normal file
View File

@@ -0,0 +1,135 @@
import { testA11y } from 'cypress/support/utils';
import { Options } from 'cypress-axe';
const ITEM_EDIT_PAGE = '/items/'.concat(Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION')).concat('/edit');
beforeEach(() => {
// All tests start with visiting the Edit Item Page
cy.visit(ITEM_EDIT_PAGE);
// This page is restricted, so we will be shown the login form. Fill it out & submit.
cy.loginViaForm(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD'));
});
describe('Edit Item > Edit Metadata tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="metadata"]').click();
// <ds-edit-item-page> tag must be loaded
cy.get('ds-edit-item-page').should('be.visible');
// Analyze <ds-edit-item-page> for accessibility issues
testA11y('ds-edit-item-page');
});
});
describe('Edit Item > Status tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="status"]').click();
// <ds-item-status> tag must be loaded
cy.get('ds-item-status').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-item-status');
});
});
describe('Edit Item > Bitstreams tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="bitstreams"]').click();
// <ds-item-bitstreams> tag must be loaded
cy.get('ds-item-bitstreams').should('be.visible');
// Table of item bitstreams must also be loaded
cy.get('div.item-bitstreams').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-item-bitstreams',
{
rules: {
// Currently Bitstreams page loads a pagination component per Bundle
// and they all use the same 'id="p-dad"'.
'duplicate-id': { enabled: false },
},
} as Options,
);
});
});
describe('Edit Item > Curate tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="curate"]').click();
// <ds-item-curate> tag must be loaded
cy.get('ds-item-curate').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-item-curate');
});
});
describe('Edit Item > Relationships tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="relationships"]').click();
// <ds-item-relationships> tag must be loaded
cy.get('ds-item-relationships').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-item-relationships');
});
});
describe('Edit Item > Version History tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="versionhistory"]').click();
// <ds-item-version-history> tag must be loaded
cy.get('ds-item-version-history').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-item-version-history');
});
});
describe('Edit Item > Access Control tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="access-control"]').click();
// <ds-item-access-control> tag must be loaded
cy.get('ds-item-access-control').should('be.visible');
// Analyze for accessibility issues
testA11y('ds-item-access-control');
});
});
describe('Edit Item > Collection Mapper tab', () => {
it('should pass accessibility tests', () => {
cy.get('a[data-test="mapper"]').click();
// <ds-item-collection-mapper> tag must be loaded
cy.get('ds-item-collection-mapper').should('be.visible');
// Analyze entire page for accessibility issues
testA11y('ds-item-collection-mapper');
// Click on the "Map new collections" tab
cy.get('li[data-test="mapTab"] a').click();
// Make sure search form is now visible
cy.get('ds-search-form').should('be.visible');
// Analyze entire page (again) for accessibility issues
testA11y('ds-item-collection-mapper');
});
});

View File

@@ -1,10 +1,8 @@
import { Options } from 'cypress-axe';
import { TEST_ENTITY_PUBLICATION } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
describe('Item Page', () => { describe('Item Page', () => {
const ITEMPAGE = '/items/'.concat(TEST_ENTITY_PUBLICATION); const ITEMPAGE = '/items/'.concat(Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION'));
const ENTITYPAGE = '/entities/publication/'.concat(TEST_ENTITY_PUBLICATION); const ENTITYPAGE = '/entities/publication/'.concat(Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION'));
// Test that entities will redirect to /entities/[type]/[uuid] when accessed via /items/[uuid] // Test that entities will redirect to /entities/[type]/[uuid] when accessed via /items/[uuid]
it('should redirect to the entity page when navigating to an item page', () => { it('should redirect to the entity page when navigating to an item page', () => {
@@ -19,13 +17,16 @@ describe('Item Page', () => {
cy.get('ds-item-page').should('be.visible'); cy.get('ds-item-page').should('be.visible');
// Analyze <ds-item-page> for accessibility issues // Analyze <ds-item-page> for accessibility issues
// Disable heading-order checks until it is fixed testA11y('ds-item-page');
testA11y('ds-item-page', });
{
rules: { it('should pass accessibility tests on full item page', () => {
'heading-order': { enabled: false } cy.visit(ENTITYPAGE + '/full');
}
} as Options // <ds-full-item-page> tag must be loaded
); cy.get('ds-full-item-page').should('be.visible');
// Analyze <ds-full-item-page> for accessibility issues
testA11y('ds-full-item-page');
}); });
}); });

View File

@@ -1,12 +1,12 @@
import { REGEX_MATCH_NON_EMPTY_TEXT, TEST_ENTITY_PUBLICATION } from 'cypress/support/e2e'; import { REGEX_MATCH_NON_EMPTY_TEXT } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
describe('Item Statistics Page', () => { describe('Item Statistics Page', () => {
const ITEMSTATISTICSPAGE = '/statistics/items/'.concat(TEST_ENTITY_PUBLICATION); const ITEMSTATISTICSPAGE = '/statistics/items/'.concat(Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION'));
it('should load if you click on "Statistics" from an Item/Entity page', () => { it('should load if you click on "Statistics" from an Item/Entity page', () => {
cy.visit('/entities/publication/'.concat(TEST_ENTITY_PUBLICATION)); cy.visit('/entities/publication/'.concat(Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION')));
cy.get('ds-navbar ds-link-menu-item a[title="Statistics"]').click(); cy.get('ds-navbar ds-link-menu-item a[data-test="link-menu-item.menu.section.statistics"]').click();
cy.location('pathname').should('eq', ITEMSTATISTICSPAGE); cy.location('pathname').should('eq', ITEMSTATISTICSPAGE);
}); });
@@ -24,7 +24,7 @@ describe('Item Statistics Page', () => {
it('should contain a "Total visits per month" section', () => { it('should contain a "Total visits per month" section', () => {
cy.visit(ITEMSTATISTICSPAGE); cy.visit(ITEMSTATISTICSPAGE);
// Check just for existence because this table is empty in CI environment as it's historical data // Check just for existence because this table is empty in CI environment as it's historical data
cy.get('.'.concat(TEST_ENTITY_PUBLICATION).concat('_TotalVisitsPerMonth')).should('exist'); cy.get('.'.concat(Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION')).concat('_TotalVisitsPerMonth')).should('exist');
}); });
it('should pass accessibility tests', () => { it('should pass accessibility tests', () => {

View File

@@ -1,42 +1,42 @@
import { TEST_ADMIN_PASSWORD, TEST_ADMIN_USER, TEST_ENTITY_PUBLICATION } from 'cypress/support/e2e'; import { testA11y } from 'cypress/support/utils';
const page = { const page = {
openLoginMenu() { openLoginMenu() {
// Click the "Log In" dropdown menu in header // Click the "Log In" dropdown menu in header
cy.get('ds-themed-navbar [data-test="login-menu"]').click(); cy.get('ds-themed-header [data-test="login-menu"]').click();
}, },
openUserMenu() { openUserMenu() {
// Once logged in, click the User menu in header // Once logged in, click the User menu in header
cy.get('ds-themed-navbar [data-test="user-menu"]').click(); cy.get('ds-themed-header [data-test="user-menu"]').click();
}, },
submitLoginAndPasswordByPressingButton(email, password) { submitLoginAndPasswordByPressingButton(email, password) {
// Enter email // Enter email
cy.get('ds-themed-navbar [data-test="email"]').type(email); cy.get('ds-themed-header [data-test="email"]').type(email);
// Enter password // Enter password
cy.get('ds-themed-navbar [data-test="password"]').type(password); cy.get('ds-themed-header [data-test="password"]').type(password);
// Click login button // Click login button
cy.get('ds-themed-navbar [data-test="login-button"]').click(); cy.get('ds-themed-header [data-test="login-button"]').click();
}, },
submitLoginAndPasswordByPressingEnter(email, password) { submitLoginAndPasswordByPressingEnter(email, password) {
// In opened Login modal, fill out email & password, then click Enter // In opened Login modal, fill out email & password, then click Enter
cy.get('ds-themed-navbar [data-test="email"]').type(email); cy.get('ds-themed-header [data-test="email"]').type(email);
cy.get('ds-themed-navbar [data-test="password"]').type(password); cy.get('ds-themed-header [data-test="password"]').type(password);
cy.get('ds-themed-navbar [data-test="password"]').type('{enter}'); cy.get('ds-themed-header [data-test="password"]').type('{enter}');
}, },
submitLogoutByPressingButton() { submitLogoutByPressingButton() {
// This is the POST command that will actually log us out // This is the POST command that will actually log us out
cy.intercept('POST', '/server/api/authn/logout').as('logout'); cy.intercept('POST', '/server/api/authn/logout').as('logout');
// Click logout button // Click logout button
cy.get('ds-themed-navbar [data-test="logout-button"]').click(); cy.get('ds-themed-header [data-test="logout-button"]').click();
// Wait until above POST command responds before continuing // Wait until above POST command responds before continuing
// (This ensures next action waits until logout completes) // (This ensures next action waits until logout completes)
cy.wait('@logout'); cy.wait('@logout');
} },
}; };
describe('Login Modal', () => { describe('Login Modal', () => {
it('should login when clicking button & stay on same page', () => { it('should login when clicking button & stay on same page', () => {
const ENTITYPAGE = '/entities/publication/'.concat(TEST_ENTITY_PUBLICATION); const ENTITYPAGE = '/entities/publication/'.concat(Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION'));
cy.visit(ENTITYPAGE); cy.visit(ENTITYPAGE);
// Login menu should exist // Login menu should exist
@@ -46,7 +46,7 @@ describe('Login Modal', () => {
page.openLoginMenu(); page.openLoginMenu();
cy.get('.form-login').should('be.visible'); cy.get('.form-login').should('be.visible');
page.submitLoginAndPasswordByPressingButton(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); page.submitLoginAndPasswordByPressingButton(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD'));
cy.get('ds-log-in').should('not.exist'); cy.get('ds-log-in').should('not.exist');
// Verify we are still on the same page // Verify we are still on the same page
@@ -66,7 +66,7 @@ describe('Login Modal', () => {
cy.get('.form-login').should('be.visible'); cy.get('.form-login').should('be.visible');
// Login, and the <ds-log-in> tag should no longer exist // Login, and the <ds-log-in> tag should no longer exist
page.submitLoginAndPasswordByPressingEnter(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); page.submitLoginAndPasswordByPressingEnter(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD'));
cy.get('.form-login').should('not.exist'); cy.get('.form-login').should('not.exist');
// Verify we are still on homepage // Verify we are still on homepage
@@ -80,7 +80,7 @@ describe('Login Modal', () => {
it('should support logout', () => { it('should support logout', () => {
// First authenticate & access homepage // First authenticate & access homepage
cy.login(TEST_ADMIN_USER, TEST_ADMIN_PASSWORD); cy.login(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD'));
cy.visit('/'); cy.visit('/');
// Verify ds-log-in tag doesn't exist, but ds-log-out tag does exist // Verify ds-log-in tag doesn't exist, but ds-log-out tag does exist
@@ -102,12 +102,15 @@ describe('Login Modal', () => {
page.openLoginMenu(); page.openLoginMenu();
// Registration link should be visible // Registration link should be visible
cy.get('ds-themed-navbar [data-test="register"]').should('be.visible'); cy.get('ds-themed-header [data-test="register"]').should('be.visible');
// Click registration link & you should go to registration page // Click registration link & you should go to registration page
cy.get('ds-themed-navbar [data-test="register"]').click(); cy.get('ds-themed-header [data-test="register"]').click();
cy.location('pathname').should('eq', '/register'); cy.location('pathname').should('eq', '/register');
cy.get('ds-register-email').should('exist'); cy.get('ds-register-email').should('exist');
// Test accessibility of this page
testA11y('ds-register-email');
}); });
it('should allow forgot password', () => { it('should allow forgot password', () => {
@@ -116,11 +119,32 @@ describe('Login Modal', () => {
page.openLoginMenu(); page.openLoginMenu();
// Forgot password link should be visible // Forgot password link should be visible
cy.get('ds-themed-navbar [data-test="forgot"]').should('be.visible'); cy.get('ds-themed-header [data-test="forgot"]').should('be.visible');
// Click link & you should go to Forgot Password page // Click link & you should go to Forgot Password page
cy.get('ds-themed-navbar [data-test="forgot"]').click(); cy.get('ds-themed-header [data-test="forgot"]').click();
cy.location('pathname').should('eq', '/forgot'); cy.location('pathname').should('eq', '/forgot');
cy.get('ds-forgot-email').should('exist'); cy.get('ds-forgot-email').should('exist');
// Test accessibility of this page
testA11y('ds-forgot-email');
});
it('should pass accessibility tests in menus', () => {
cy.visit('/');
// Open login menu & verify accessibility
page.openLoginMenu();
cy.get('ds-log-in').should('exist');
testA11y('ds-log-in');
// Now login
page.submitLoginAndPasswordByPressingButton(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD'));
cy.get('ds-log-in').should('not.exist');
// Open user menu, verify user menu accesibility
page.openUserMenu();
cy.get('ds-user-menu').should('be.visible');
testA11y('ds-user-menu');
}); });
}); });

View File

@@ -1,5 +1,3 @@
import { Options } from 'cypress-axe';
import { TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD, TEST_SUBMIT_COLLECTION_NAME } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
describe('My DSpace page', () => { describe('My DSpace page', () => {
@@ -7,7 +5,7 @@ describe('My DSpace page', () => {
cy.visit('/mydspace'); cy.visit('/mydspace');
// This page is restricted, so we will be shown the login form. Fill it out & submit. // This page is restricted, so we will be shown the login form. Fill it out & submit.
cy.loginViaForm(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.loginViaForm(Cypress.env('DSPACE_TEST_SUBMIT_USER'), Cypress.env('DSPACE_TEST_SUBMIT_USER_PASSWORD'));
cy.get('ds-my-dspace-page').should('be.visible'); cy.get('ds-my-dspace-page').should('be.visible');
@@ -19,28 +17,14 @@ describe('My DSpace page', () => {
cy.get('.filter-toggle').click({ multiple: true }); cy.get('.filter-toggle').click({ multiple: true });
// Analyze <ds-my-dspace-page> for accessibility issues // Analyze <ds-my-dspace-page> for accessibility issues
testA11y( testA11y('ds-my-dspace-page');
{
include: ['ds-my-dspace-page'],
exclude: [
['nouislider'] // Date filter slider is missing ARIA labels. Will be fixed by #1175
],
},
{
rules: {
// Search filters fail these two "moderate" impact rules
'heading-order': { enabled: false },
'landmark-unique': { enabled: false }
}
} as Options
);
}); });
it('should have a working detailed view that passes accessibility tests', () => { it('should have a working detailed view that passes accessibility tests', () => {
cy.visit('/mydspace'); cy.visit('/mydspace');
// This page is restricted, so we will be shown the login form. Fill it out & submit. // This page is restricted, so we will be shown the login form. Fill it out & submit.
cy.loginViaForm(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.loginViaForm(Cypress.env('DSPACE_TEST_SUBMIT_USER'), Cypress.env('DSPACE_TEST_SUBMIT_USER_PASSWORD'));
cy.get('ds-my-dspace-page').should('be.visible'); cy.get('ds-my-dspace-page').should('be.visible');
@@ -49,16 +33,8 @@ describe('My DSpace page', () => {
cy.get('ds-object-detail').should('be.visible'); cy.get('ds-object-detail').should('be.visible');
// Analyze <ds-search-page> for accessibility issues // Analyze <ds-my-dspace-page> for accessibility issues
testA11y('ds-my-dspace-page', testA11y('ds-my-dspace-page');
{
rules: {
// Search filters fail these two "moderate" impact rules
'heading-order': { enabled: false },
'landmark-unique': { enabled: false }
}
} as Options
);
}); });
// NOTE: Deleting existing submissions is exercised by submission.spec.ts // NOTE: Deleting existing submissions is exercised by submission.spec.ts
@@ -66,7 +42,7 @@ describe('My DSpace page', () => {
cy.visit('/mydspace'); cy.visit('/mydspace');
// This page is restricted, so we will be shown the login form. Fill it out & submit. // This page is restricted, so we will be shown the login form. Fill it out & submit.
cy.loginViaForm(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.loginViaForm(Cypress.env('DSPACE_TEST_SUBMIT_USER'), Cypress.env('DSPACE_TEST_SUBMIT_USER_PASSWORD'));
// Open the New Submission dropdown // Open the New Submission dropdown
cy.get('button[data-test="submission-dropdown"]').click(); cy.get('button[data-test="submission-dropdown"]').click();
@@ -77,10 +53,10 @@ describe('My DSpace page', () => {
cy.get('ds-create-item-parent-selector').should('be.visible'); cy.get('ds-create-item-parent-selector').should('be.visible');
// Type in a known Collection name in the search box // Type in a known Collection name in the search box
cy.get('ds-authorized-collection-selector input[type="search"]').type(TEST_SUBMIT_COLLECTION_NAME); cy.get('ds-authorized-collection-selector input[type="search"]').type(Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_NAME'));
// Click on the button matching that known Collection name // Click on the button matching that known Collection name
cy.get('ds-authorized-collection-selector button[title="'.concat(TEST_SUBMIT_COLLECTION_NAME).concat('"]')).click(); cy.get('ds-authorized-collection-selector button[title="'.concat(Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_NAME')).concat('"]')).click();
// New URL should include /workspaceitems, as we've started a new submission // New URL should include /workspaceitems, as we've started a new submission
cy.url().should('include', '/workspaceitems'); cy.url().should('include', '/workspaceitems');
@@ -89,7 +65,7 @@ describe('My DSpace page', () => {
cy.get('ds-submission-edit').should('be.visible'); cy.get('ds-submission-edit').should('be.visible');
// A Collection menu button should exist & its value should be the selected collection // A Collection menu button should exist & its value should be the selected collection
cy.get('#collectionControlsMenuButton span').should('have.text', TEST_SUBMIT_COLLECTION_NAME); cy.get('#collectionControlsMenuButton span').should('have.text', Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_NAME'));
// Now that we've created a submission, we'll test that we can go back and Edit it. // Now that we've created a submission, we'll test that we can go back and Edit it.
// Get our Submission URL, to parse out the ID of this new submission // Get our Submission URL, to parse out the ID of this new submission
@@ -138,7 +114,7 @@ describe('My DSpace page', () => {
cy.visit('/mydspace'); cy.visit('/mydspace');
// This page is restricted, so we will be shown the login form. Fill it out & submit. // This page is restricted, so we will be shown the login form. Fill it out & submit.
cy.loginViaForm(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.loginViaForm(Cypress.env('DSPACE_TEST_SUBMIT_USER'), Cypress.env('DSPACE_TEST_SUBMIT_USER_PASSWORD'));
// Open the New Import dropdown // Open the New Import dropdown
cy.get('button[data-test="import-dropdown"]').click(); cy.get('button[data-test="import-dropdown"]').click();
@@ -150,6 +126,9 @@ describe('My DSpace page', () => {
// The external import searchbox should be visible // The external import searchbox should be visible
cy.get('ds-submission-import-external-searchbar').should('be.visible'); cy.get('ds-submission-import-external-searchbar').should('be.visible');
// Test for accessibility issues
testA11y('ds-submission-import-external');
}); });
}); });

View File

@@ -1,8 +1,13 @@
import { testA11y } from 'cypress/support/utils';
describe('PageNotFound', () => { describe('PageNotFound', () => {
it('should contain element ds-pagenotfound when navigating to page that doesnt exist', () => { it('should contain element ds-pagenotfound when navigating to page that doesnt exist', () => {
// request an invalid page (UUIDs at root path aren't valid) // request an invalid page (UUIDs at root path aren't valid)
cy.visit('/e9019a69-d4f1-4773-b6a3-bd362caa46f2', { failOnStatusCode: false }); cy.visit('/e9019a69-d4f1-4773-b6a3-bd362caa46f2', { failOnStatusCode: false });
cy.get('ds-pagenotfound').should('be.visible'); cy.get('ds-pagenotfound').should('be.visible');
// Analyze <ds-pagenotfound> for accessibility issues
testA11y('ds-pagenotfound');
}); });
it('should not contain element ds-pagenotfound when navigating to existing page', () => { it('should not contain element ds-pagenotfound when navigating to existing page', () => {

View File

@@ -1,23 +1,21 @@
import { TEST_SEARCH_TERM } from 'cypress/support/e2e';
const page = { const page = {
fillOutQueryInNavBar(query) { fillOutQueryInNavBar(query) {
// Click the magnifying glass // Click the magnifying glass
cy.get('ds-themed-navbar [data-test="header-search-icon"]').click(); cy.get('ds-themed-header [data-test="header-search-icon"]').click();
// Fill out a query in input that appears // Fill out a query in input that appears
cy.get('ds-themed-navbar [data-test="header-search-box"]').type(query); cy.get('ds-themed-header [data-test="header-search-box"]').type(query);
}, },
submitQueryByPressingEnter() { submitQueryByPressingEnter() {
cy.get('ds-themed-navbar [data-test="header-search-box"]').type('{enter}'); cy.get('ds-themed-header [data-test="header-search-box"]').type('{enter}');
}, },
submitQueryByPressingIcon() { submitQueryByPressingIcon() {
cy.get('ds-themed-navbar [data-test="header-search-icon"]').click(); cy.get('ds-themed-header [data-test="header-search-icon"]').click();
} },
}; };
describe('Search from Navigation Bar', () => { describe('Search from Navigation Bar', () => {
// NOTE: these tests currently assume this query will return results! // NOTE: these tests currently assume this query will return results!
const query = TEST_SEARCH_TERM; const query = Cypress.env('DSPACE_TEST_SEARCH_TERM');
it('should go to search page with correct query if submitted (from home)', () => { it('should go to search page with correct query if submitted (from home)', () => {
cy.visit('/'); cy.visit('/');

View File

@@ -1,8 +1,10 @@
import { Options } from 'cypress-axe';
import { TEST_SEARCH_TERM } from 'cypress/support/e2e';
import { testA11y } from 'cypress/support/utils'; import { testA11y } from 'cypress/support/utils';
import { Options } from 'cypress-axe';
describe('Search Page', () => { describe('Search Page', () => {
// NOTE: these tests currently assume this query will return results!
const query = Cypress.env('DSPACE_TEST_SEARCH_TERM');
it('should redirect to the correct url when query was set and submit button was triggered', () => { it('should redirect to the correct url when query was set and submit button was triggered', () => {
const queryString = 'Another interesting query string'; const queryString = 'Another interesting query string';
cy.visit('/search'); cy.visit('/search');
@@ -13,8 +15,8 @@ describe('Search Page', () => {
}); });
it('should load results and pass accessibility tests', () => { it('should load results and pass accessibility tests', () => {
cy.visit('/search?query='.concat(TEST_SEARCH_TERM)); cy.visit('/search?query='.concat(query));
cy.get('[data-test="search-box"]').should('have.value', TEST_SEARCH_TERM); cy.get('[data-test="search-box"]').should('have.value', query);
// <ds-search-page> tag must be loaded // <ds-search-page> tag must be loaded
cy.get('ds-search-page').should('be.visible'); cy.get('ds-search-page').should('be.visible');
@@ -27,25 +29,11 @@ describe('Search Page', () => {
cy.get('[data-test="filter-toggle"]').click({ multiple: true }); cy.get('[data-test="filter-toggle"]').click({ multiple: true });
// Analyze <ds-search-page> for accessibility issues // Analyze <ds-search-page> for accessibility issues
testA11y( testA11y('ds-search-page');
{
include: ['ds-search-page'],
exclude: [
['nouislider'] // Date filter slider is missing ARIA labels. Will be fixed by #1175
],
},
{
rules: {
// Search filters fail these two "moderate" impact rules
'heading-order': { enabled: false },
'landmark-unique': { enabled: false }
}
} as Options
);
}); });
it('should have a working grid view that passes accessibility tests', () => { it('should have a working grid view that passes accessibility tests', () => {
cy.visit('/search?query='.concat(TEST_SEARCH_TERM)); cy.visit('/search?query='.concat(query));
// Click button in sidebar to display grid view // Click button in sidebar to display grid view
cy.get('ds-search-sidebar [data-test="grid-view"]').click(); cy.get('ds-search-sidebar [data-test="grid-view"]').click();
@@ -60,11 +48,10 @@ describe('Search Page', () => {
testA11y('ds-search-page', testA11y('ds-search-page',
{ {
rules: { rules: {
// Search filters fail these two "moderate" impact rules // Card titles fail this test currently
'heading-order': { enabled: false }, 'heading-order': { enabled: false },
'landmark-unique': { enabled: false } },
} } as Options,
} as Options
); );
}); });
}); });

View File

@@ -1,14 +1,16 @@
import { TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD, TEST_SUBMIT_COLLECTION_NAME, TEST_SUBMIT_COLLECTION_UUID } from 'cypress/support/e2e'; import { testA11y } from 'cypress/support/utils';
//import { TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD, TEST_SUBMIT_COLLECTION_NAME, TEST_SUBMIT_COLLECTION_UUID, TEST_ADMIN_USER, TEST_ADMIN_PASSWORD } from 'cypress/support/e2e';
import { Options } from 'cypress-axe';
describe('New Submission page', () => { describe('New Submission page', () => {
// NOTE: We already test that new submissions can be started from MyDSpace in my-dspace.spec.ts
// NOTE: We already test that new Item submissions can be started from MyDSpace in my-dspace.spec.ts
it('should create a new submission when using /submit path & pass accessibility', () => { it('should create a new submission when using /submit path & pass accessibility', () => {
// Test that calling /submit with collection & entityType will create a new submission // Test that calling /submit with collection & entityType will create a new submission
cy.visit('/submit?collection='.concat(TEST_SUBMIT_COLLECTION_UUID).concat('&entityType=none')); cy.visit('/submit?collection='.concat(Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_UUID')).concat('&entityType=none'));
// This page is restricted, so we will be shown the login form. Fill it out & submit. // This page is restricted, so we will be shown the login form. Fill it out & submit.
cy.loginViaForm(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.loginViaForm(Cypress.env('DSPACE_TEST_SUBMIT_USER'), Cypress.env('DSPACE_TEST_SUBMIT_USER_PASSWORD'));
// Should redirect to /workspaceitems, as we've started a new submission // Should redirect to /workspaceitems, as we've started a new submission
cy.url().should('include', '/workspaceitems'); cy.url().should('include', '/workspaceitems');
@@ -17,7 +19,7 @@ describe('New Submission page', () => {
cy.get('ds-submission-edit').should('be.visible'); cy.get('ds-submission-edit').should('be.visible');
// A Collection menu button should exist & it's value should be the selected collection // A Collection menu button should exist & it's value should be the selected collection
cy.get('#collectionControlsMenuButton span').should('have.text', TEST_SUBMIT_COLLECTION_NAME); cy.get('#collectionControlsMenuButton span').should('have.text', Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_NAME'));
// 4 sections should be visible by default // 4 sections should be visible by default
cy.get('div#section_traditionalpageone').should('be.visible'); cy.get('div#section_traditionalpageone').should('be.visible');
@@ -25,6 +27,25 @@ describe('New Submission page', () => {
cy.get('div#section_upload').should('be.visible'); cy.get('div#section_upload').should('be.visible');
cy.get('div#section_license').should('be.visible'); cy.get('div#section_license').should('be.visible');
// Test entire page for accessibility
testA11y('ds-submission-edit',
{
rules: {
// Author & Subject fields have invalid "aria-multiline" attrs.
// See https://github.com/DSpace/dspace-angular/issues/1272
'aria-allowed-attr': { enabled: false },
// All panels are accordians & fail "aria-required-children" and "nested-interactive".
// Seem to require updating ng-bootstrap and https://github.com/DSpace/dspace-angular/issues/2216
'aria-required-children': { enabled: false },
'nested-interactive': { enabled: false },
// All select boxes fail to have a name / aria-label.
// This is a bug in ng-dynamic-forms and may require https://github.com/DSpace/dspace-angular/issues/2216
'select-name': { enabled: false },
},
} as Options,
);
// Discard button should work // Discard button should work
// Clicking it will display a confirmation, which we will confirm with another click // Clicking it will display a confirmation, which we will confirm with another click
cy.get('button#discard').click(); cy.get('button#discard').click();
@@ -33,10 +54,10 @@ describe('New Submission page', () => {
it('should block submission & show errors if required fields are missing', () => { it('should block submission & show errors if required fields are missing', () => {
// Create a new submission // Create a new submission
cy.visit('/submit?collection='.concat(TEST_SUBMIT_COLLECTION_UUID).concat('&entityType=none')); cy.visit('/submit?collection='.concat(Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_UUID')).concat('&entityType=none'));
// This page is restricted, so we will be shown the login form. Fill it out & submit. // This page is restricted, so we will be shown the login form. Fill it out & submit.
cy.loginViaForm(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.loginViaForm(Cypress.env('DSPACE_TEST_SUBMIT_USER'), Cypress.env('DSPACE_TEST_SUBMIT_USER_PASSWORD'));
// Attempt an immediate deposit without filling out any fields // Attempt an immediate deposit without filling out any fields
cy.get('button#deposit').click(); cy.get('button#deposit').click();
@@ -93,10 +114,10 @@ describe('New Submission page', () => {
it('should allow for deposit if all required fields completed & file uploaded', () => { it('should allow for deposit if all required fields completed & file uploaded', () => {
// Create a new submission // Create a new submission
cy.visit('/submit?collection='.concat(TEST_SUBMIT_COLLECTION_UUID).concat('&entityType=none')); cy.visit('/submit?collection='.concat(Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_UUID')).concat('&entityType=none'));
// This page is restricted, so we will be shown the login form. Fill it out & submit. // This page is restricted, so we will be shown the login form. Fill it out & submit.
cy.loginViaForm(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.loginViaForm(Cypress.env('DSPACE_TEST_SUBMIT_USER'), Cypress.env('DSPACE_TEST_SUBMIT_USER_PASSWORD'));
// Fill out all required fields (Title, Date) // Fill out all required fields (Title, Date)
cy.get('input#dc_title').type('DSpace logo uploaded via e2e tests'); cy.get('input#dc_title').type('DSpace logo uploaded via e2e tests');
@@ -117,7 +138,7 @@ describe('New Submission page', () => {
// Upload our DSpace logo via drag & drop onto submission form // Upload our DSpace logo via drag & drop onto submission form
// cy.get('div#section_upload') // cy.get('div#section_upload')
cy.get('div.ds-document-drop-zone').selectFile('src/assets/images/dspace-logo.png', { cy.get('div.ds-document-drop-zone').selectFile('src/assets/images/dspace-logo.png', {
action: 'drag-drop' action: 'drag-drop',
}); });
// Wait for upload to complete before proceeding // Wait for upload to complete before proceeding
@@ -131,4 +152,76 @@ describe('New Submission page', () => {
cy.get('ds-notification div.alert-success').should('be.visible'); cy.get('ds-notification div.alert-success').should('be.visible');
}); });
it('is possible to submit a new "Person" and that form passes accessibility', () => {
// To submit a different entity type, we'll start from MyDSpace
cy.visit('/mydspace');
// This page is restricted, so we will be shown the login form. Fill it out & submit.
// NOTE: At this time, we MUST login as admin to submit Person objects
cy.loginViaForm(Cypress.env('DSPACE_TEST_ADMIN_USER'), Cypress.env('DSPACE_TEST_ADMIN_PASSWORD'));
// Open the New Submission dropdown
cy.get('button[data-test="submission-dropdown"]').click();
// Click on the "Person" type in that dropdown
cy.get('#entityControlsDropdownMenu button[title="Person"]').click();
// This should display the <ds-create-item-parent-selector> (popup window)
cy.get('ds-create-item-parent-selector').should('be.visible');
// Type in a known Collection name in the search box
cy.get('ds-authorized-collection-selector input[type="search"]').type(Cypress.env('DSPACE_TEST_SUBMIT_PERSON_COLLECTION_NAME'));
// Click on the button matching that known Collection name
cy.get('ds-authorized-collection-selector button[title="'.concat(Cypress.env('DSPACE_TEST_SUBMIT_PERSON_COLLECTION_NAME')).concat('"]')).click();
// New URL should include /workspaceitems, as we've started a new submission
cy.url().should('include', '/workspaceitems');
// The Submission edit form tag should be visible
cy.get('ds-submission-edit').should('be.visible');
// A Collection menu button should exist & its value should be the selected collection
cy.get('#collectionControlsMenuButton span').should('have.text', Cypress.env('DSPACE_TEST_SUBMIT_PERSON_COLLECTION_NAME'));
// 3 sections should be visible by default
cy.get('div#section_personStep').should('be.visible');
cy.get('div#section_upload').should('be.visible');
cy.get('div#section_license').should('be.visible');
// Test entire page for accessibility
testA11y('ds-submission-edit',
{
rules: {
// All panels are accordians & fail "aria-required-children" and "nested-interactive".
// Seem to require updating ng-bootstrap and https://github.com/DSpace/dspace-angular/issues/2216
'aria-required-children': { enabled: false },
'nested-interactive': { enabled: false },
},
} as Options,
);
// Click the lookup button next to "Publication" field
cy.get('button[data-test="lookup-button"]').click();
// A popup modal window should be visible
cy.get('ds-dynamic-lookup-relation-modal').should('be.visible');
// Popup modal should also pass accessibility tests
//testA11y('ds-dynamic-lookup-relation-modal');
testA11y({
include: ['ds-dynamic-lookup-relation-modal'],
exclude: [
['ul.nav-tabs'], // Tabs at top of model have several issues which seem to be caused by ng-bootstrap
],
});
// Close popup window
cy.get('ds-dynamic-lookup-relation-modal button.close').click();
// Back on the form, click the discard button to remove new submission
// Clicking it will display a confirmation, which we will confirm with another click
cy.get('button#discard').click();
cy.get('button#discard_submit').click();
});
}); });

View File

@@ -1,5 +1,11 @@
const fs = require('fs'); const fs = require('fs');
// These two global variables are used to store information about the REST API used
// by these e2e tests. They are filled out prior to running any tests in the before()
// method of e2e.ts. They can then be accessed by any tests via the getters below.
let REST_BASE_URL: string;
let REST_DOMAIN: string;
// Plugins enable you to tap into, modify, or extend the internal behavior of Cypress // Plugins enable you to tap into, modify, or extend the internal behavior of Cypress
// For more info, visit https://on.cypress.io/plugins-api // For more info, visit https://on.cypress.io/plugins-api
module.exports = (on, config) => { module.exports = (on, config) => {
@@ -30,6 +36,24 @@ module.exports = (on, config) => {
} }
return null; return null;
} },
// Save value of REST Base URL, looked up before all tests.
// This allows other tests to use it easily via getRestBaseURL() below.
saveRestBaseURL(url: string) {
return (REST_BASE_URL = url);
},
// Retrieve currently saved value of REST Base URL
getRestBaseURL() {
return REST_BASE_URL ;
},
// Save value of REST Domain, looked up before all tests.
// This allows other tests to use it easily via getRestBaseDomain() below.
saveRestBaseDomain(domain: string) {
return (REST_DOMAIN = domain);
},
// Retrieve currently saved value of REST Domain
getRestBaseDomain() {
return REST_DOMAIN ;
},
}); });
}; };

View File

@@ -3,13 +3,15 @@
// See docs at https://docs.cypress.io/api/cypress-api/custom-commands // See docs at https://docs.cypress.io/api/cypress-api/custom-commands
// *********************************************** // ***********************************************
import { AuthTokenInfo, TOKENITEM } from 'src/app/core/auth/models/auth-token-info.model'; import {
import { DSPACE_XSRF_COOKIE, XSRF_REQUEST_HEADER } from 'src/app/core/xsrf/xsrf.constants'; AuthTokenInfo,
TOKENITEM,
// NOTE: FALLBACK_TEST_REST_BASE_URL is only used if Cypress cannot read the REST API BaseURL } from 'src/app/core/auth/models/auth-token-info.model';
// from the Angular UI's config.json. See 'login()'. import {
export const FALLBACK_TEST_REST_BASE_URL = 'http://localhost:8080/server'; DSPACE_XSRF_COOKIE,
export const FALLBACK_TEST_REST_DOMAIN = 'localhost'; XSRF_REQUEST_HEADER,
} from 'src/app/core/xsrf/xsrf.constants';
import { v4 as uuidv4 } from 'uuid';
// Declare Cypress namespace to help with Intellisense & code completion in IDEs // Declare Cypress namespace to help with Intellisense & code completion in IDEs
// ALL custom commands MUST be listed here for code completion to work // ALL custom commands MUST be listed here for code completion to work
@@ -41,6 +43,13 @@ declare global {
* @param dsoType type of DSpace Object (e.g. "item", "collection", "community") * @param dsoType type of DSpace Object (e.g. "item", "collection", "community")
*/ */
generateViewEvent(uuid: string, dsoType: string): typeof generateViewEvent; generateViewEvent(uuid: string, dsoType: string): typeof generateViewEvent;
/**
* Create a new CSRF token and add to required Cookie. CSRF Token is returned
* in chainable in order to allow it to be sent also in required CSRF header.
* @returns Chainable reference to allow CSRF token to also be sent in header.
*/
createCSRFCookie(): Chainable<any>;
} }
} }
} }
@@ -54,42 +63,17 @@ declare global {
* @param password password to login as * @param password password to login as
*/ */
function login(email: string, password: string): void { function login(email: string, password: string): void {
// Cypress doesn't have access to the running application in Node.js. // Create a fake CSRF cookie/token to use in POST
// So, it's not possible to inject or load the AppConfig or environment of the Angular UI. cy.createCSRFCookie().then((csrfToken: string) => {
// Instead, we'll read our running application's config.json, which contains the configs & // get our REST API's base URL, also needed for POST
// is regenerated at runtime each time the Angular UI application starts up. cy.task('getRestBaseURL').then((baseRestUrl: string) => {
cy.task('readUIConfig').then((str: string) => {
// Parse config into a JSON object
const config = JSON.parse(str);
// Find the URL of our REST API. Have a fallback ready, just in case 'rest.baseUrl' cannot be found.
let baseRestUrl = FALLBACK_TEST_REST_BASE_URL;
if (!config.rest.baseUrl) {
console.warn("Could not load 'rest.baseUrl' from config.json. Falling back to " + FALLBACK_TEST_REST_BASE_URL);
} else {
//console.log("Found 'rest.baseUrl' in config.json. Using this REST API for login: ".concat(config.rest.baseUrl));
baseRestUrl = config.rest.baseUrl;
}
// Now find domain of our REST API, again with a fallback.
let baseDomain = FALLBACK_TEST_REST_DOMAIN;
if (!config.rest.host) {
console.warn("Could not load 'rest.host' from config.json. Falling back to " + FALLBACK_TEST_REST_DOMAIN);
} else {
baseDomain = config.rest.host;
}
// Create a fake CSRF Token. Set it in the required server-side cookie
const csrfToken = 'fakeLoginCSRFToken';
cy.setCookie(DSPACE_XSRF_COOKIE, csrfToken, { 'domain': baseDomain });
// Now, send login POST request including that CSRF token // Now, send login POST request including that CSRF token
cy.request({ cy.request({
method: 'POST', method: 'POST',
url: baseRestUrl + '/api/authn/login', url: baseRestUrl + '/api/authn/login',
headers: { [XSRF_REQUEST_HEADER]: csrfToken }, headers: { [XSRF_REQUEST_HEADER]: csrfToken },
form: true, // indicates the body should be form urlencoded form: true, // indicates the body should be form urlencoded
body: { user: email, password: password } body: { user: email, password: password },
}).then((resp) => { }).then((resp) => {
// We expect a successful login // We expect a successful login
expect(resp.status).to.eq(200); expect(resp.status).to.eq(200);
@@ -104,9 +88,7 @@ function login(email: string, password: string): void {
// This ensures the UI will recognize we are logged in on next "visit()" // This ensures the UI will recognize we are logged in on next "visit()"
cy.setCookie(TOKENITEM, JSON.stringify(authinfo)); cy.setCookie(TOKENITEM, JSON.stringify(authinfo));
}); });
});
// Remove cookie with fake CSRF token, as it's no longer needed
cy.clearCookie(DSPACE_XSRF_COOKIE);
}); });
} }
// Add as a Cypress command (i.e. assign to 'cy.login') // Add as a Cypress command (i.e. assign to 'cy.login')
@@ -141,34 +123,10 @@ Cypress.Commands.add('loginViaForm', loginViaForm);
* @param dsoType type of DSpace Object (e.g. "item", "collection", "community") * @param dsoType type of DSpace Object (e.g. "item", "collection", "community")
*/ */
function generateViewEvent(uuid: string, dsoType: string): void { function generateViewEvent(uuid: string, dsoType: string): void {
// Cypress doesn't have access to the running application in Node.js. // Create a fake CSRF cookie/token to use in POST
// So, it's not possible to inject or load the AppConfig or environment of the Angular UI. cy.createCSRFCookie().then((csrfToken: string) => {
// Instead, we'll read our running application's config.json, which contains the configs & // get our REST API's base URL, also needed for POST
// is regenerated at runtime each time the Angular UI application starts up. cy.task('getRestBaseURL').then((baseRestUrl: string) => {
cy.task('readUIConfig').then((str: string) => {
// Parse config into a JSON object
const config = JSON.parse(str);
// Find the URL of our REST API. Have a fallback ready, just in case 'rest.baseUrl' cannot be found.
let baseRestUrl = FALLBACK_TEST_REST_BASE_URL;
if (!config.rest.baseUrl) {
console.warn("Could not load 'rest.baseUrl' from config.json. Falling back to " + FALLBACK_TEST_REST_BASE_URL);
} else {
baseRestUrl = config.rest.baseUrl;
}
// Now find domain of our REST API, again with a fallback.
let baseDomain = FALLBACK_TEST_REST_DOMAIN;
if (!config.rest.host) {
console.warn("Could not load 'rest.host' from config.json. Falling back to " + FALLBACK_TEST_REST_DOMAIN);
} else {
baseDomain = config.rest.host;
}
// Create a fake CSRF Token. Set it in the required server-side cookie
const csrfToken = 'fakeGenerateViewEventCSRFToken';
cy.setCookie(DSPACE_XSRF_COOKIE, csrfToken, { 'domain': baseDomain });
// Now, send 'statistics/viewevents' POST request including that fake CSRF token in required header // Now, send 'statistics/viewevents' POST request including that fake CSRF token in required header
cy.request({ cy.request({
method: 'POST', method: 'POST',
@@ -177,6 +135,8 @@ function generateViewEvent(uuid: string, dsoType: string): void {
[XSRF_REQUEST_HEADER] : csrfToken, [XSRF_REQUEST_HEADER] : csrfToken,
// use a known public IP address to avoid being seen as a "bot" // use a known public IP address to avoid being seen as a "bot"
'X-Forwarded-For': '1.1.1.1', 'X-Forwarded-For': '1.1.1.1',
// Use a user-agent of a Firefox browser on Windows. This again avoids being seen as a "bot"
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0',
}, },
//form: true, // indicates the body should be form urlencoded //form: true, // indicates the body should be form urlencoded
body: { targetId: uuid, targetType: dsoType }, body: { targetId: uuid, targetType: dsoType },
@@ -184,11 +144,32 @@ function generateViewEvent(uuid: string, dsoType: string): void {
// We expect a 201 (which means statistics event was created) // We expect a 201 (which means statistics event was created)
expect(resp.status).to.eq(201); expect(resp.status).to.eq(201);
}); });
});
// Remove cookie with fake CSRF token, as it's no longer needed
cy.clearCookie(DSPACE_XSRF_COOKIE);
}); });
} }
// Add as a Cypress command (i.e. assign to 'cy.generateViewEvent') // Add as a Cypress command (i.e. assign to 'cy.generateViewEvent')
Cypress.Commands.add('generateViewEvent', generateViewEvent); Cypress.Commands.add('generateViewEvent', generateViewEvent);
/**
* Can be used by tests to generate a random XSRF/CSRF token and save it to
* the required XSRF/CSRF cookie for usage when sending POST requests or similar.
* The generated CSRF token is returned in a Chainable to allow it to be also sent
* in the CSRF HTTP Header.
* @returns a Cypress Chainable which can be used to get the generated CSRF Token
*/
function createCSRFCookie(): Cypress.Chainable {
// Generate a new token which is a random UUID
const csrfToken: string = uuidv4();
// Save it to our required cookie
cy.task('getRestBaseDomain').then((baseDomain: string) => {
// Create a fake CSRF Token. Set it in the required server-side cookie
cy.setCookie(DSPACE_XSRF_COOKIE, csrfToken, { 'domain': baseDomain });
});
// return the generated token wrapped in a chainable
return cy.wrap(csrfToken);
}
// Add as a Cypress command (i.e. assign to 'cy.createCSRFCookie')
Cypress.Commands.add('createCSRFCookie', createCSRFCookie);

View File

@@ -15,49 +15,57 @@
// Import all custom Commands (from commands.ts) for all tests // Import all custom Commands (from commands.ts) for all tests
import './commands'; import './commands';
// Import Cypress Axe tools for all tests // Import Cypress Axe tools for all tests
// https://github.com/component-driven/cypress-axe // https://github.com/component-driven/cypress-axe
import 'cypress-axe'; import 'cypress-axe';
import { DSPACE_XSRF_COOKIE } from 'src/app/core/xsrf/xsrf.constants';
// Runs once before all tests
before(() => {
// Cypress doesn't have access to the running application in Node.js.
// So, it's not possible to inject or load the AppConfig or environment of the Angular UI.
// Instead, we'll read our running application's config.json, which contains the configs &
// is regenerated at runtime each time the Angular UI application starts up.
cy.task('readUIConfig').then((str: string) => {
// Parse config into a JSON object
const config = JSON.parse(str);
// Find URL of our REST API & save to global variable via task
let baseRestUrl = FALLBACK_TEST_REST_BASE_URL;
if (!config.rest.baseUrl) {
console.warn("Could not load 'rest.baseUrl' from config.json. Falling back to " + FALLBACK_TEST_REST_BASE_URL);
} else {
baseRestUrl = config.rest.baseUrl;
}
cy.task('saveRestBaseURL', baseRestUrl);
// Find domain of our REST API & save to global variable via task.
let baseDomain = FALLBACK_TEST_REST_DOMAIN;
if (!config.rest.host) {
console.warn("Could not load 'rest.host' from config.json. Falling back to " + FALLBACK_TEST_REST_DOMAIN);
} else {
baseDomain = config.rest.host;
}
cy.task('saveRestBaseDomain', baseDomain);
});
});
// Runs once before the first test in each "block" // Runs once before the first test in each "block"
beforeEach(() => { beforeEach(() => {
// Pre-agree to all Klaro cookies by setting the klaro-anonymous cookie // Pre-agree to all Klaro cookies by setting the klaro-anonymous cookie
// This just ensures it doesn't get in the way of matching other objects in the page. // This just ensures it doesn't get in the way of matching other objects in the page.
cy.setCookie('klaro-anonymous', '{%22authentication%22:true%2C%22preferences%22:true%2C%22acknowledgement%22:true%2C%22google-analytics%22:true%2C%22google-recaptcha%22:true}'); cy.setCookie('klaro-anonymous', '{%22authentication%22:true%2C%22preferences%22:true%2C%22acknowledgement%22:true%2C%22google-analytics%22:true%2C%22google-recaptcha%22:true}');
// Remove any CSRF cookies saved from prior tests
cy.clearCookie(DSPACE_XSRF_COOKIE);
}); });
// For better stability between tests, we visit "about:blank" (i.e. blank page) after each test. // NOTE: FALLBACK_TEST_REST_BASE_URL is only used if Cypress cannot read the REST API BaseURL
// This ensures any remaining/outstanding XHR requests are killed, so they don't affect the next test. // from the Angular UI's config.json. See 'before()' above.
// Borrowed from: https://glebbahmutov.com/blog/visit-blank-page-between-tests/ const FALLBACK_TEST_REST_BASE_URL = 'http://localhost:8080/server';
/*afterEach(() => { const FALLBACK_TEST_REST_DOMAIN = 'localhost';
cy.window().then((win) => {
win.location.href = 'about:blank';
});
});*/
// Global constants used in tests
// May be overridden in our cypress.json config file using specified environment variables.
// Default values listed here are all valid for the Demo Entities Data set available at
// https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data
// (This is the data set used in our CI environment)
// Admin account used for administrative tests
export const TEST_ADMIN_USER = Cypress.env('DSPACE_TEST_ADMIN_USER') || 'dspacedemo+admin@gmail.com';
export const TEST_ADMIN_PASSWORD = Cypress.env('DSPACE_TEST_ADMIN_PASSWORD') || 'dspace';
// Community/collection/publication used for view/edit tests
export const TEST_COLLECTION = Cypress.env('DSPACE_TEST_COLLECTION') || '282164f5-d325-4740-8dd1-fa4d6d3e7200';
export const TEST_COMMUNITY = Cypress.env('DSPACE_TEST_COMMUNITY') || '0958c910-2037-42a9-81c7-dca80e3892b4';
export const TEST_ENTITY_PUBLICATION = Cypress.env('DSPACE_TEST_ENTITY_PUBLICATION') || 'e98b0f27-5c19-49a0-960d-eb6ad5287067';
// Search term (should return results) used in search tests
export const TEST_SEARCH_TERM = Cypress.env('DSPACE_TEST_SEARCH_TERM') || 'test';
// Collection used for submission tests
export const TEST_SUBMIT_COLLECTION_NAME = Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_NAME') || 'Sample Collection';
export const TEST_SUBMIT_COLLECTION_UUID = Cypress.env('DSPACE_TEST_SUBMIT_COLLECTION_UUID') || '9d8334e9-25d3-4a67-9cea-3dffdef80144';
export const TEST_SUBMIT_USER = Cypress.env('DSPACE_TEST_SUBMIT_USER') || 'dspacedemo+submit@gmail.com';
export const TEST_SUBMIT_USER_PASSWORD = Cypress.env('DSPACE_TEST_SUBMIT_USER_PASSWORD') || 'dspace';
// USEFUL REGEX for testing // USEFUL REGEX for testing

View File

@@ -7,7 +7,7 @@ import { Options } from 'cypress-axe';
function terminalLog(violations: Result[]) { function terminalLog(violations: Result[]) {
cy.task( cy.task(
'log', 'log',
`${violations.length} accessibility violation${violations.length === 1 ? '' : 's'} ${violations.length === 1 ? 'was' : 'were'} detected` `${violations.length} accessibility violation${violations.length === 1 ? '' : 's'} ${violations.length === 1 ? 'was' : 'were'} detected`,
); );
// pluck specific keys to keep the table readable // pluck specific keys to keep the table readable
const violationData = violations.map( const violationData = violations.map(
@@ -17,8 +17,8 @@ function terminalLog(violations: Result[]) {
description, description,
helpUrl, helpUrl,
nodes: nodes.length, nodes: nodes.length,
html: nodes.map(node => node.html) html: nodes.map(node => node.html),
}) }),
); );
// Print violations as an array, since 'node.html' above often breaks table alignment // Print violations as an array, since 'node.html' above often breaks table alignment
@@ -38,7 +38,7 @@ export const testA11y = (context?: any, options?: Options) => {
// Disable color contrast checks as they are inaccurate / result in a lot of false positives // Disable color contrast checks as they are inaccurate / result in a lot of false positives
// See also open issues in axe-core: https://github.com/dequelabs/axe-core/labels/color%20contrast // See also open issues in axe-core: https://github.com/dequelabs/axe-core/labels/color%20contrast
{ id: 'color-contrast', enabled: false }, { id: 'color-contrast', enabled: false },
] ],
}); });
cy.checkA11y(context, options, terminalLog); cy.checkA11y(context, options, terminalLog);
}; };

View File

@@ -23,14 +23,14 @@ the Docker compose scripts in this 'docker' folder.
This Dockerfile is used to build a *development* DSpace 7 Angular UI image, published as 'dspace/dspace-angular' This Dockerfile is used to build a *development* DSpace 7 Angular UI image, published as 'dspace/dspace-angular'
``` ```
docker build -t dspace/dspace-angular:dspace-7_x . docker build -t dspace/dspace-angular:latest .
``` ```
This image is built *automatically* after each commit is made to the `main` branch. This image is built *automatically* after each commit is made to the `main` branch.
Admins to our DockerHub repo can manually publish with the following command. Admins to our DockerHub repo can manually publish with the following command.
``` ```
docker push dspace/dspace-angular:dspace-7_x docker push dspace/dspace-angular:latest
``` ```
### Dockerfile.dist ### Dockerfile.dist
@@ -39,7 +39,7 @@ The `Dockerfile.dist` is used to generate a *production* build and runtime envir
```bash ```bash
# build the latest image # build the latest image
docker build -f Dockerfile.dist -t dspace/dspace-angular:dspace-7_x-dist . docker build -f Dockerfile.dist -t dspace/dspace-angular:latest-dist .
``` ```
A default/demo version of this image is built *automatically*. A default/demo version of this image is built *automatically*.
@@ -101,8 +101,8 @@ and the backend at http://localhost:8080/server/
## Run DSpace Angular dist build with DSpace Demo site backend ## Run DSpace Angular dist build with DSpace Demo site backend
This allows you to run the Angular UI in *production* mode, pointing it at the demo backend This allows you to run the Angular UI in *production* mode, pointing it at the demo or sandbox backend
(https://api7.dspace.org/server/). (https://demo.dspace.org/server/ or https://sandbox.dspace.org/server/).
``` ```
docker-compose -f docker/docker-compose-dist.yml pull docker-compose -f docker/docker-compose-dist.yml pull

View File

@@ -14,13 +14,8 @@
# Therefore, it should be kept in sync with that file # Therefore, it should be kept in sync with that file
version: "3.7" version: "3.7"
networks:
dspacenet:
services: services:
dspace-cli: dspace-cli:
networks:
dspacenet: {}
environment: environment:
# This assetstore zip is available from https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data # This assetstore zip is available from https://github.com/DSpace-Labs/AIP-Files/releases/tag/demo-entities-data
- LOADASSETS=https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/assetstore.tar.gz - LOADASSETS=https://github.com/DSpace-Labs/AIP-Files/releases/download/demo-entities-data/assetstore.tar.gz

View File

@@ -13,10 +13,16 @@
# #
# Therefore, it should be kept in sync with that file # Therefore, it should be kept in sync with that file
version: "3.7" version: "3.7"
networks:
# Default to using network named 'dspacenet' from docker-compose-rest.yml.
# Its full name will be prepended with the project name (e.g. "-p d7" means it will be named "d7_dspacenet")
# If COMPOSITE_PROJECT_NAME is missing, default value will be "docker" (name of folder this file is in)
default:
name: ${COMPOSE_PROJECT_NAME:-docker}_dspacenet
external: true
services: services:
dspace-cli: dspace-cli:
image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-dspace-7_x}" image: "${DOCKER_OWNER:-dspace}/dspace-cli:${DSPACE_VER:-latest}"
container_name: dspace-cli container_name: dspace-cli
environment: environment:
# Below syntax may look odd, but it is how to override dspace.cfg settings via env variables. # Below syntax may look odd, but it is how to override dspace.cfg settings via env variables.
@@ -30,16 +36,12 @@ services:
# solr.server: Ensure we are using the 'dspacesolr' image for Solr # solr.server: Ensure we are using the 'dspacesolr' image for Solr
solr__P__server: http://dspacesolr:8983/solr solr__P__server: http://dspacesolr:8983/solr
volumes: volumes:
- "assetstore:/dspace/assetstore" # Keep DSpace assetstore directory between reboots
- assetstore:/dspace/assetstore
entrypoint: /dspace/bin/dspace entrypoint: /dspace/bin/dspace
command: help command: help
networks:
- dspacenet
tty: true tty: true
stdin_open: true stdin_open: true
volumes: volumes:
assetstore: assetstore:
networks:
dspacenet:

View File

@@ -33,11 +33,11 @@ services:
# Tell Statistics to commit all views immediately instead of waiting on Solr's autocommit. # Tell Statistics to commit all views immediately instead of waiting on Solr's autocommit.
# This allows us to generate statistics in e2e tests so that statistics pages can be tested thoroughly. # This allows us to generate statistics in e2e tests so that statistics pages can be tested thoroughly.
solr__D__statistics__P__autoCommit: 'false' solr__D__statistics__P__autoCommit: 'false'
image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-latest-test}"
depends_on: depends_on:
- dspacedb - dspacedb
image: dspace/dspace:dspace-7_x-test
networks: networks:
dspacenet: - dspacenet
ports: ports:
- published: 8080 - published: 8080
target: 8080 target: 8080
@@ -45,8 +45,6 @@ services:
tty: true tty: true
volumes: volumes:
- assetstore:/dspace/assetstore - assetstore:/dspace/assetstore
# Mount DSpace's solr configs to a volume, so that we can share to 'dspacesolr' container (see below)
- solr_configs:/dspace/solr
# Ensure that the database is ready BEFORE starting tomcat # Ensure that the database is ready BEFORE starting tomcat
# 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep # 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep
# 2. Then, run database migration to init database tables (including any out-of-order ignored migrations, if any) # 2. Then, run database migration to init database tables (including any out-of-order ignored migrations, if any)
@@ -70,21 +68,18 @@ services:
PGDATA: /pgdata PGDATA: /pgdata
image: dspace/dspace-postgres-pgcrypto:loadsql image: dspace/dspace-postgres-pgcrypto:loadsql
networks: networks:
dspacenet: - dspacenet
stdin_open: true stdin_open: true
tty: true tty: true
volumes: volumes:
# Keep Postgres data directory between reboots
- pgdata:/pgdata - pgdata:/pgdata
# DSpace Solr container # DSpace Solr container
dspacesolr: dspacesolr:
container_name: dspacesolr container_name: dspacesolr
# Uses official Solr image at https://hub.docker.com/_/solr/ image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-latest}"
image: solr:8.11-slim
# Needs main 'dspace' container to start first to guarantee access to solr_configs
depends_on:
- dspace
networks: networks:
dspacenet: - dspacenet
ports: ports:
- published: 8983 - published: 8983
target: 8983 target: 8983
@@ -92,9 +87,6 @@ services:
tty: true tty: true
working_dir: /var/solr/data working_dir: /var/solr/data
volumes: volumes:
# Mount our "solr_configs" volume available under the Solr's configsets folder (in a 'dspace' subfolder)
# This copies the Solr configs from main 'dspace' container into 'dspacesolr' via that volume
- solr_configs:/opt/solr/server/solr/configsets/dspace
# Keep Solr data directory between reboots # Keep Solr data directory between reboots
- solr_data:/var/solr/data - solr_data:/var/solr/data
# Initialize all DSpace Solr cores using the mounted configsets (see above), then start Solr # Initialize all DSpace Solr cores using the mounted configsets (see above), then start Solr
@@ -103,14 +95,18 @@ services:
- '-c' - '-c'
- | - |
init-var-solr init-var-solr
precreate-core authority /opt/solr/server/solr/configsets/dspace/authority precreate-core authority /opt/solr/server/solr/configsets/authority
precreate-core oai /opt/solr/server/solr/configsets/dspace/oai cp -r /opt/solr/server/solr/configsets/authority/* authority
precreate-core search /opt/solr/server/solr/configsets/dspace/search precreate-core oai /opt/solr/server/solr/configsets/oai
precreate-core statistics /opt/solr/server/solr/configsets/dspace/statistics cp -r /opt/solr/server/solr/configsets/oai/* oai
precreate-core search /opt/solr/server/solr/configsets/search
cp -r /opt/solr/server/solr/configsets/search/* search
precreate-core statistics /opt/solr/server/solr/configsets/statistics
cp -r /opt/solr/server/solr/configsets/statistics/* statistics
precreate-core qaevent /opt/solr/server/solr/configsets/qaevent
cp -r /opt/solr/server/solr/configsets/qaevent/* qaevent
exec solr -f exec solr -f
volumes: volumes:
assetstore: assetstore:
pgdata: pgdata:
solr_data: solr_data:
# Special volume used to share Solr configs from 'dspace' to 'dspacesolr' container (see above)
solr_configs:

View File

@@ -24,10 +24,10 @@ services:
# This is because Server Side Rendering (SSR) currently requires a public URL, # This is because Server Side Rendering (SSR) currently requires a public URL,
# see this bug: https://github.com/DSpace/dspace-angular/issues/1485 # see this bug: https://github.com/DSpace/dspace-angular/issues/1485
DSPACE_REST_SSL: 'true' DSPACE_REST_SSL: 'true'
DSPACE_REST_HOST: api7.dspace.org DSPACE_REST_HOST: sandbox.dspace.org
DSPACE_REST_PORT: 443 DSPACE_REST_PORT: 443
DSPACE_REST_NAMESPACE: /server DSPACE_REST_NAMESPACE: /server
image: dspace/dspace-angular:dspace-7_x-dist image: dspace/dspace-angular:${DSPACE_VER:-latest}-dist
build: build:
context: .. context: ..
dockerfile: Dockerfile.dist dockerfile: Dockerfile.dist

View File

@@ -39,11 +39,11 @@ services:
# proxies.trusted.ipranges: This setting is required for a REST API running in Docker to trust requests # proxies.trusted.ipranges: This setting is required for a REST API running in Docker to trust requests
# from the host machine. This IP range MUST correspond to the 'dspacenet' subnet defined above. # from the host machine. This IP range MUST correspond to the 'dspacenet' subnet defined above.
proxies__P__trusted__P__ipranges: '172.23.0' proxies__P__trusted__P__ipranges: '172.23.0'
image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7_x-test}" image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-latest-test}"
depends_on: depends_on:
- dspacedb - dspacedb
networks: networks:
dspacenet: - dspacenet
ports: ports:
- published: 8080 - published: 8080
target: 8080 target: 8080
@@ -51,8 +51,6 @@ services:
tty: true tty: true
volumes: volumes:
- assetstore:/dspace/assetstore - assetstore:/dspace/assetstore
# Mount DSpace's solr configs to a volume, so that we can share to 'dspacesolr' container (see below)
- solr_configs:/dspace/solr
# Ensure that the database is ready BEFORE starting tomcat # Ensure that the database is ready BEFORE starting tomcat
# 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep # 1. While a TCP connection to dspacedb port 5432 is not available, continue to sleep
# 2. Then, run database migration to init database tables # 2. Then, run database migration to init database tables
@@ -69,25 +67,23 @@ services:
container_name: dspacedb container_name: dspacedb
environment: environment:
PGDATA: /pgdata PGDATA: /pgdata
image: dspace/dspace-postgres-pgcrypto image: "${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-latest}"
networks: networks:
dspacenet: - dspacenet
ports: ports:
- published: 5432 - published: 5432
target: 5432 target: 5432
stdin_open: true stdin_open: true
tty: true tty: true
volumes: volumes:
# Keep Postgres data directory between reboots
- pgdata:/pgdata - pgdata:/pgdata
# DSpace Solr container # DSpace Solr container
dspacesolr: dspacesolr:
container_name: dspacesolr container_name: dspacesolr
image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-dspace-7_x}" image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-latest}"
# Needs main 'dspace' container to start first to guarantee access to solr_configs
depends_on:
- dspace
networks: networks:
dspacenet: - dspacenet
ports: ports:
- published: 8983 - published: 8983
target: 8983 target: 8983
@@ -115,10 +111,10 @@ services:
cp -r /opt/solr/server/solr/configsets/search/* search cp -r /opt/solr/server/solr/configsets/search/* search
precreate-core statistics /opt/solr/server/solr/configsets/statistics precreate-core statistics /opt/solr/server/solr/configsets/statistics
cp -r /opt/solr/server/solr/configsets/statistics/* statistics cp -r /opt/solr/server/solr/configsets/statistics/* statistics
precreate-core qaevent /opt/solr/server/solr/configsets/qaevent
cp -r /opt/solr/server/solr/configsets/qaevent/* qaevent
exec solr -f exec solr -f
volumes: volumes:
assetstore: assetstore:
pgdata: pgdata:
solr_data: solr_data:
# Special volume used to share Solr configs from 'dspace' to 'dspacesolr' container (see above)
solr_configs:

View File

@@ -24,7 +24,7 @@ services:
DSPACE_REST_HOST: localhost DSPACE_REST_HOST: localhost
DSPACE_REST_PORT: 8080 DSPACE_REST_PORT: 8080
DSPACE_REST_NAMESPACE: /server DSPACE_REST_NAMESPACE: /server
image: dspace/dspace-angular:dspace-7_x image: dspace/dspace-angular:${DSPACE_VER:-latest}
build: build:
context: .. context: ..
dockerfile: Dockerfile dockerfile: Dockerfile

View File

@@ -48,7 +48,7 @@ dspace-angular connects to your DSpace installation by using its REST endpoint.
```yaml ```yaml
rest: rest:
ssl: true ssl: true
host: api7.dspace.org host: demo.dspace.org
port: 443 port: 443
nameSpace: /server nameSpace: /server
} }
@@ -57,7 +57,7 @@ rest:
Alternately you can set the following environment variables. If any of these are set, it will override all configuration files: Alternately you can set the following environment variables. If any of these are set, it will override all configuration files:
``` ```
DSPACE_REST_SSL=true DSPACE_REST_SSL=true
DSPACE_REST_HOST=api7.dspace.org DSPACE_REST_HOST=demo.dspace.org
DSPACE_REST_PORT=443 DSPACE_REST_PORT=443
DSPACE_REST_NAMESPACE=/server DSPACE_REST_NAMESPACE=/server
``` ```

View File

@@ -15,7 +15,10 @@ module.exports = function (config) {
], ],
client: { client: {
clearContext: false, // leave Jasmine Spec Runner output visible in browser clearContext: false, // leave Jasmine Spec Runner output visible in browser
captureConsole: false captureConsole: false,
jasmine: {
failSpecWithNoExpectations: true
}
}, },
coverageIstanbulReporter: { coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/dspace-angular'), dir: require('path').join(__dirname, './coverage/dspace-angular'),

View File

@@ -1,6 +1,6 @@
{ {
"name": "dspace-angular", "name": "dspace-angular",
"version": "7.6.0", "version": "8.0.0-rc1",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"config:watch": "nodemon", "config:watch": "nodemon",
@@ -15,14 +15,14 @@
"analyze": "webpack-bundle-analyzer dist/browser/stats.json", "analyze": "webpack-bundle-analyzer dist/browser/stats.json",
"build": "ng build --configuration development", "build": "ng build --configuration development",
"build:stats": "ng build --stats-json", "build:stats": "ng build --stats-json",
"build:prod": "yarn run build:ssr", "build:prod": "cross-env NODE_ENV=production yarn run build:ssr",
"build:ssr": "ng build --configuration production && ng run dspace-angular:server:production", "build:ssr": "ng build --configuration production && ng run dspace-angular:server:production",
"test": "ng test --source-map=true --watch=false --configuration test", "test": "ng test --source-map=true --watch=false --configuration test",
"test:watch": "nodemon --exec \"ng test --source-map=true --watch=true --configuration test\"", "test:watch": "nodemon --exec \"ng test --source-map=true --watch=true --configuration test\"",
"test:headless": "ng test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage", "test:headless": "ng test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage",
"lint": "ng lint", "lint": "ng lint",
"lint-fix": "ng lint --fix=true", "lint-fix": "ng lint --fix=true",
"e2e": "ng e2e", "e2e": "cross-env NODE_ENV=production ng e2e",
"clean:dev:config": "rimraf src/assets/config.json", "clean:dev:config": "rimraf src/assets/config.json",
"clean:coverage": "rimraf coverage", "clean:coverage": "rimraf coverage",
"clean:dist": "rimraf dist", "clean:dist": "rimraf dist",
@@ -55,34 +55,34 @@
"ts-node": "10.2.1" "ts-node": "10.2.1"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "^15.2.8", "@angular/animations": "^16.2.12",
"@angular/cdk": "^15.2.8", "@angular/cdk": "^16.2.12",
"@angular/common": "^15.2.8", "@angular/common": "^16.2.12",
"@angular/compiler": "^15.2.8", "@angular/compiler": "^16.2.12",
"@angular/core": "^15.2.8", "@angular/core": "^16.2.12",
"@angular/forms": "^15.2.8", "@angular/forms": "^16.2.12",
"@angular/localize": "15.2.8", "@angular/localize": "16.2.12",
"@angular/platform-browser": "^15.2.8", "@angular/platform-browser": "^16.2.12",
"@angular/platform-browser-dynamic": "^15.2.8", "@angular/platform-browser-dynamic": "^16.2.12",
"@angular/platform-server": "^15.2.8", "@angular/platform-server": "^16.2.12",
"@angular/router": "^15.2.8", "@angular/router": "^16.2.12",
"@babel/runtime": "7.21.0", "@babel/runtime": "7.21.0",
"@kolkov/ngx-gallery": "^2.0.1", "@kolkov/ngx-gallery": "^2.0.1",
"@material-ui/core": "^4.11.0", "@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.11.3", "@material-ui/icons": "^4.11.3",
"@ng-bootstrap/ng-bootstrap": "^11.0.0", "@ng-bootstrap/ng-bootstrap": "^11.0.0",
"@ng-dynamic-forms/core": "^15.0.0", "@ng-dynamic-forms/core": "^16.0.0",
"@ng-dynamic-forms/ui-ng-bootstrap": "^15.0.0", "@ng-dynamic-forms/ui-ng-bootstrap": "^16.0.0",
"@ngrx/effects": "^15.4.0", "@ngrx/effects": "^16.3.0",
"@ngrx/router-store": "^15.4.0", "@ngrx/router-store": "^16.3.0",
"@ngrx/store": "^15.4.0", "@ngrx/store": "^16.3.0",
"@nguniversal/express-engine": "^15.2.1", "@nguniversal/express-engine": "^16.2.0",
"@ngx-translate/core": "^14.0.0", "@ngx-translate/core": "^14.0.0",
"@nicky-lenaers/ngx-scroll-to": "^14.0.0", "@nicky-lenaers/ngx-scroll-to": "^14.0.0",
"@types/grecaptcha": "^3.0.4", "@types/grecaptcha": "^3.0.4",
"angular-idle-preload": "3.0.0", "angular-idle-preload": "3.0.0",
"angulartics2": "^12.2.0", "angulartics2": "^12.2.0",
"axios": "^0.27.2", "axios": "^1.6.0",
"bootstrap": "^4.6.1", "bootstrap": "^4.6.1",
"cerialize": "0.1.18", "cerialize": "0.1.18",
"cli-progress": "^3.12.0", "cli-progress": "^3.12.0",
@@ -94,11 +94,12 @@
"date-fns-tz": "^1.3.7", "date-fns-tz": "^1.3.7",
"deepmerge": "^4.3.1", "deepmerge": "^4.3.1",
"ejs": "^3.1.9", "ejs": "^3.1.9",
"express": "^4.18.2", "express": "^4.19.2",
"express-rate-limit": "^5.1.3", "express-rate-limit": "^5.1.3",
"fast-json-patch": "^3.1.1", "fast-json-patch": "^3.1.1",
"filesize": "^6.1.0", "filesize": "^6.1.0",
"http-proxy-middleware": "^1.0.5", "http-proxy-middleware": "^1.0.5",
"http-terminator": "^3.2.0",
"isbot": "^3.6.10", "isbot": "^3.6.10",
"js-cookie": "2.2.1", "js-cookie": "2.2.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
@@ -109,46 +110,44 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lru-cache": "^7.14.1", "lru-cache": "^7.14.1",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"markdown-it-mathjax3": "^4.3.2",
"mirador": "^3.3.0", "mirador": "^3.3.0",
"mirador-dl-plugin": "^0.13.0", "mirador-dl-plugin": "^0.13.0",
"mirador-share-plugin": "^0.11.0", "mirador-share-plugin": "^0.11.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"ng-mocks": "^14.10.0", "ng-mocks": "^14.10.0",
"ng2-file-upload": "1.4.0", "ng2-file-upload": "5.0.0",
"ng2-nouislider": "^1.8.3", "ng2-nouislider": "^2.0.0",
"ngx-infinite-scroll": "^15.0.0", "ngx-infinite-scroll": "^16.0.0",
"ngx-pagination": "6.0.3", "ngx-pagination": "6.0.3",
"ngx-sortablejs": "^11.1.0", "ngx-ui-switch": "^14.1.0",
"ngx-ui-switch": "^14.0.3", "nouislider": "^15.7.1",
"nouislider": "^14.6.3",
"pem": "1.14.7", "pem": "1.14.7",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react-copy-to-clipboard": "^5.1.0", "react-copy-to-clipboard": "^5.1.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rxjs": "^7.8.0", "rxjs": "^7.8.0",
"sanitize-html": "^2.10.0", "sanitize-html": "^2.12.1",
"sortablejs": "1.15.0", "sortablejs": "1.15.0",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"webfontloader": "1.6.28", "webfontloader": "1.6.28",
"zone.js": "~0.11.5" "zone.js": "~0.13.3"
}, },
"devDependencies": { "devDependencies": {
"@angular-builders/custom-webpack": "~15.0.0", "@angular-builders/custom-webpack": "~16.0.0",
"@angular-devkit/build-angular": "^15.2.6", "@angular-devkit/build-angular": "^16.2.12",
"@angular-eslint/builder": "15.2.1", "@angular-eslint/builder": "16.3.1",
"@angular-eslint/eslint-plugin": "15.2.1", "@angular-eslint/eslint-plugin": "16.3.1",
"@angular-eslint/eslint-plugin-template": "15.2.1", "@angular-eslint/eslint-plugin-template": "16.3.1",
"@angular-eslint/schematics": "15.2.1", "@angular-eslint/schematics": "16.3.1",
"@angular-eslint/template-parser": "15.2.1", "@angular-eslint/template-parser": "16.3.1",
"@angular/cli": "^15.2.6", "@angular/cli": "^16.2.12",
"@angular/compiler-cli": "^15.2.8", "@angular/compiler-cli": "^16.2.12",
"@angular/language-service": "^15.2.8", "@angular/language-service": "^16.2.12",
"@cypress/schematic": "^1.5.0", "@cypress/schematic": "^1.5.0",
"@fortawesome/fontawesome-free": "^6.4.0", "@fortawesome/fontawesome-free": "^6.4.0",
"@ngrx/store-devtools": "^15.4.0", "@ngrx/store-devtools": "^16.3.0",
"@ngtools/webpack": "^15.2.6", "@ngtools/webpack": "^16.2.12",
"@nguniversal/builders": "^15.2.1", "@nguniversal/builders": "^16.2.0",
"@types/deep-freeze": "0.1.2", "@types/deep-freeze": "0.1.2",
"@types/ejs": "^3.1.2", "@types/ejs": "^3.1.2",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
@@ -159,19 +158,22 @@
"@types/sanitize-html": "^2.9.0", "@types/sanitize-html": "^2.9.0",
"@typescript-eslint/eslint-plugin": "^5.59.1", "@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1", "@typescript-eslint/parser": "^5.59.1",
"axe-core": "^4.7.0", "axe-core": "^4.7.2",
"compression-webpack-plugin": "^9.2.0", "compression-webpack-plugin": "^9.2.0",
"copy-webpack-plugin": "^6.4.1", "copy-webpack-plugin": "^6.4.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cypress": "12.10.0", "cypress": "12.17.4",
"cypress-axe": "^1.4.0", "cypress-axe": "^1.4.0",
"deep-freeze": "0.0.1", "deep-freeze": "0.0.1",
"eslint": "^8.39.0", "eslint": "^8.39.0",
"eslint-plugin-deprecation": "^1.4.1", "eslint-plugin-deprecation": "^1.4.1",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsdoc": "^39.6.4", "eslint-plugin-import-newlines": "^1.3.1",
"eslint-plugin-jsdoc": "^45.0.0",
"eslint-plugin-jsonc": "^2.6.0", "eslint-plugin-jsonc": "^2.6.0",
"eslint-plugin-lodash": "^7.4.0", "eslint-plugin-lodash": "^7.4.0",
"eslint-plugin-rxjs": "^5.0.3",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^2.0.0", "eslint-plugin-unused-imports": "^2.0.0",
"express-static-gzip": "^2.1.7", "express-static-gzip": "^2.1.7",
"jasmine-core": "^3.8.0", "jasmine-core": "^3.8.0",
@@ -182,7 +184,7 @@
"karma-jasmine": "~4.0.0", "karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0", "karma-jasmine-html-reporter": "^1.5.0",
"karma-mocha-reporter": "2.2.5", "karma-mocha-reporter": "2.2.5",
"ngx-mask": "^13.1.7", "ngx-mask": "14.2.4",
"nodemon": "^2.0.22", "nodemon": "^2.0.22",
"postcss": "^8.4", "postcss": "^8.4",
"postcss-apply": "0.12.0", "postcss-apply": "0.12.0",
@@ -198,7 +200,7 @@
"sass-loader": "^12.6.0", "sass-loader": "^12.6.0",
"sass-resources-loader": "^2.2.5", "sass-resources-loader": "^2.2.5",
"ts-node": "^8.10.2", "ts-node": "^8.10.2",
"typescript": "~4.8.4", "typescript": "~4.9.3",
"webpack": "5.76.1", "webpack": "5.76.1",
"webpack-bundle-analyzer": "^4.8.0", "webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^4.2.0", "webpack-cli": "^4.2.0",

View File

@@ -32,6 +32,7 @@ import isbot from 'isbot';
import { createCertificate } from 'pem'; import { createCertificate } from 'pem';
import { createServer } from 'https'; import { createServer } from 'https';
import { json } from 'body-parser'; import { json } from 'body-parser';
import { createHttpTerminator } from 'http-terminator';
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
@@ -47,7 +48,7 @@ import { hasNoValue, hasValue } from './src/app/shared/empty.util';
import { UIServerConfig } from './src/config/ui-server-config.interface'; import { UIServerConfig } from './src/config/ui-server-config.interface';
import { ServerAppModule } from './src/main.server'; import bootstrap from './src/main.server';
import { buildAppConfig } from './src/config/config.server'; import { buildAppConfig } from './src/config/config.server';
import { APP_CONFIG, AppConfig } from './src/config/app-config.interface'; import { APP_CONFIG, AppConfig } from './src/config/app-config.interface';
@@ -129,7 +130,8 @@ export function app() {
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine) // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', (_, options, callback) => server.engine('html', (_, options, callback) =>
ngExpressEngine({ ngExpressEngine({
bootstrap: ServerAppModule, bootstrap,
inlineCriticalCss: environment.universal.inlineCriticalCss,
providers: [ providers: [
{ {
provide: REQUEST, provide: REQUEST,
@@ -141,10 +143,10 @@ export function app() {
}, },
{ {
provide: APP_CONFIG, provide: APP_CONFIG,
useValue: environment useValue: environment,
} },
] ],
})(_, (options as any), callback) })(_, (options as any), callback),
); );
server.engine('ejs', ejs.renderFile); server.engine('ejs', ejs.renderFile);
@@ -161,7 +163,7 @@ export function app() {
server.get('/robots.txt', (req, res) => { server.get('/robots.txt', (req, res) => {
res.setHeader('content-type', 'text/plain'); res.setHeader('content-type', 'text/plain');
res.render('assets/robots.txt.ejs', { res.render('assets/robots.txt.ejs', {
'origin': req.protocol + '://' + req.headers.host 'origin': req.protocol + '://' + req.headers.host,
}); });
}); });
@@ -176,7 +178,7 @@ export function app() {
router.use('/sitemap**', createProxyMiddleware({ router.use('/sitemap**', createProxyMiddleware({
target: `${environment.rest.baseUrl}/sitemaps`, target: `${environment.rest.baseUrl}/sitemaps`,
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'), pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
changeOrigin: true changeOrigin: true,
})); }));
/** /**
@@ -185,7 +187,7 @@ export function app() {
router.use('/signposting**', createProxyMiddleware({ router.use('/signposting**', createProxyMiddleware({
target: `${environment.rest.baseUrl}`, target: `${environment.rest.baseUrl}`,
pathRewrite: path => path.replace(environment.ui.nameSpace, '/'), pathRewrite: path => path.replace(environment.ui.nameSpace, '/'),
changeOrigin: true changeOrigin: true,
})); }));
/** /**
@@ -196,7 +198,7 @@ export function app() {
const RateLimit = require('express-rate-limit'); const RateLimit = require('express-rate-limit');
const limiter = new RateLimit({ const limiter = new RateLimit({
windowMs: (environment.ui as UIServerConfig).rateLimiter.windowMs, windowMs: (environment.ui as UIServerConfig).rateLimiter.windowMs,
max: (environment.ui as UIServerConfig).rateLimiter.max max: (environment.ui as UIServerConfig).rateLimiter.max,
}); });
server.use(limiter); server.use(limiter);
} }
@@ -320,22 +322,23 @@ function initCache() {
if (botCacheEnabled()) { if (botCacheEnabled()) {
// Initialize a new "least-recently-used" item cache (where least recently used pages are removed first) // Initialize a new "least-recently-used" item cache (where least recently used pages are removed first)
// See https://www.npmjs.com/package/lru-cache // See https://www.npmjs.com/package/lru-cache
// When enabled, each page defaults to expiring after 1 day // When enabled, each page defaults to expiring after 1 day (defined in default-app-config.ts)
botCache = new LRU( { botCache = new LRU( {
max: environment.cache.serverSide.botCache.max, max: environment.cache.serverSide.botCache.max,
ttl: environment.cache.serverSide.botCache.timeToLive || 24 * 60 * 60 * 1000, // 1 day ttl: environment.cache.serverSide.botCache.timeToLive,
allowStale: environment.cache.serverSide.botCache.allowStale ?? true // if object is stale, return stale value before deleting allowStale: environment.cache.serverSide.botCache.allowStale,
}); });
} }
if (anonymousCacheEnabled()) { if (anonymousCacheEnabled()) {
// NOTE: While caches may share SSR pages, this cache must be kept separately because the timeToLive // NOTE: While caches may share SSR pages, this cache must be kept separately because the timeToLive
// may expire pages more frequently. // may expire pages more frequently.
// When enabled, each page defaults to expiring after 10 seconds (to minimize anonymous users seeing out-of-date content) // When enabled, each page defaults to expiring after 10 seconds (defined in default-app-config.ts)
// to minimize anonymous users seeing out-of-date content
anonymousCache = new LRU( { anonymousCache = new LRU( {
max: environment.cache.serverSide.anonymousCache.max, max: environment.cache.serverSide.anonymousCache.max,
ttl: environment.cache.serverSide.anonymousCache.timeToLive || 10 * 1000, // 10 seconds ttl: environment.cache.serverSide.anonymousCache.timeToLive,
allowStale: environment.cache.serverSide.anonymousCache.allowStale ?? true // if object is stale, return stale value before deleting allowStale: environment.cache.serverSide.anonymousCache.allowStale,
}); });
} }
} }
@@ -413,7 +416,7 @@ function checkCacheForRequest(cacheName: string, cache: LRU<string, any>, req, r
const key = getCacheKey(req); const key = getCacheKey(req);
// Check if this page is in our cache // Check if this page is in our cache
let cachedCopy = cache.get(key); const cachedCopy = cache.get(key);
if (cachedCopy) { if (cachedCopy) {
if (environment.cache.serverSide.debug) { console.log(`CACHE HIT FOR ${key} in ${cacheName} cache`); } if (environment.cache.serverSide.debug) { console.log(`CACHE HIT FOR ${key} in ${cacheName} cache`); }
@@ -487,7 +490,7 @@ function saveToCache(req, page: any) {
*/ */
function hasNotSucceeded(statusCode) { function hasNotSucceeded(statusCode) {
const rgx = new RegExp(/^20+/); const rgx = new RegExp(/^20+/);
return !rgx.test(statusCode) return !rgx.test(statusCode);
} }
function retrieveHeaders(response) { function retrieveHeaders(response) {
@@ -525,23 +528,46 @@ function serverStarted() {
* @param keys SSL credentials * @param keys SSL credentials
*/ */
function createHttpsServer(keys) { function createHttpsServer(keys) {
createServer({ const listener = createServer({
key: keys.serviceKey, key: keys.serviceKey,
cert: keys.certificate cert: keys.certificate,
}, app).listen(environment.ui.port, environment.ui.host, () => { }, app).listen(environment.ui.port, environment.ui.host, () => {
serverStarted(); serverStarted();
}); });
// Graceful shutdown when signalled
const terminator = createHttpTerminator({ server: listener });
process.on('SIGINT', () => {
void (async ()=> {
console.debug('Closing HTTPS server on signal');
await terminator.terminate().catch(e => { console.error(e); });
console.debug('HTTPS server closed');
})();
});
} }
/**
* Create an HTTP server with the configured port and host.
*/
function run() { function run() {
const port = environment.ui.port || 4000; const port = environment.ui.port || 4000;
const host = environment.ui.host || '/'; const host = environment.ui.host || '/';
// Start up the Node server // Start up the Node server
const server = app(); const server = app();
server.listen(port, host, () => { const listener = server.listen(port, host, () => {
serverStarted(); serverStarted();
}); });
// Graceful shutdown when signalled
const terminator = createHttpTerminator({ server: listener });
process.on('SIGINT', () => {
void (async () => {
console.debug('Closing HTTP server on signal');
await terminator.terminate().catch(e => { console.error(e); });
console.debug('HTTP server closed.');return undefined;
})();
});
} }
function start() { function start() {
@@ -572,7 +598,7 @@ function start() {
if (serviceKey && certificate) { if (serviceKey && certificate) {
createHttpsServer({ createHttpsServer({
serviceKey: serviceKey, serviceKey: serviceKey,
certificate: certificate certificate: certificate,
}); });
} else { } else {
console.warn('Disabling certificate validation and proceeding with a self-signed certificate. If this is a production server, it is recommended that you configure a valid certificate instead.'); console.warn('Disabling certificate validation and proceeding with a self-signed certificate. If this is a production server, it is recommended that you configure a valid certificate instead.');
@@ -581,7 +607,7 @@ function start() {
createCertificate({ createCertificate({
days: 1, days: 1,
selfSigned: true selfSigned: true,
}, (error, keys) => { }, (error, keys) => {
createHttpsServer(keys); createHttpsServer(keys);
}); });
@@ -602,7 +628,7 @@ function healthCheck(req, res) {
}) })
.catch((error) => { .catch((error) => {
res.status(error.response.status).send({ res.status(error.response.status).send({
error: error.message error: error.message,
}); });
}); });
} }

View File

@@ -0,0 +1,117 @@
import { AbstractControl } from '@angular/forms';
import {
mapToCanActivate,
Route,
} from '@angular/router';
import {
DYNAMIC_ERROR_MESSAGES_MATCHER,
DynamicErrorMessagesMatcher,
} from '@ng-dynamic-forms/core';
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
import { GroupAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/group-administrator.guard';
import { SiteAdministratorGuard } from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
import {
EPERSON_PATH,
GROUP_PATH,
} from './access-control-routing-paths';
import { BulkAccessComponent } from './bulk-access/bulk-access.component';
import { EPeopleRegistryComponent } from './epeople-registry/epeople-registry.component';
import { EPersonFormComponent } from './epeople-registry/eperson-form/eperson-form.component';
import { EPersonResolver } from './epeople-registry/eperson-resolver.service';
import { GroupFormComponent } from './group-registry/group-form/group-form.component';
import { GroupPageGuard } from './group-registry/group-page.guard';
import { GroupsRegistryComponent } from './group-registry/groups-registry.component';
/**
* Condition for displaying error messages on email form field
*/
export const ValidateEmailErrorStateMatcher: DynamicErrorMessagesMatcher =
(control: AbstractControl, model: any, hasFocus: boolean) => {
return (control.touched && !hasFocus) || (control.errors?.emailTaken && hasFocus);
};
const providers = [
{
provide: DYNAMIC_ERROR_MESSAGES_MATCHER,
useValue: ValidateEmailErrorStateMatcher,
},
];
export const ROUTES: Route[] = [
{
path: EPERSON_PATH,
component: EPeopleRegistryComponent,
resolve: {
breadcrumb: i18nBreadcrumbResolver,
},
providers,
data: { title: 'admin.access-control.epeople.title', breadcrumbKey: 'admin.access-control.epeople' },
canActivate: mapToCanActivate([SiteAdministratorGuard]),
},
{
path: `${EPERSON_PATH}/create`,
component: EPersonFormComponent,
resolve: {
breadcrumb: i18nBreadcrumbResolver,
},
providers,
data: { title: 'admin.access-control.epeople.add.title', breadcrumbKey: 'admin.access-control.epeople.add' },
canActivate: mapToCanActivate([SiteAdministratorGuard]),
},
{
path: `${EPERSON_PATH}/:id/edit`,
component: EPersonFormComponent,
resolve: {
breadcrumb: i18nBreadcrumbResolver,
ePerson: EPersonResolver,
},
providers,
data: { title: 'admin.access-control.epeople.edit.title', breadcrumbKey: 'admin.access-control.epeople.edit' },
canActivate: mapToCanActivate([SiteAdministratorGuard]),
},
{
path: GROUP_PATH,
component: GroupsRegistryComponent,
resolve: {
breadcrumb: i18nBreadcrumbResolver,
},
providers,
data: { title: 'admin.access-control.groups.title', breadcrumbKey: 'admin.access-control.groups' },
canActivate: mapToCanActivate([GroupAdministratorGuard]),
},
{
path: `${GROUP_PATH}/create`,
component: GroupFormComponent,
resolve: {
breadcrumb: i18nBreadcrumbResolver,
},
providers,
data: {
title: 'admin.access-control.groups.title.addGroup',
breadcrumbKey: 'admin.access-control.groups.addGroup',
},
canActivate: mapToCanActivate([GroupAdministratorGuard]),
},
{
path: `${GROUP_PATH}/:groupId/edit`,
component: GroupFormComponent,
resolve: {
breadcrumb: i18nBreadcrumbResolver,
},
providers,
data: {
title: 'admin.access-control.groups.title.singleGroup',
breadcrumbKey: 'admin.access-control.groups.singleGroup',
},
canActivate: mapToCanActivate([GroupPageGuard]),
},
{
path: 'bulk-access',
component: BulkAccessComponent,
resolve: {
breadcrumb: i18nBreadcrumbResolver,
},
data: { title: 'admin.access-control.bulk-access.title', breadcrumbKey: 'admin.access-control.bulk-access' },
canActivate: mapToCanActivate([SiteAdministratorGuard]),
},
];

View File

@@ -1,12 +1,22 @@
import { URLCombiner } from '../core/url-combiner/url-combiner';
import { getAccessControlModuleRoute } from '../app-routing-paths'; import { getAccessControlModuleRoute } from '../app-routing-paths';
import { URLCombiner } from '../core/url-combiner/url-combiner';
export const GROUP_EDIT_PATH = 'groups'; export const EPERSON_PATH = 'epeople';
export function getEPersonsRoute(): string {
return new URLCombiner(getAccessControlModuleRoute(), EPERSON_PATH).toString();
}
export function getEPersonEditRoute(id: string): string {
return new URLCombiner(getEPersonsRoute(), id, 'edit').toString();
}
export const GROUP_PATH = 'groups';
export function getGroupsRoute() { export function getGroupsRoute() {
return new URLCombiner(getAccessControlModuleRoute(), GROUP_EDIT_PATH).toString(); return new URLCombiner(getAccessControlModuleRoute(), GROUP_PATH).toString();
} }
export function getGroupEditRoute(id: string) { export function getGroupEditRoute(id: string) {
return new URLCombiner(getAccessControlModuleRoute(), GROUP_EDIT_PATH, id).toString(); return new URLCombiner(getGroupsRoute(), id, 'edit').toString();
} }

View File

@@ -1,73 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { EPeopleRegistryComponent } from './epeople-registry/epeople-registry.component';
import { GroupFormComponent } from './group-registry/group-form/group-form.component';
import { GroupsRegistryComponent } from './group-registry/groups-registry.component';
import { GROUP_EDIT_PATH } from './access-control-routing-paths';
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
import { GroupPageGuard } from './group-registry/group-page.guard';
import {
GroupAdministratorGuard
} from '../core/data/feature-authorization/feature-authorization-guard/group-administrator.guard';
import {
SiteAdministratorGuard
} from '../core/data/feature-authorization/feature-authorization-guard/site-administrator.guard';
import { BulkAccessComponent } from './bulk-access/bulk-access.component';
@NgModule({
imports: [
RouterModule.forChild([
{
path: 'epeople',
component: EPeopleRegistryComponent,
resolve: {
breadcrumb: I18nBreadcrumbResolver
},
data: { title: 'admin.access-control.epeople.title', breadcrumbKey: 'admin.access-control.epeople' },
canActivate: [SiteAdministratorGuard]
},
{
path: GROUP_EDIT_PATH,
component: GroupsRegistryComponent,
resolve: {
breadcrumb: I18nBreadcrumbResolver
},
data: { title: 'admin.access-control.groups.title', breadcrumbKey: 'admin.access-control.groups' },
canActivate: [GroupAdministratorGuard]
},
{
path: `${GROUP_EDIT_PATH}/newGroup`,
component: GroupFormComponent,
resolve: {
breadcrumb: I18nBreadcrumbResolver
},
data: { title: 'admin.access-control.groups.title.addGroup', breadcrumbKey: 'admin.access-control.groups.addGroup' },
canActivate: [GroupAdministratorGuard]
},
{
path: `${GROUP_EDIT_PATH}/:groupId`,
component: GroupFormComponent,
resolve: {
breadcrumb: I18nBreadcrumbResolver
},
data: { title: 'admin.access-control.groups.title.singleGroup', breadcrumbKey: 'admin.access-control.groups.singleGroup' },
canActivate: [GroupPageGuard]
},
{
path: 'bulk-access',
component: BulkAccessComponent,
resolve: {
breadcrumb: I18nBreadcrumbResolver
},
data: { title: 'admin.access-control.bulk-access.title', breadcrumbKey: 'admin.access-control.bulk-access' },
canActivate: [SiteAdministratorGuard]
},
])
]
})
/**
* Routing module for the AccessControl section of the admin sidebar
*/
export class AccessControlRoutingModule {
}

View File

@@ -1,67 +0,0 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { SharedModule } from '../shared/shared.module';
import { AccessControlRoutingModule } from './access-control-routing.module';
import { EPeopleRegistryComponent } from './epeople-registry/epeople-registry.component';
import { EPersonFormComponent } from './epeople-registry/eperson-form/eperson-form.component';
import { GroupFormComponent } from './group-registry/group-form/group-form.component';
import { MembersListComponent } from './group-registry/group-form/members-list/members-list.component';
import { SubgroupsListComponent } from './group-registry/group-form/subgroup-list/subgroups-list.component';
import { GroupsRegistryComponent } from './group-registry/groups-registry.component';
import { FormModule } from '../shared/form/form.module';
import { DYNAMIC_ERROR_MESSAGES_MATCHER, DynamicErrorMessagesMatcher } from '@ng-dynamic-forms/core';
import { AbstractControl } from '@angular/forms';
import { BulkAccessComponent } from './bulk-access/bulk-access.component';
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
import { BulkAccessBrowseComponent } from './bulk-access/browse/bulk-access-browse.component';
import { BulkAccessSettingsComponent } from './bulk-access/settings/bulk-access-settings.component';
import { SearchModule } from '../shared/search/search.module';
import { AccessControlFormModule } from '../shared/access-control-form-container/access-control-form.module';
/**
* Condition for displaying error messages on email form field
*/
export const ValidateEmailErrorStateMatcher: DynamicErrorMessagesMatcher =
(control: AbstractControl, model: any, hasFocus: boolean) => {
return (control.touched && !hasFocus) || (control.errors?.emailTaken && hasFocus);
};
@NgModule({
imports: [
CommonModule,
SharedModule,
RouterModule,
AccessControlRoutingModule,
FormModule,
NgbAccordionModule,
SearchModule,
AccessControlFormModule,
],
exports: [
MembersListComponent,
],
declarations: [
EPeopleRegistryComponent,
EPersonFormComponent,
GroupsRegistryComponent,
GroupFormComponent,
SubgroupsListComponent,
MembersListComponent,
BulkAccessComponent,
BulkAccessBrowseComponent,
BulkAccessSettingsComponent,
],
providers: [
{
provide: DYNAMIC_ERROR_MESSAGES_MATCHER,
useValue: ValidateEmailErrorStateMatcher
},
]
})
/**
* This module handles all components related to the access control pages
*/
export class AccessControlModule {
}

View File

@@ -1,15 +1,15 @@
<ngb-accordion #acc="ngbAccordion" [activeIds]="'browse'"> <ngb-accordion #acc="ngbAccordion" [activeIds]="'browse'">
<ngb-panel [id]="'browse'"> <ngb-panel [id]="'browse'">
<ng-template ngbPanelHeader> <ng-template ngbPanelHeader>
<div class="w-100 d-flex justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('browse')" <div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('browse')"
data-test="browse"> data-test="browse">
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" <button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()"
[attr.aria-expanded]="!acc.isExpanded('browse')" [attr.aria-expanded]="acc.isExpanded('browse')"
aria-controls="collapsePanels"> aria-controls="bulk-access-browse-panel-content">
{{ 'admin.access-control.bulk-access-browse.header' | translate }} {{ 'admin.access-control.bulk-access-browse.header' | translate }}
</button> </button>
<div class="text-right d-flex"> <div class="text-right d-flex gap-2">
<div class="ml-3 d-inline-block"> <div class="d-flex my-auto">
<span *ngIf="acc.isExpanded('browse')" class="fas fa-chevron-up fa-fw"></span> <span *ngIf="acc.isExpanded('browse')" class="fas fa-chevron-up fa-fw"></span>
<span *ngIf="!acc.isExpanded('browse')" class="fas fa-chevron-down fa-fw"></span> <span *ngIf="!acc.isExpanded('browse')" class="fas fa-chevron-down fa-fw"></span>
</div> </div>
@@ -17,8 +17,9 @@
</div> </div>
</ng-template> </ng-template>
<ng-template ngbPanelContent> <ng-template ngbPanelContent>
<div id="bulk-access-browse-panel-content">
<ul ngbNav #nav="ngbNav" [(activeId)]="activateId" class="nav-pills"> <ul ngbNav #nav="ngbNav" [(activeId)]="activateId" class="nav-pills">
<li [ngbNavItem]="'search'"> <li [ngbNavItem]="'search'" role="presentation">
<a ngbNavLink>{{'admin.access-control.bulk-access-browse.search.header' | translate}}</a> <a ngbNavLink>{{'admin.access-control.bulk-access-browse.search.header' | translate}}</a>
<ng-template ngbNavContent> <ng-template ngbNavContent>
<div class="mx-n3"> <div class="mx-n3">
@@ -29,7 +30,7 @@
</div> </div>
</ng-template> </ng-template>
</li> </li>
<li [ngbNavItem]="'selected'"> <li [ngbNavItem]="'selected'" role="presentation">
<a ngbNavLink> <a ngbNavLink>
{{'admin.access-control.bulk-access-browse.selected.header' | translate: {number: ((objectsSelected$ | async)?.payload?.totalElements) ? (objectsSelected$ | async)?.payload?.totalElements : '0'} }} {{'admin.access-control.bulk-access-browse.selected.header' | translate: {number: ((objectsSelected$ | async)?.payload?.totalElements) ? (objectsSelected$ | async)?.payload?.totalElements : '0'} }}
</a> </a>
@@ -62,6 +63,7 @@
</li> </li>
</ul> </ul>
<div [ngbNavOutlet]="nav" class="mt-5"></div> <div [ngbNavOutlet]="nav" class="mt-5"></div>
</div>
</ng-template> </ng-template>
</ngb-panel> </ngb-panel>
</ngb-accordion> </ngb-accordion>

View File

@@ -1,16 +1,28 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
import { of } from 'rxjs'; ComponentFixture,
import { NgbAccordionModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; TestBed,
waitForAsync,
} from '@angular/core/testing';
import {
NgbAccordionModule,
NgbNavModule,
} from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs';
import { BulkAccessBrowseComponent } from './bulk-access-browse.component'; import { buildPaginatedList } from '../../../core/data/paginated-list.model';
import { PageInfo } from '../../../core/shared/page-info.model';
import { getMockThemeService } from '../../../shared/mocks/theme-service.mock';
import { ListableObjectComponentLoaderComponent } from '../../../shared/object-collection/shared/listable-object/listable-object-component-loader.component';
import { SelectableListItemControlComponent } from '../../../shared/object-collection/shared/selectable-list-item-control/selectable-list-item-control.component';
import { SelectableListService } from '../../../shared/object-list/selectable-list/selectable-list.service'; import { SelectableListService } from '../../../shared/object-list/selectable-list/selectable-list.service';
import { SelectableObject } from '../../../shared/object-list/selectable-list/selectable-list.service.spec'; import { SelectableObject } from '../../../shared/object-list/selectable-list/selectable-list.service.spec';
import { PageInfo } from '../../../core/shared/page-info.model'; import { PaginationComponent } from '../../../shared/pagination/pagination.component';
import { buildPaginatedList } from '../../../core/data/paginated-list.model';
import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils';
import { ThemedSearchComponent } from '../../../shared/search/themed-search.component';
import { ThemeService } from '../../../shared/theme-support/theme.service';
import { BulkAccessBrowseComponent } from './bulk-access-browse.component';
describe('BulkAccessBrowseComponent', () => { describe('BulkAccessBrowseComponent', () => {
let component: BulkAccessBrowseComponent; let component: BulkAccessBrowseComponent;
@@ -31,14 +43,28 @@ describe('BulkAccessBrowseComponent', () => {
imports: [ imports: [
NgbAccordionModule, NgbAccordionModule,
NgbNavModule, NgbNavModule,
TranslateModule.forRoot() TranslateModule.forRoot(),
BulkAccessBrowseComponent,
],
providers: [
{ provide: SelectableListService, useValue: selectableListService },
{ provide: ThemeService, useValue: getMockThemeService() },
], ],
declarations: [BulkAccessBrowseComponent],
providers: [ { provide: SelectableListService, useValue: selectableListService }, ],
schemas: [ schemas: [
NO_ERRORS_SCHEMA NO_ERRORS_SCHEMA,
] ],
}).compileComponents(); })
.overrideComponent(BulkAccessBrowseComponent, {
remove: {
imports: [
PaginationComponent,
ThemedSearchComponent,
SelectableListItemControlComponent,
ListableObjectComponentLoaderComponent,
],
},
})
.compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
@@ -72,7 +98,7 @@ describe('BulkAccessBrowseComponent', () => {
'elementsPerPage': 5, 'elementsPerPage': 5,
'totalElements': 2, 'totalElements': 2,
'totalPages': 1, 'totalPages': 1,
'currentPage': 1 'currentPage': 1,
}), [selected1, selected2]); }), [selected1, selected2]);
const rd = createSuccessfulRemoteDataObject(list); const rd = createSuccessfulRemoteDataObject(list);

View File

@@ -1,19 +1,48 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import {
AsyncPipe,
NgForOf,
NgIf,
} from '@angular/common';
import {
Component,
Input,
OnDestroy,
OnInit,
} from '@angular/core';
import {
NgbAccordionModule,
NgbNavModule,
} from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { NgxPaginationModule } from 'ngx-pagination';
import {
BehaviorSubject,
Subscription,
} from 'rxjs';
import {
distinctUntilChanged,
map,
} from 'rxjs/operators';
import { BehaviorSubject, Subscription } from 'rxjs'; import {
import { distinctUntilChanged, map } from 'rxjs/operators'; buildPaginatedList,
PaginatedList,
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component'; } from '../../../core/data/paginated-list.model';
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
import { SelectableListService } from '../../../shared/object-list/selectable-list/selectable-list.service';
import { SelectableListState } from '../../../shared/object-list/selectable-list/selectable-list.reducer';
import { RemoteData } from '../../../core/data/remote-data'; import { RemoteData } from '../../../core/data/remote-data';
import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils';
import { PageInfo } from '../../../core/shared/page-info.model'; import { PageInfo } from '../../../core/shared/page-info.model';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-configuration.service';
import { hasValue } from '../../../shared/empty.util'; import { hasValue } from '../../../shared/empty.util';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
import { ListableObjectComponentLoaderComponent } from '../../../shared/object-collection/shared/listable-object/listable-object-component-loader.component';
import { SelectableListItemControlComponent } from '../../../shared/object-collection/shared/selectable-list-item-control/selectable-list-item-control.component';
import { SelectableListState } from '../../../shared/object-list/selectable-list/selectable-list.reducer';
import { SelectableListService } from '../../../shared/object-list/selectable-list/selectable-list.service';
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils';
import { ThemedSearchComponent } from '../../../shared/search/themed-search.component';
import { BrowserOnlyPipe } from '../../../shared/utils/browser-only.pipe';
@Component({ @Component({
selector: 'ds-bulk-access-browse', selector: 'ds-bulk-access-browse',
@@ -22,9 +51,24 @@ import { hasValue } from '../../../shared/empty.util';
providers: [ providers: [
{ {
provide: SEARCH_CONFIG_SERVICE, provide: SEARCH_CONFIG_SERVICE,
useClass: SearchConfigurationService useClass: SearchConfigurationService,
} },
] ],
imports: [
PaginationComponent,
AsyncPipe,
NgbAccordionModule,
TranslateModule,
NgIf,
NgbNavModule,
ThemedSearchComponent,
BrowserOnlyPipe,
NgForOf,
NgxPaginationModule,
SelectableListItemControlComponent,
ListableObjectComponentLoaderComponent,
],
standalone: true,
}) })
export class BulkAccessBrowseComponent implements OnInit, OnDestroy { export class BulkAccessBrowseComponent implements OnInit, OnDestroy {
@@ -49,7 +93,7 @@ export class BulkAccessBrowseComponent implements OnInit, OnDestroy {
paginationOptions$: BehaviorSubject<PaginationComponentOptions> = new BehaviorSubject<PaginationComponentOptions>(Object.assign(new PaginationComponentOptions(), { paginationOptions$: BehaviorSubject<PaginationComponentOptions> = new BehaviorSubject<PaginationComponentOptions>(Object.assign(new PaginationComponentOptions(), {
id: 'bas', id: 'bas',
pageSize: 5, pageSize: 5,
currentPage: 1 currentPage: 1,
})); }));
/** /**
@@ -67,20 +111,20 @@ export class BulkAccessBrowseComponent implements OnInit, OnDestroy {
this.subs.push( this.subs.push(
this.selectableListService.getSelectableList(this.listId).pipe( this.selectableListService.getSelectableList(this.listId).pipe(
distinctUntilChanged(), distinctUntilChanged(),
map((list: SelectableListState) => this.generatePaginatedListBySelectedElements(list)) map((list: SelectableListState) => this.generatePaginatedListBySelectedElements(list)),
).subscribe(this.objectsSelected$) ).subscribe(this.objectsSelected$),
); );
} }
pageNext() { pageNext() {
this.paginationOptions$.next(Object.assign(new PaginationComponentOptions(), this.paginationOptions$.value, { this.paginationOptions$.next(Object.assign(new PaginationComponentOptions(), this.paginationOptions$.value, {
currentPage: this.paginationOptions$.value.currentPage + 1 currentPage: this.paginationOptions$.value.currentPage + 1,
})); }));
} }
pagePrev() { pagePrev() {
this.paginationOptions$.next(Object.assign(new PaginationComponentOptions(), this.paginationOptions$.value, { this.paginationOptions$.next(Object.assign(new PaginationComponentOptions(), this.paginationOptions$.value, {
currentPage: this.paginationOptions$.value.currentPage - 1 currentPage: this.paginationOptions$.value.currentPage - 1,
})); }));
} }
@@ -99,12 +143,12 @@ export class BulkAccessBrowseComponent implements OnInit, OnDestroy {
elementsPerPage: this.paginationOptions$.value.pageSize, elementsPerPage: this.paginationOptions$.value.pageSize,
totalElements: list?.selection.length, totalElements: list?.selection.length,
totalPages: this.calculatePageCount(this.paginationOptions$.value.pageSize, list?.selection.length), totalPages: this.calculatePageCount(this.paginationOptions$.value.pageSize, list?.selection.length),
currentPage: this.paginationOptions$.value.currentPage currentPage: this.paginationOptions$.value.currentPage,
}); });
if (pageInfo.currentPage > pageInfo.totalPages) { if (pageInfo.currentPage > pageInfo.totalPages) {
pageInfo.currentPage = pageInfo.totalPages; pageInfo.currentPage = pageInfo.totalPages;
this.paginationOptions$.next(Object.assign(new PaginationComponentOptions(), this.paginationOptions$.value, { this.paginationOptions$.next(Object.assign(new PaginationComponentOptions(), this.paginationOptions$.value, {
currentPage: pageInfo.currentPage currentPage: pageInfo.currentPage,
})); }));
} }
return createSuccessfulRemoteDataObject(buildPaginatedList(pageInfo, list?.selection || [])); return createSuccessfulRemoteDataObject(buildPaginatedList(pageInfo, list?.selection || []));

View File

@@ -1,4 +1,5 @@
<div class="container"> <div class="container">
<h1>{{ 'admin.access-control.bulk-access.title' | translate }}</h1>
<ds-bulk-access-browse [listId]="listId"></ds-bulk-access-browse> <ds-bulk-access-browse [listId]="listId"></ds-bulk-access-browse>
<div class="clearfix mb-3"></div> <div class="clearfix mb-3"></div>
<ds-bulk-access-settings #dsBulkSettings ></ds-bulk-access-settings> <ds-bulk-access-settings #dsBulkSettings ></ds-bulk-access-settings>

View File

@@ -1,18 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
ComponentFixture,
TestBed,
} from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { BulkAccessComponent } from './bulk-access.component';
import { BulkAccessControlService } from '../../shared/access-control-form-container/bulk-access-control.service';
import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
import { SelectableListState } from '../../shared/object-list/selectable-list/selectable-list.reducer';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { Process } from '../../process-page/processes/process.model'; import { Process } from '../../process-page/processes/process.model';
import { RouterTestingModule } from '@angular/router/testing'; import { BulkAccessControlService } from '../../shared/access-control-form-container/bulk-access-control.service';
import { getMockThemeService } from '../../shared/mocks/theme-service.mock';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { SelectableListState } from '../../shared/object-list/selectable-list/selectable-list.reducer';
import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { ThemeService } from '../../shared/theme-support/theme.service';
import { BulkAccessComponent } from './bulk-access.component';
import { BulkAccessSettingsComponent } from './settings/bulk-access-settings.component';
describe('BulkAccessComponent', () => { describe('BulkAccessComponent', () => {
let component: BulkAccessComponent; let component: BulkAccessComponent;
@@ -31,35 +36,35 @@ describe('BulkAccessComponent', () => {
'startDate': { 'startDate': {
'year': 2026, 'year': 2026,
'month': 5, 'month': 5,
'day': 31 'day': 31,
},
'endDate': null,
}, },
'endDate': null
}
], ],
'state': { 'state': {
'item': { 'item': {
'toggleStatus': true, 'toggleStatus': true,
'accessMode': 'replace' 'accessMode': 'replace',
}, },
'bitstream': { 'bitstream': {
'toggleStatus': false, 'toggleStatus': false,
'accessMode': '', 'accessMode': '',
'changesLimit': '', 'changesLimit': '',
'selectedBitstreams': [] 'selectedBitstreams': [],
} },
} },
}; };
const mockFile = { const mockFile = {
'uuids': [ 'uuids': [
'1234', '5678' '1234', '5678',
], ],
'file': { } 'file': { },
}; };
const mockSettings: any = jasmine.createSpyObj('AccessControlFormContainerComponent', { const mockSettings: any = jasmine.createSpyObj('AccessControlFormContainerComponent', {
getValue: jasmine.createSpy('getValue'), getValue: jasmine.createSpy('getValue'),
reset: jasmine.createSpy('reset') reset: jasmine.createSpy('reset'),
}); });
const selection: any[] = [{ indexableObject: { uuid: '1234' } }, { indexableObject: { uuid: '5678' } }]; const selection: any[] = [{ indexableObject: { uuid: '1234' } }, { indexableObject: { uuid: '5678' } }];
const selectableListState: SelectableListState = { id: 'test', selection }; const selectableListState: SelectableListState = { id: 'test', selection };
@@ -71,15 +76,23 @@ describe('BulkAccessComponent', () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [ imports: [
RouterTestingModule, RouterTestingModule,
TranslateModule.forRoot() TranslateModule.forRoot(),
BulkAccessComponent,
], ],
declarations: [ BulkAccessComponent ],
providers: [ providers: [
{ provide: BulkAccessControlService, useValue: bulkAccessControlServiceMock }, { provide: BulkAccessControlService, useValue: bulkAccessControlServiceMock },
{ provide: NotificationsService, useValue: NotificationsServiceStub }, { provide: NotificationsService, useValue: NotificationsServiceStub },
{ provide: SelectableListService, useValue: selectableListServiceMock } { provide: SelectableListService, useValue: selectableListServiceMock },
{ provide: ThemeService, useValue: getMockThemeService() },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA],
})
.overrideComponent(BulkAccessComponent, {
remove: {
imports: [
BulkAccessSettingsComponent,
],
},
}) })
.compileComponents(); .compileComponents();
}); });

View File

@@ -1,17 +1,34 @@
import { Component, OnInit, ViewChild } from '@angular/core'; import {
Component,
OnInit,
ViewChild,
} from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import {
BehaviorSubject,
Subscription,
} from 'rxjs';
import {
distinctUntilChanged,
map,
} from 'rxjs/operators';
import { BehaviorSubject, Subscription } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { BulkAccessSettingsComponent } from './settings/bulk-access-settings.component';
import { BulkAccessControlService } from '../../shared/access-control-form-container/bulk-access-control.service'; import { BulkAccessControlService } from '../../shared/access-control-form-container/bulk-access-control.service';
import { SelectableListState } from '../../shared/object-list/selectable-list/selectable-list.reducer'; import { SelectableListState } from '../../shared/object-list/selectable-list/selectable-list.reducer';
import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service'; import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
import { BulkAccessBrowseComponent } from './browse/bulk-access-browse.component';
import { BulkAccessSettingsComponent } from './settings/bulk-access-settings.component';
@Component({ @Component({
selector: 'ds-bulk-access', selector: 'ds-bulk-access',
templateUrl: './bulk-access.component.html', templateUrl: './bulk-access.component.html',
styleUrls: ['./bulk-access.component.scss'] styleUrls: ['./bulk-access.component.scss'],
imports: [
TranslateModule,
BulkAccessSettingsComponent,
BulkAccessBrowseComponent,
],
standalone: true,
}) })
export class BulkAccessComponent implements OnInit { export class BulkAccessComponent implements OnInit {
@@ -37,7 +54,7 @@ export class BulkAccessComponent implements OnInit {
constructor( constructor(
private bulkAccessControlService: BulkAccessControlService, private bulkAccessControlService: BulkAccessControlService,
private selectableListService: SelectableListService private selectableListService: SelectableListService,
) { ) {
} }
@@ -45,8 +62,8 @@ export class BulkAccessComponent implements OnInit {
this.subs.push( this.subs.push(
this.selectableListService.getSelectableList(this.listId).pipe( this.selectableListService.getSelectableList(this.listId).pipe(
distinctUntilChanged(), distinctUntilChanged(),
map((list: SelectableListState) => this.generateIdListBySelectedElements(list)) map((list: SelectableListState) => this.generateIdListBySelectedElements(list)),
).subscribe(this.objectsSelected$) ).subscribe(this.objectsSelected$),
); );
} }
@@ -74,12 +91,12 @@ export class BulkAccessComponent implements OnInit {
const { file } = this.bulkAccessControlService.createPayloadFile({ const { file } = this.bulkAccessControlService.createPayloadFile({
bitstreamAccess, bitstreamAccess,
itemAccess, itemAccess,
state: settings.state state: settings.state,
}); });
this.bulkAccessControlService.executeScript( this.bulkAccessControlService.executeScript(
this.objectsSelected$.value || [], this.objectsSelected$.value || [],
file file,
).subscribe(); ).subscribe();
} }

View File

@@ -1,13 +1,13 @@
<ngb-accordion #acc="ngbAccordion" [activeIds]="'settings'"> <ngb-accordion #acc="ngbAccordion" [activeIds]="'settings'">
<ngb-panel [id]="'settings'"> <ngb-panel [id]="'settings'">
<ng-template ngbPanelHeader> <ng-template ngbPanelHeader>
<div class="w-100 d-flex justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('settings')" data-test="settings"> <div class="w-100 d-flex gap-3 justify-content-between collapse-toggle" ngbPanelToggle (click)="acc.toggle('settings')" data-test="settings">
<button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="!acc.isExpanded('browse')" <button type="button" class="btn btn-link p-0" (click)="$event.preventDefault()" [attr.aria-expanded]="acc.isExpanded('settings')"
aria-controls="collapsePanels"> aria-controls="bulk-access-settings-panel-content">
{{ 'admin.access-control.bulk-access-settings.header' | translate }} {{ 'admin.access-control.bulk-access-settings.header' | translate }}
</button> </button>
<div class="text-right d-flex"> <div class="text-right d-flex gap-2">
<div class="ml-3 d-inline-block"> <div class="d-flex my-auto">
<span *ngIf="acc.isExpanded('settings')" class="fas fa-chevron-up fa-fw"></span> <span *ngIf="acc.isExpanded('settings')" class="fas fa-chevron-up fa-fw"></span>
<span *ngIf="!acc.isExpanded('settings')" class="fas fa-chevron-down fa-fw"></span> <span *ngIf="!acc.isExpanded('settings')" class="fas fa-chevron-down fa-fw"></span>
</div> </div>
@@ -15,7 +15,7 @@
</div> </div>
</ng-template> </ng-template>
<ng-template ngbPanelContent> <ng-template ngbPanelContent>
<ds-access-control-form-container #dsAccessControlForm [showSubmit]="false"></ds-access-control-form-container> <ds-access-control-form-container id="bulk-access-settings-panel-content" #dsAccessControlForm [showSubmit]="false"></ds-access-control-form-container>
</ng-template> </ng-template>
</ngb-panel> </ngb-panel>
</ngb-accordion> </ngb-accordion>

View File

@@ -1,8 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
ComponentFixture,
TestBed,
} from '@angular/core/testing';
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { AccessControlFormContainerComponent } from '../../../shared/access-control-form-container/access-control-form-container.component';
import { BulkAccessSettingsComponent } from './bulk-access-settings.component'; import { BulkAccessSettingsComponent } from './bulk-access-settings.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
describe('BulkAccessSettingsComponent', () => { describe('BulkAccessSettingsComponent', () => {
let component: BulkAccessSettingsComponent; let component: BulkAccessSettingsComponent;
@@ -15,36 +20,39 @@ describe('BulkAccessSettingsComponent', () => {
'startDate': { 'startDate': {
'year': 2026, 'year': 2026,
'month': 5, 'month': 5,
'day': 31 'day': 31,
},
'endDate': null,
}, },
'endDate': null
}
], ],
'state': { 'state': {
'item': { 'item': {
'toggleStatus': true, 'toggleStatus': true,
'accessMode': 'replace' 'accessMode': 'replace',
}, },
'bitstream': { 'bitstream': {
'toggleStatus': false, 'toggleStatus': false,
'accessMode': '', 'accessMode': '',
'changesLimit': '', 'changesLimit': '',
'selectedBitstreams': [] 'selectedBitstreams': [],
} },
} },
}; };
const mockControl: any = jasmine.createSpyObj('AccessControlFormContainerComponent', { const mockControl: any = jasmine.createSpyObj('AccessControlFormContainerComponent', {
getFormValue: jasmine.createSpy('getFormValue'), getFormValue: jasmine.createSpy('getFormValue'),
reset: jasmine.createSpy('reset') reset: jasmine.createSpy('reset'),
}); });
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [NgbAccordionModule, TranslateModule.forRoot()], imports: [NgbAccordionModule, TranslateModule.forRoot(), BulkAccessSettingsComponent],
declarations: [BulkAccessSettingsComponent], schemas: [NO_ERRORS_SCHEMA],
schemas: [NO_ERRORS_SCHEMA] })
}).compileComponents(); .overrideComponent(BulkAccessSettingsComponent, {
remove: { imports: [AccessControlFormContainerComponent] },
})
.compileComponents();
}); });
beforeEach(() => { beforeEach(() => {

View File

@@ -1,13 +1,25 @@
import { Component, ViewChild } from '@angular/core'; import { NgIf } from '@angular/common';
import { import {
AccessControlFormContainerComponent Component,
} from '../../../shared/access-control-form-container/access-control-form-container.component'; ViewChild,
} from '@angular/core';
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { AccessControlFormContainerComponent } from '../../../shared/access-control-form-container/access-control-form-container.component';
@Component({ @Component({
selector: 'ds-bulk-access-settings', selector: 'ds-bulk-access-settings',
templateUrl: 'bulk-access-settings.component.html', templateUrl: 'bulk-access-settings.component.html',
styleUrls: ['./bulk-access-settings.component.scss'], styleUrls: ['./bulk-access-settings.component.scss'],
exportAs: 'dsBulkSettings' exportAs: 'dsBulkSettings',
imports: [
NgbAccordionModule,
TranslateModule,
NgIf,
AccessControlFormContainerComponent,
],
standalone: true,
}) })
export class BulkAccessSettingsComponent { export class BulkAccessSettingsComponent {

View File

@@ -1,5 +1,6 @@
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPerson } from '../../core/eperson/models/eperson.model';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';

View File

@@ -2,24 +2,20 @@
<div class="epeople-registry row"> <div class="epeople-registry row">
<div class="col-12"> <div class="col-12">
<div class="d-flex justify-content-between border-bottom mb-3"> <div class="d-flex justify-content-between border-bottom mb-3">
<h2 id="header" class="pb-2">{{labelPrefix + 'head' | translate}}</h2> <h1 id="header" class="pb-2">{{labelPrefix + 'head' | translate}}</h1>
<div *ngIf="!isEPersonFormShown"> <div>
<button class="mr-auto btn btn-success addEPerson-button" <button class="mr-auto btn btn-success addEPerson-button"
(click)="isEPersonFormShown = true"> [routerLink]="'create'">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
<span class="d-none d-sm-inline ml-1">{{labelPrefix + 'button.add' | translate}}</span> <span class="d-none d-sm-inline ml-1">{{labelPrefix + 'button.add' | translate}}</span>
</button> </button>
</div> </div>
</div> </div>
<ds-eperson-form *ngIf="isEPersonFormShown" (submitForm)="reset()" <h2 id="search" class="border-bottom pb-2">
(cancelForm)="isEPersonFormShown = false"></ds-eperson-form> {{labelPrefix + 'search.head' | translate}}
</h2>
<div *ngIf="!isEPersonFormShown">
<h3 id="search" class="border-bottom pb-2">{{labelPrefix + 'search.head' | translate}}
</h3>
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between"> <form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
<div> <div>
<select name="scope" id="scope" formControlName="scope" class="form-control" aria-label="Search scope"> <select name="scope" id="scope" formControlName="scope" class="form-control" aria-label="Search scope">
@@ -47,7 +43,7 @@
<ds-themed-loading *ngIf="searching$ | async"></ds-themed-loading> <ds-themed-loading *ngIf="searching$ | async"></ds-themed-loading>
<ds-pagination <ds-pagination
*ngIf="(pageInfoState$ | async)?.totalElements > 0 && !(searching$ | async)" *ngIf="(pageInfoState$ | async)?.totalElements > 0 && (searching$ | async) !== true"
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="pageInfoState$" [pageInfoState]="pageInfoState$"
[collectionSize]="(pageInfoState$ | async)?.totalElements" [collectionSize]="(pageInfoState$ | async)?.totalElements"
@@ -72,12 +68,12 @@
<td>{{epersonDto.eperson.email}}</td> <td>{{epersonDto.eperson.email}}</td>
<td> <td>
<div class="btn-group edit-field"> <div class="btn-group edit-field">
<button (click)="toggleEditEPerson(epersonDto.eperson)" <button [routerLink]="getEditEPeoplePage(epersonDto.eperson.id)"
class="btn btn-outline-primary btn-sm access-control-editEPersonButton" class="btn btn-outline-primary btn-sm access-control-editEPersonButton"
title="{{labelPrefix + 'table.edit.buttons.edit' | translate: { name: dsoNameService.getName(epersonDto.eperson) } }}"> title="{{labelPrefix + 'table.edit.buttons.edit' | translate: { name: dsoNameService.getName(epersonDto.eperson) } }}">
<i class="fas fa-edit fa-fw"></i> <i class="fas fa-edit fa-fw"></i>
</button> </button>
<button [disabled]="!epersonDto.ableToDelete" (click)="deleteEPerson(epersonDto.eperson)" <button *ngIf="epersonDto.ableToDelete" (click)="deleteEPerson(epersonDto.eperson)"
class="delete-button btn btn-outline-danger btn-sm access-control-deleteEPersonButton" class="delete-button btn btn-outline-danger btn-sm access-control-deleteEPersonButton"
title="{{labelPrefix + 'table.edit.buttons.remove' | translate: { name: dsoNameService.getName(epersonDto.eperson) } }}"> title="{{labelPrefix + 'table.edit.buttons.remove' | translate: { name: dsoNameService.getName(epersonDto.eperson) } }}">
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
@@ -91,10 +87,9 @@
</ds-pagination> </ds-pagination>
<div *ngIf="(pageInfoState$ | async)?.totalElements == 0" class="alert alert-info w-100 mb-2" role="alert"> <div *ngIf="(pageInfoState$ | async)?.totalElements === 0" class="alert alert-info w-100 mb-2" role="alert">
{{labelPrefix + 'no-items' | translate}} {{labelPrefix + 'no-items' | translate}}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>

View File

@@ -1,47 +1,75 @@
import { Router } from '@angular/router';
import { Observable, of as observableOf } from 'rxjs';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import {
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; DebugElement,
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; NO_ERRORS_SCHEMA,
import { BrowserModule, By } from '@angular/platform-browser'; } from '@angular/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import {
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; ComponentFixture,
import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; fakeAsync,
TestBed,
tick,
waitForAsync,
} from '@angular/core/testing';
import {
FormsModule,
ReactiveFormsModule,
} from '@angular/forms';
import {
BrowserModule,
By,
} from '@angular/platform-browser';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import {
NgbModal,
NgbModule,
} from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { FindListOptions } from '../../core/data/find-list-options.model';
import {
buildPaginatedList,
PaginatedList,
} from '../../core/data/paginated-list.model';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { RequestService } from '../../core/data/request.service';
import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPerson } from '../../core/eperson/models/eperson.model';
import { PaginationService } from '../../core/pagination/pagination.service';
import { PageInfo } from '../../core/shared/page-info.model'; import { PageInfo } from '../../core/shared/page-info.model';
import { FormBuilderService } from '../../shared/form/builder/form-builder.service'; import { FormBuilderService } from '../../shared/form/builder/form-builder.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component';
import { EPeopleRegistryComponent } from './epeople-registry.component';
import { EPersonMock, EPersonMock2 } from '../../shared/testing/eperson.mock';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { getMockFormBuilderService } from '../../shared/mocks/form-builder-service.mock'; import { getMockFormBuilderService } from '../../shared/mocks/form-builder-service.mock';
import { getMockTranslateService } from '../../shared/mocks/translate.service.mock'; import { RouterMock } from '../../shared/mocks/router.mock';
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { PaginationComponent } from '../../shared/pagination/pagination.component';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import {
EPersonMock,
EPersonMock2,
} from '../../shared/testing/eperson.mock';
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { RouterStub } from '../../shared/testing/router.stub';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { RequestService } from '../../core/data/request.service';
import { PaginationService } from '../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
import { FindListOptions } from '../../core/data/find-list-options.model'; import { EPeopleRegistryComponent } from './epeople-registry.component';
import { EPersonFormComponent } from './eperson-form/eperson-form.component';
describe('EPeopleRegistryComponent', () => { describe('EPeopleRegistryComponent', () => {
let component: EPeopleRegistryComponent; let component: EPeopleRegistryComponent;
let fixture: ComponentFixture<EPeopleRegistryComponent>; let fixture: ComponentFixture<EPeopleRegistryComponent>;
let translateService: TranslateService;
let builderService: FormBuilderService; let builderService: FormBuilderService;
let mockEPeople; let mockEPeople: EPerson[];
let ePersonDataServiceStub: any; let ePersonDataServiceStub: any;
let authorizationService: AuthorizationDataService; let authorizationService: AuthorizationDataService;
let modalService; let modalService: NgbModal;
let paginationService: PaginationServiceStub;
let paginationService; beforeEach(waitForAsync(async () => {
beforeEach(waitForAsync(() => {
jasmine.getEnv().allowRespy(true); jasmine.getEnv().allowRespy(true);
mockEPeople = [EPersonMock, EPersonMock2]; mockEPeople = [EPersonMock, EPersonMock2];
ePersonDataServiceStub = { ePersonDataServiceStub = {
@@ -52,7 +80,7 @@ describe('EPeopleRegistryComponent', () => {
elementsPerPage: this.allEpeople.length, elementsPerPage: this.allEpeople.length,
totalElements: this.allEpeople.length, totalElements: this.allEpeople.length,
totalPages: 1, totalPages: 1,
currentPage: 1 currentPage: 1,
}), this.allEpeople)); }), this.allEpeople));
}, },
getActiveEPerson(): Observable<EPerson> { getActiveEPerson(): Observable<EPerson> {
@@ -67,7 +95,7 @@ describe('EPeopleRegistryComponent', () => {
elementsPerPage: [result].length, elementsPerPage: [result].length,
totalElements: [result].length, totalElements: [result].length,
totalPages: 1, totalPages: 1,
currentPage: 1 currentPage: 1,
}), [result])); }), [result]));
} }
if (scope === 'metadata') { if (scope === 'metadata') {
@@ -76,7 +104,7 @@ describe('EPeopleRegistryComponent', () => {
elementsPerPage: this.allEpeople.length, elementsPerPage: this.allEpeople.length,
totalElements: this.allEpeople.length, totalElements: this.allEpeople.length,
totalPages: 1, totalPages: 1,
currentPage: 1 currentPage: 1,
}), this.allEpeople)); }), this.allEpeople));
} }
const result = this.allEpeople.find((ePerson: EPerson) => { const result = this.allEpeople.find((ePerson: EPerson) => {
@@ -86,14 +114,14 @@ describe('EPeopleRegistryComponent', () => {
elementsPerPage: [result].length, elementsPerPage: [result].length,
totalElements: [result].length, totalElements: [result].length,
totalPages: 1, totalPages: 1,
currentPage: 1 currentPage: 1,
}), [result])); }), [result]));
} }
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo({ return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo({
elementsPerPage: this.allEpeople.length, elementsPerPage: this.allEpeople.length,
totalElements: this.allEpeople.length, totalElements: this.allEpeople.length,
totalPages: 1, totalPages: 1,
currentPage: 1 currentPage: 1,
}), this.allEpeople)); }), this.allEpeople));
}, },
deleteEPerson(ePerson: EPerson): Observable<boolean> { deleteEPerson(ePerson: EPerson): Observable<boolean> {
@@ -113,42 +141,44 @@ describe('EPeopleRegistryComponent', () => {
}, },
getEPeoplePageRouterLink(): string { getEPeoplePageRouterLink(): string {
return '/access-control/epeople'; return '/access-control/epeople';
} },
}; };
authorizationService = jasmine.createSpyObj('authorizationService', { authorizationService = jasmine.createSpyObj('authorizationService', {
isAuthorized: observableOf(true) isAuthorized: observableOf(true),
}); });
builderService = getMockFormBuilderService(); builderService = getMockFormBuilderService();
translateService = getMockTranslateService();
paginationService = new PaginationServiceStub(); paginationService = new PaginationServiceStub();
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, RouterTestingModule.withRoutes([]),
TranslateModule.forRoot({ TranslateModule.forRoot(), EPeopleRegistryComponent],
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
}),
],
declarations: [EPeopleRegistryComponent],
providers: [ providers: [
{ provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: EPersonDataService, useValue: ePersonDataServiceStub },
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: AuthorizationDataService, useValue: authorizationService }, { provide: AuthorizationDataService, useValue: authorizationService },
{ provide: FormBuilderService, useValue: builderService }, { provide: FormBuilderService, useValue: builderService },
{ provide: Router, useValue: new RouterStub() }, { provide: Router, useValue: new RouterMock() },
{ provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) }, { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) },
{ provide: PaginationService, useValue: paginationService } { provide: PaginationService, useValue: paginationService },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA],
}).compileComponents(); })
.overrideComponent(EPeopleRegistryComponent, {
remove: {
imports: [
EPersonFormComponent,
ThemedLoadingComponent,
PaginationComponent,
],
},
})
.compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(EPeopleRegistryComponent); fixture = TestBed.createComponent(EPeopleRegistryComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
modalService = (component as any).modalService; modalService = TestBed.inject(NgbModal);
spyOn(modalService, 'open').and.returnValue(Object.assign({ componentInstance: Object.assign({ response: observableOf(true) }) })); spyOn(modalService, 'open').and.returnValue(Object.assign({ componentInstance: Object.assign({ response: observableOf(true) }) }));
fixture.detectChanges(); fixture.detectChanges();
}); });
@@ -158,10 +188,10 @@ describe('EPeopleRegistryComponent', () => {
}); });
it('should display list of ePeople', () => { it('should display list of ePeople', () => {
const ePeopleIdsFound = fixture.debugElement.queryAll(By.css('#epeople tr td:first-child')); const ePeopleIdsFound: DebugElement[] = fixture.debugElement.queryAll(By.css('#epeople tr td:first-child'));
expect(ePeopleIdsFound.length).toEqual(2); expect(ePeopleIdsFound.length).toEqual(2);
mockEPeople.map((ePerson: EPerson) => { mockEPeople.map((ePerson: EPerson) => {
expect(ePeopleIdsFound.find((foundEl) => { expect(ePeopleIdsFound.find((foundEl: DebugElement) => {
return (foundEl.nativeElement.textContent.trim() === ePerson.uuid); return (foundEl.nativeElement.textContent.trim() === ePerson.uuid);
})).toBeTruthy(); })).toBeTruthy();
}); });
@@ -169,7 +199,7 @@ describe('EPeopleRegistryComponent', () => {
describe('search', () => { describe('search', () => {
describe('when searching with scope/query (scope metadata)', () => { describe('when searching with scope/query (scope metadata)', () => {
let ePeopleIdsFound; let ePeopleIdsFound: DebugElement[];
beforeEach(fakeAsync(() => { beforeEach(fakeAsync(() => {
component.search({ scope: 'metadata', query: EPersonMock2.name }); component.search({ scope: 'metadata', query: EPersonMock2.name });
tick(); tick();
@@ -179,14 +209,14 @@ describe('EPeopleRegistryComponent', () => {
it('should display search result', () => { it('should display search result', () => {
expect(ePeopleIdsFound.length).toEqual(1); expect(ePeopleIdsFound.length).toEqual(1);
expect(ePeopleIdsFound.find((foundEl) => { expect(ePeopleIdsFound.find((foundEl: DebugElement) => {
return (foundEl.nativeElement.textContent.trim() === EPersonMock2.uuid); return (foundEl.nativeElement.textContent.trim() === EPersonMock2.uuid);
})).toBeTruthy(); })).toBeTruthy();
}); });
}); });
describe('when searching with scope/query (scope email)', () => { describe('when searching with scope/query (scope email)', () => {
let ePeopleIdsFound; let ePeopleIdsFound: DebugElement[];
beforeEach(fakeAsync(() => { beforeEach(fakeAsync(() => {
component.search({ scope: 'email', query: EPersonMock.email }); component.search({ scope: 'email', query: EPersonMock.email });
tick(); tick();
@@ -196,43 +226,13 @@ describe('EPeopleRegistryComponent', () => {
it('should display search result', () => { it('should display search result', () => {
expect(ePeopleIdsFound.length).toEqual(1); expect(ePeopleIdsFound.length).toEqual(1);
expect(ePeopleIdsFound.find((foundEl) => { expect(ePeopleIdsFound.find((foundEl: DebugElement) => {
return (foundEl.nativeElement.textContent.trim() === EPersonMock.uuid); return (foundEl.nativeElement.textContent.trim() === EPersonMock.uuid);
})).toBeTruthy(); })).toBeTruthy();
}); });
}); });
}); });
describe('toggleEditEPerson', () => {
describe('when you click on first edit eperson button', () => {
beforeEach(fakeAsync(() => {
const editButtons = fixture.debugElement.queryAll(By.css('.access-control-editEPersonButton'));
editButtons[0].triggerEventHandler('click', {
preventDefault: () => {/**/
}
});
tick();
fixture.detectChanges();
}));
it('editEPerson form is toggled', () => {
const ePeopleIds = fixture.debugElement.queryAll(By.css('#epeople tr td:first-child'));
ePersonDataServiceStub.getActiveEPerson().subscribe((activeEPerson: EPerson) => {
if (ePeopleIds[0] && activeEPerson === ePeopleIds[0].nativeElement.textContent) {
expect(component.isEPersonFormShown).toEqual(false);
} else {
expect(component.isEPersonFormShown).toEqual(true);
}
});
});
it('EPerson search section is hidden', () => {
expect(fixture.debugElement.query(By.css('#search'))).toBeNull();
});
});
});
describe('deleteEPerson', () => { describe('deleteEPerson', () => {
describe('when you click on first delete eperson button', () => { describe('when you click on first delete eperson button', () => {
let ePeopleIdsFoundBeforeDelete; let ePeopleIdsFoundBeforeDelete;
@@ -242,7 +242,7 @@ describe('EPeopleRegistryComponent', () => {
const deleteButtons = fixture.debugElement.queryAll(By.css('.access-control-deleteEPersonButton')); const deleteButtons = fixture.debugElement.queryAll(By.css('.access-control-deleteEPersonButton'));
deleteButtons[0].triggerEventHandler('click', { deleteButtons[0].triggerEventHandler('click', {
preventDefault: () => {/**/ preventDefault: () => {/**/
} },
}); });
tick(); tick();
fixture.detectChanges(); fixture.detectChanges();
@@ -258,19 +258,12 @@ describe('EPeopleRegistryComponent', () => {
}); });
}); });
describe('delete EPerson button when the isAuthorized returns false', () => {
let ePeopleDeleteButton; it('should hide delete EPerson button when the isAuthorized returns false', () => {
beforeEach(() => {
spyOn(authorizationService, 'isAuthorized').and.returnValue(observableOf(false)); spyOn(authorizationService, 'isAuthorized').and.returnValue(observableOf(false));
component.initialisePage(); component.initialisePage();
fixture.detectChanges(); fixture.detectChanges();
});
it('should be disabled', () => { expect(fixture.debugElement.query(By.css('#epeople tr td div button.delete-button'))).toBeNull();
ePeopleDeleteButton = fixture.debugElement.queryAll(By.css('#epeople tr td div button.delete-button'));
ePeopleDeleteButton.forEach((deleteButton: DebugElement) => {
expect(deleteButton.nativeElement.disabled).toBe(true);
});
});
}); });
}); });

View File

@@ -1,31 +1,86 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import {
import { UntypedFormBuilder } from '@angular/forms'; AsyncPipe,
import { Router } from '@angular/router'; NgClass,
import { TranslateService } from '@ngx-translate/core'; NgForOf,
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs'; NgIf,
import { map, switchMap, take } from 'rxjs/operators'; } from '@angular/common';
import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; import {
Component,
OnDestroy,
OnInit,
} from '@angular/core';
import {
ReactiveFormsModule,
UntypedFormBuilder,
} from '@angular/forms';
import {
Router,
RouterModule,
} from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import {
BehaviorSubject,
combineLatest,
Observable,
Subscription,
} from 'rxjs';
import {
map,
switchMap,
take,
} from 'rxjs/operators';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import {
buildPaginatedList,
PaginatedList,
} from '../../core/data/paginated-list.model';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { RequestService } from '../../core/data/request.service';
import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPerson } from '../../core/eperson/models/eperson.model';
import { hasValue } from '../../shared/empty.util';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { EpersonDtoModel } from '../../core/eperson/models/eperson-dto.model'; import { EpersonDtoModel } from '../../core/eperson/models/eperson-dto.model';
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { getAllSucceededRemoteData, getFirstCompletedRemoteData } from '../../core/shared/operators';
import { ConfirmationModalComponent } from '../../shared/confirmation-modal/confirmation-modal.component';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { RequestService } from '../../core/data/request.service';
import { PageInfo } from '../../core/shared/page-info.model';
import { NoContent } from '../../core/shared/NoContent.model';
import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationService } from '../../core/pagination/pagination.service';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { NoContent } from '../../core/shared/NoContent.model';
import {
getAllSucceededRemoteData,
getFirstCompletedRemoteData,
} from '../../core/shared/operators';
import { PageInfo } from '../../core/shared/page-info.model';
import { ConfirmationModalComponent } from '../../shared/confirmation-modal/confirmation-modal.component';
import { hasValue } from '../../shared/empty.util';
import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { PaginationComponent } from '../../shared/pagination/pagination.component';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import {
getEPersonEditRoute,
getEPersonsRoute,
} from '../access-control-routing-paths';
import { EPersonFormComponent } from './eperson-form/eperson-form.component';
@Component({ @Component({
selector: 'ds-epeople-registry', selector: 'ds-epeople-registry',
templateUrl: './epeople-registry.component.html', templateUrl: './epeople-registry.component.html',
imports: [
TranslateModule,
RouterModule,
AsyncPipe,
NgIf,
EPersonFormComponent,
ReactiveFormsModule,
ThemedLoadingComponent,
PaginationComponent,
NgClass,
NgForOf,
],
standalone: true,
}) })
/** /**
* A component used for managing all existing epeople within the repository. * A component used for managing all existing epeople within the repository.
@@ -61,14 +116,9 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
id: 'elp', id: 'elp',
pageSize: 5, pageSize: 5,
currentPage: 1 currentPage: 1,
}); });
/**
* Whether or not to show the EPerson form
*/
isEPersonFormShown: boolean;
// The search form // The search form
searchForm; searchForm;
@@ -114,26 +164,20 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
*/ */
initialisePage() { initialisePage() {
this.searching$.next(true); this.searching$.next(true);
this.isEPersonFormShown = false;
this.search({ scope: this.currentSearchScope, query: this.currentSearchQuery }); this.search({ scope: this.currentSearchScope, query: this.currentSearchQuery });
this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => {
if (eperson != null && eperson.id) {
this.isEPersonFormShown = true;
}
}));
this.subs.push(this.ePeople$.pipe( this.subs.push(this.ePeople$.pipe(
switchMap((epeople: PaginatedList<EPerson>) => { switchMap((epeople: PaginatedList<EPerson>) => {
if (epeople.pageInfo.totalElements > 0) { if (epeople.pageInfo.totalElements > 0) {
return combineLatest([...epeople.page.map((eperson: EPerson) => { return combineLatest(epeople.page.map((eperson: EPerson) => {
return this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined).pipe( return this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined).pipe(
map((authorized) => { map((authorized) => {
const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel(); const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel();
epersonDtoModel.ableToDelete = authorized; epersonDtoModel.ableToDelete = authorized;
epersonDtoModel.eperson = eperson; epersonDtoModel.eperson = eperson;
return epersonDtoModel; return epersonDtoModel;
}) }),
); );
})]).pipe(map((dtos: EpersonDtoModel[]) => { })).pipe(map((dtos: EpersonDtoModel[]) => {
return buildPaginatedList(epeople.pageInfo, dtos); return buildPaginatedList(epeople.pageInfo, dtos);
})); }));
} else { } else {
@@ -160,15 +204,15 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
const query: string = data.query; const query: string = data.query;
const scope: string = data.scope; const scope: string = data.scope;
if (query != null && this.currentSearchQuery !== query) { if (query != null && this.currentSearchQuery !== query) {
this.router.navigate([this.epersonService.getEPeoplePageRouterLink()], { void this.router.navigate([getEPersonsRoute()], {
queryParamsHandling: 'merge' queryParamsHandling: 'merge',
}); });
this.currentSearchQuery = query; this.currentSearchQuery = query;
this.paginationService.resetPage(this.config.id); this.paginationService.resetPage(this.config.id);
} }
if (scope != null && this.currentSearchScope !== scope) { if (scope != null && this.currentSearchScope !== scope) {
this.router.navigate([this.epersonService.getEPeoplePageRouterLink()], { void this.router.navigate([getEPersonsRoute()], {
queryParamsHandling: 'merge' queryParamsHandling: 'merge',
}); });
this.currentSearchScope = scope; this.currentSearchScope = scope;
this.paginationService.resetPage(this.config.id); this.paginationService.resetPage(this.config.id);
@@ -176,15 +220,15 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
} }
return this.epersonService.searchByScope(this.currentSearchScope, this.currentSearchQuery, { return this.epersonService.searchByScope(this.currentSearchScope, this.currentSearchQuery, {
currentPage: findListOptions.currentPage, currentPage: findListOptions.currentPage,
elementsPerPage: findListOptions.pageSize elementsPerPage: findListOptions.pageSize,
}); });
} },
), ),
getAllSucceededRemoteData(), getAllSucceededRemoteData(),
).subscribe((peopleRD) => { ).subscribe((peopleRD) => {
this.ePeople$.next(peopleRD.payload); this.ePeople$.next(peopleRD.payload);
this.pageInfoState$.next(peopleRD.payload.pageInfo); this.pageInfoState$.next(peopleRD.payload.pageInfo);
} },
); );
} }
@@ -194,7 +238,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
*/ */
isActive(eperson: EPerson): Observable<boolean> { isActive(eperson: EPerson): Observable<boolean> {
return this.getActiveEPerson().pipe( return this.getActiveEPerson().pipe(
map((activeEPerson) => eperson === activeEPerson) map((activeEPerson) => eperson === activeEPerson),
); );
} }
@@ -205,30 +249,13 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
return this.epersonService.getActiveEPerson(); return this.epersonService.getActiveEPerson();
} }
/**
* Start editing the selected EPerson
* @param ePerson
*/
toggleEditEPerson(ePerson: EPerson) {
this.getActiveEPerson().pipe(take(1)).subscribe((activeEPerson: EPerson) => {
if (ePerson === activeEPerson) {
this.epersonService.cancelEditEPerson();
this.isEPersonFormShown = false;
} else {
this.epersonService.editEPerson(ePerson);
this.isEPersonFormShown = true;
}
});
this.scrollToTop();
}
/** /**
* Deletes EPerson, show notification on success/failure & updates EPeople list * Deletes EPerson, show notification on success/failure & updates EPeople list
*/ */
deleteEPerson(ePerson: EPerson) { deleteEPerson(ePerson: EPerson) {
if (hasValue(ePerson.id)) { if (hasValue(ePerson.id)) {
const modalRef = this.modalService.open(ConfirmationModalComponent); const modalRef = this.modalService.open(ConfirmationModalComponent);
modalRef.componentInstance.dso = ePerson; modalRef.componentInstance.name = this.dsoNameService.getName(ePerson);
modalRef.componentInstance.headerLabel = 'confirmation-modal.delete-eperson.header'; modalRef.componentInstance.headerLabel = 'confirmation-modal.delete-eperson.header';
modalRef.componentInstance.infoLabel = 'confirmation-modal.delete-eperson.info'; modalRef.componentInstance.infoLabel = 'confirmation-modal.delete-eperson.info';
modalRef.componentInstance.cancelLabel = 'confirmation-modal.delete-eperson.cancel'; modalRef.componentInstance.cancelLabel = 'confirmation-modal.delete-eperson.cancel';
@@ -242,7 +269,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
if (restResponse.hasSucceeded) { if (restResponse.hasSucceeded) {
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { name: this.dsoNameService.getName(ePerson) })); this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { name: this.dsoNameService.getName(ePerson) }));
} else { } else {
this.notificationsService.error('Error occured when trying to delete EPerson with id: ' + ePerson.id + ' with code: ' + restResponse.statusCode + ' and message: ' + restResponse.errorMessage); this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { id: ePerson.id, statusCode: restResponse.statusCode, errorMessage: restResponse.errorMessage }));
} }
}); });
} }
@@ -264,16 +291,6 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
} }
scrollToTop() {
(function smoothscroll() {
const currentScroll = document.documentElement.scrollTop || document.body.scrollTop;
if (currentScroll > 0) {
window.requestAnimationFrame(smoothscroll);
window.scrollTo(0, currentScroll - (currentScroll / 8));
}
})();
}
/** /**
* Reset all input-fields to be empty and search all search * Reset all input-fields to be empty and search all search
*/ */
@@ -284,20 +301,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
this.search({ query: '' }); this.search({ query: '' });
} }
/** getEditEPeoplePage(id: string): string {
* This method will set everything to stale, which will cause the lists on this page to update. return getEPersonEditRoute(id);
*/
reset(): void {
this.epersonService.getBrowseEndpoint().pipe(
take(1),
switchMap((href: string) => {
return this.requestService.setStaleByHrefSubstring(href).pipe(
take(1),
);
})
).subscribe(()=>{
this.epersonService.cancelEditEPerson();
this.isEPersonFormShown = false;
});
} }
} }

View File

@@ -1,6 +1,12 @@
import { EPeopleRegistryCancelEPersonAction, EPeopleRegistryEditEPersonAction } from './epeople-registry.actions';
import { ePeopleRegistryReducer, EPeopleRegistryState } from './epeople-registry.reducers';
import { EPersonMock } from '../../shared/testing/eperson.mock'; import { EPersonMock } from '../../shared/testing/eperson.mock';
import {
EPeopleRegistryCancelEPersonAction,
EPeopleRegistryEditEPersonAction,
} from './epeople-registry.actions';
import {
ePeopleRegistryReducer,
EPeopleRegistryState,
} from './epeople-registry.reducers';
const initialState: EPeopleRegistryState = { const initialState: EPeopleRegistryState = {
editEPerson: null, editEPerson: null,

View File

@@ -2,7 +2,7 @@ import { EPerson } from '../../core/eperson/models/eperson.model';
import { import {
EPeopleRegistryAction, EPeopleRegistryAction,
EPeopleRegistryActionTypes, EPeopleRegistryActionTypes,
EPeopleRegistryEditEPersonAction EPeopleRegistryEditEPersonAction,
} from './epeople-registry.actions'; } from './epeople-registry.actions';
/** /**
@@ -30,13 +30,13 @@ export function ePeopleRegistryReducer(state = initialState, action: EPeopleRegi
case EPeopleRegistryActionTypes.EDIT_EPERSON: { case EPeopleRegistryActionTypes.EDIT_EPERSON: {
return Object.assign({}, state, { return Object.assign({}, state, {
editEPerson: (action as EPeopleRegistryEditEPersonAction).eperson editEPerson: (action as EPeopleRegistryEditEPersonAction).eperson,
}); });
} }
case EPeopleRegistryActionTypes.CANCEL_EDIT_EPERSON: { case EPeopleRegistryActionTypes.CANCEL_EDIT_EPERSON: {
return Object.assign({}, state, { return Object.assign({}, state, {
editEPerson: null editEPerson: null,
}); });
} }

View File

@@ -1,11 +1,15 @@
<div *ngIf="epersonService.getActiveEPerson() | async; then editheader; else createHeader"></div> <div class="container">
<div class="group-form row">
<div class="col-12">
<div *ngIf="epersonService.getActiveEPerson() | async; then editHeader; else createHeader"></div>
<ng-template #createHeader> <ng-template #createHeader>
<h4>{{messagePrefix + '.create' | translate}}</h4> <h1 class="border-bottom pb-2">{{messagePrefix + '.create' | translate}}</h1>
</ng-template> </ng-template>
<ng-template #editheader> <ng-template #editHeader>
<h4>{{messagePrefix + '.edit' | translate}}</h4> <h1 class="border-bottom pb-2">{{messagePrefix + '.edit' | translate}}</h1>
</ng-template> </ng-template>
<ds-form [formId]="formId" <ds-form [formId]="formId"
@@ -16,23 +20,24 @@
[submitLabel]="submitLabel" [submitLabel]="submitLabel"
(submitForm)="onSubmit()"> (submitForm)="onSubmit()">
<div before class="btn-group"> <div before class="btn-group">
<button (click)="onCancel()" <button (click)="onCancel()" type="button" class="btn btn-outline-secondary">
class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}</button> <i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}
</button>
</div> </div>
<div *ngIf="displayResetPassword" between class="btn-group"> <div *ngIf="displayResetPassword" between class="btn-group">
<button class="btn btn-primary" [disabled]="!(canReset$ | async)" (click)="resetPassword()"> <button class="btn btn-primary" [disabled]="(canReset$ | async) !== true" type="button" (click)="resetPassword()">
<i class="fa fa-key"></i> {{'admin.access-control.epeople.actions.reset' | translate}} <i class="fa fa-key"></i> {{'admin.access-control.epeople.actions.reset' | translate}}
</button> </button>
</div> </div>
<div between class="btn-group ml-1"> <div *ngIf="canImpersonate$ | async" between class="btn-group">
<button *ngIf="!isImpersonated" class="btn btn-primary" [ngClass]="{'d-none' : !(canImpersonate$ | async)}" (click)="impersonate()"> <button *ngIf="!isImpersonated" class="btn btn-primary" type="button" (click)="impersonate()">
<i class="fa fa-user-secret"></i> {{'admin.access-control.epeople.actions.impersonate' | translate}} <i class="fa fa-user-secret"></i> {{'admin.access-control.epeople.actions.impersonate' | translate}}
</button> </button>
<button *ngIf="isImpersonated" class="btn btn-primary" (click)="stopImpersonating()"> <button *ngIf="isImpersonated" class="btn btn-primary" type="button" (click)="stopImpersonating()">
<i class="fa fa-user-secret"></i> {{'admin.access-control.epeople.actions.stop-impersonating' | translate}} <i class="fa fa-user-secret"></i> {{'admin.access-control.epeople.actions.stop-impersonating' | translate}}
</button> </button>
</div> </div>
<button after class="btn btn-danger delete-button" [disabled]="!(canDelete$ | async)" (click)="delete()"> <button *ngIf="canDelete$ | async" after class="btn btn-danger delete-button" type="button" (click)="delete()">
<i class="fas fa-trash"></i> {{'admin.access-control.epeople.actions.delete' | translate}} <i class="fas fa-trash"></i> {{'admin.access-control.epeople.actions.delete' | translate}}
</button> </button>
</ds-form> </ds-form>
@@ -40,15 +45,15 @@
<ds-themed-loading [showMessage]="false" *ngIf="!formGroup"></ds-themed-loading> <ds-themed-loading [showMessage]="false" *ngIf="!formGroup"></ds-themed-loading>
<div *ngIf="epersonService.getActiveEPerson() | async"> <div *ngIf="epersonService.getActiveEPerson() | async">
<h5>{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}</h5> <h2>{{messagePrefix + '.groupsEPersonIsMemberOf' | translate}}</h2>
<ds-themed-loading [showMessage]="false" *ngIf="!(groups | async)"></ds-themed-loading> <ds-themed-loading [showMessage]="false" *ngIf="groups$ | async | dsHasNoValue"></ds-themed-loading>
<ds-pagination <ds-pagination
*ngIf="(groups | async)?.payload?.totalElements > 0" *ngIf="(groups$ | async)?.payload?.totalElements > 0"
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="(groups | async)?.payload" [pageInfoState]="groupsPageInfoState$"
[collectionSize]="(groups | async)?.payload?.totalElements" [collectionSize]="(groups$ | async)?.payload?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true" [hidePagerWhenSinglePage]="true"
(pageChange)="onPageChange($event)"> (pageChange)="onPageChange($event)">
@@ -63,7 +68,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let group of (groups | async)?.payload?.page"> <tr *ngFor="let group of (groups$ | async)?.payload?.page">
<td class="align-middle">{{group.id}}</td> <td class="align-middle">{{group.id}}</td>
<td class="align-middle"> <td class="align-middle">
<a (click)="groupsDataService.startEditingNewGroup(group)" <a (click)="groupsDataService.startEditingNewGroup(group)"
@@ -79,7 +84,7 @@
</ds-pagination> </ds-pagination>
<div *ngIf="(groups | async)?.payload?.totalElements == 0" class="alert alert-info w-100 mb-2" role="alert"> <div *ngIf="(groups$ | async)?.payload?.totalElements === 0" class="alert alert-info w-100 mb-2" role="alert">
<div>{{messagePrefix + '.memberOfNoGroups' | translate}}</div> <div>{{messagePrefix + '.memberOfNoGroups' | translate}}</div>
<div> <div>
<button [routerLink]="[groupsDataService.getGroupRegistryRouterLink()]" <button [routerLink]="[groupsDataService.getGroupRegistryRouterLink()]"
@@ -87,3 +92,6 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div>

View File

@@ -1,36 +1,73 @@
import { Observable, of as observableOf } from 'rxjs';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import {
import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; ComponentFixture,
import { BrowserModule, By } from '@angular/platform-browser'; TestBed,
waitForAsync,
} from '@angular/core/testing';
import {
FormsModule,
ReactiveFormsModule,
UntypedFormControl,
UntypedFormGroup,
Validators,
} from '@angular/forms';
import {
BrowserModule,
By,
} from '@angular/platform-browser';
import {
ActivatedRoute,
Router,
} from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import {
import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model'; TranslateLoader,
TranslateModule,
} from '@ngx-translate/core';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { AuthService } from '../../../core/auth/auth.service';
import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FindListOptions } from '../../../core/data/find-list-options.model';
import {
buildPaginatedList,
PaginatedList,
} from '../../../core/data/paginated-list.model';
import { RemoteData } from '../../../core/data/remote-data'; import { RemoteData } from '../../../core/data/remote-data';
import { RequestService } from '../../../core/data/request.service';
import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../../core/eperson/group-data.service';
import { EPerson } from '../../../core/eperson/models/eperson.model'; import { EPerson } from '../../../core/eperson/models/eperson.model';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { PageInfo } from '../../../core/shared/page-info.model'; import { PageInfo } from '../../../core/shared/page-info.model';
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
import { FormComponent } from '../../../shared/form/form.component';
import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component';
import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock';
import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock';
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub';
import { AuthServiceStub } from '../../../shared/testing/auth-service.stub';
import {
EPersonMock,
EPersonMock2,
} from '../../../shared/testing/eperson.mock';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
import { RouterStub } from '../../../shared/testing/router.stub';
import { createPaginatedList } from '../../../shared/testing/utils.test';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { HasNoValuePipe } from '../../../shared/utils/has-no-value.pipe';
import { EPeopleRegistryComponent } from '../epeople-registry.component'; import { EPeopleRegistryComponent } from '../epeople-registry.component';
import { EPersonFormComponent } from './eperson-form.component'; import { EPersonFormComponent } from './eperson-form.component';
import { EPersonMock, EPersonMock2 } from '../../../shared/testing/eperson.mock';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock';
import { AuthService } from '../../../core/auth/auth.service';
import { AuthServiceStub } from '../../../shared/testing/auth-service.stub';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { GroupDataService } from '../../../core/eperson/group-data.service';
import { createPaginatedList } from '../../../shared/testing/utils.test';
import { RequestService } from '../../../core/data/request.service';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub';
import { FindListOptions } from '../../../core/data/find-list-options.model';
import { ValidateEmailNotTaken } from './validators/email-taken.validator'; import { ValidateEmailNotTaken } from './validators/email-taken.validator';
import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service';
describe('EPersonFormComponent', () => { describe('EPersonFormComponent', () => {
let component: EPersonFormComponent; let component: EPersonFormComponent;
@@ -43,6 +80,8 @@ describe('EPersonFormComponent', () => {
let authorizationService: AuthorizationDataService; let authorizationService: AuthorizationDataService;
let groupsDataService: GroupDataService; let groupsDataService: GroupDataService;
let epersonRegistrationService: EpersonRegistrationService; let epersonRegistrationService: EpersonRegistrationService;
let route: ActivatedRouteStub;
let router: RouterStub;
let paginationService; let paginationService;
@@ -106,7 +145,10 @@ describe('EPersonFormComponent', () => {
}, },
getEPersonByEmail(email): Observable<RemoteData<EPerson>> { getEPersonByEmail(email): Observable<RemoteData<EPerson>> {
return createSuccessfulRemoteDataObject$(null); return createSuccessfulRemoteDataObject$(null);
} },
findById(_id: string, _useCachedVersionIfAvailable = true, _reRequestOnStale = true, ..._linksToFollow: FollowLinkConfig<EPerson>[]): Observable<RemoteData<EPerson>> {
return createSuccessfulRemoteDataObject$(null);
},
}; };
builderService = Object.assign(getMockFormBuilderService(),{ builderService = Object.assign(getMockFormBuilderService(),{
createFormGroup(formModel, options = null) { createFormGroup(formModel, options = null) {
@@ -169,7 +211,7 @@ describe('EPersonFormComponent', () => {
}, },
isObject(value) { isObject(value) {
return typeof value === 'object' && value !== null; return typeof value === 'object' && value !== null;
} },
}); });
authService = new AuthServiceStub(); authService = new AuthServiceStub();
authorizationService = jasmine.createSpyObj('authorizationService', { authorizationService = jasmine.createSpyObj('authorizationService', {
@@ -178,20 +220,23 @@ describe('EPersonFormComponent', () => {
}); });
groupsDataService = jasmine.createSpyObj('groupsDataService', { groupsDataService = jasmine.createSpyObj('groupsDataService', {
findListByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), findListByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])),
getGroupRegistryRouterLink: '' getGroupRegistryRouterLink: '',
}); });
paginationService = new PaginationServiceStub(); paginationService = new PaginationServiceStub();
route = new ActivatedRouteStub();
router = new RouterStub();
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
provide: TranslateLoader, provide: TranslateLoader,
useClass: TranslateLoaderMock useClass: TranslateLoaderMock,
} },
}), }),
EPersonFormComponent,
HasNoValuePipe,
], ],
declarations: [EPersonFormComponent],
providers: [ providers: [
{ provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: EPersonDataService, useValue: ePersonDataServiceStub },
{ provide: GroupDataService, useValue: groupsDataService }, { provide: GroupDataService, useValue: groupsDataService },
@@ -202,14 +247,20 @@ describe('EPersonFormComponent', () => {
{ provide: PaginationService, useValue: paginationService }, { provide: PaginationService, useValue: paginationService },
{ provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) }, { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) },
{ provide: EpersonRegistrationService, useValue: epersonRegistrationService }, { provide: EpersonRegistrationService, useValue: epersonRegistrationService },
EPeopleRegistryComponent { provide: ActivatedRoute, useValue: route },
{ provide: Router, useValue: router },
EPeopleRegistryComponent,
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA],
}).compileComponents(); })
.overrideComponent(EPersonFormComponent, {
remove: { imports: [ ThemedLoadingComponent, PaginationComponent,FormComponent] },
})
.compileComponents();
})); }));
epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', { epersonRegistrationService = jasmine.createSpyObj('epersonRegistrationService', {
registerEmail: createSuccessfulRemoteDataObject$(null) registerEmail: createSuccessfulRemoteDataObject$(null),
}); });
beforeEach(() => { beforeEach(() => {
@@ -241,12 +292,12 @@ describe('EPersonFormComponent', () => {
metadata: { metadata: {
'eperson.firstname': [ 'eperson.firstname': [
{ {
value: firstName value: firstName,
} },
], ],
'eperson.lastname': [ 'eperson.lastname': [
{ {
value: lastName value: lastName,
}, },
], ],
}, },
@@ -263,24 +314,18 @@ describe('EPersonFormComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
}); });
describe('firstName, lastName and email should be required', () => { describe('firstName, lastName and email should be required', () => {
it('form should be invalid because the firstName is required', waitForAsync(() => { it('form should be invalid because the firstName is required', () => {
fixture.whenStable().then(() => {
expect(component.formGroup.controls.firstName.valid).toBeFalse(); expect(component.formGroup.controls.firstName.valid).toBeFalse();
expect(component.formGroup.controls.firstName.errors.required).toBeTrue(); expect(component.formGroup.controls.firstName.errors.required).toBeTrue();
}); });
})); it('form should be invalid because the lastName is required', () => {
it('form should be invalid because the lastName is required', waitForAsync(() => {
fixture.whenStable().then(() => {
expect(component.formGroup.controls.lastName.valid).toBeFalse(); expect(component.formGroup.controls.lastName.valid).toBeFalse();
expect(component.formGroup.controls.lastName.errors.required).toBeTrue(); expect(component.formGroup.controls.lastName.errors.required).toBeTrue();
}); });
})); it('form should be invalid because the email is required', () => {
it('form should be invalid because the email is required', waitForAsync(() => {
fixture.whenStable().then(() => {
expect(component.formGroup.controls.email.valid).toBeFalse(); expect(component.formGroup.controls.email.valid).toBeFalse();
expect(component.formGroup.controls.email.errors.required).toBeTrue(); expect(component.formGroup.controls.email.errors.required).toBeTrue();
}); });
}));
}); });
describe('after inserting information firstName,lastName and email not required', () => { describe('after inserting information firstName,lastName and email not required', () => {
@@ -290,24 +335,18 @@ describe('EPersonFormComponent', () => {
component.formGroup.controls.email.setValue('test@test.com'); component.formGroup.controls.email.setValue('test@test.com');
fixture.detectChanges(); fixture.detectChanges();
}); });
it('firstName should be valid because the firstName is set', waitForAsync(() => { it('firstName should be valid because the firstName is set', () => {
fixture.whenStable().then(() => {
expect(component.formGroup.controls.firstName.valid).toBeTrue(); expect(component.formGroup.controls.firstName.valid).toBeTrue();
expect(component.formGroup.controls.firstName.errors).toBeNull(); expect(component.formGroup.controls.firstName.errors).toBeNull();
}); });
})); it('lastName should be valid because the lastName is set', () => {
it('lastName should be valid because the lastName is set', waitForAsync(() => {
fixture.whenStable().then(() => {
expect(component.formGroup.controls.lastName.valid).toBeTrue(); expect(component.formGroup.controls.lastName.valid).toBeTrue();
expect(component.formGroup.controls.lastName.errors).toBeNull(); expect(component.formGroup.controls.lastName.errors).toBeNull();
}); });
})); it('email should be valid because the email is set', () => {
it('email should be valid because the email is set', waitForAsync(() => {
fixture.whenStable().then(() => {
expect(component.formGroup.controls.email.valid).toBeTrue(); expect(component.formGroup.controls.email.valid).toBeTrue();
expect(component.formGroup.controls.email.errors).toBeNull(); expect(component.formGroup.controls.email.errors).toBeNull();
}); });
}));
}); });
@@ -316,12 +355,10 @@ describe('EPersonFormComponent', () => {
component.formGroup.controls.email.setValue('test@test'); component.formGroup.controls.email.setValue('test@test');
fixture.detectChanges(); fixture.detectChanges();
}); });
it('email should not be valid because the email pattern', waitForAsync(() => { it('email should not be valid because the email pattern', () => {
fixture.whenStable().then(() => {
expect(component.formGroup.controls.email.valid).toBeFalse(); expect(component.formGroup.controls.email.valid).toBeFalse();
expect(component.formGroup.controls.email.errors.pattern).toBeTruthy(); expect(component.formGroup.controls.email.errors.pattern).toBeTruthy();
}); });
}));
}); });
describe('after already utilized email', () => { describe('after already utilized email', () => {
@@ -329,19 +366,17 @@ describe('EPersonFormComponent', () => {
const ePersonServiceWithEperson = Object.assign(ePersonDataServiceStub,{ const ePersonServiceWithEperson = Object.assign(ePersonDataServiceStub,{
getEPersonByEmail(): Observable<RemoteData<EPerson>> { getEPersonByEmail(): Observable<RemoteData<EPerson>> {
return createSuccessfulRemoteDataObject$(EPersonMock); return createSuccessfulRemoteDataObject$(EPersonMock);
} },
}); });
component.formGroup.controls.email.setValue('test@test.com'); component.formGroup.controls.email.setValue('test@test.com');
component.formGroup.controls.email.setAsyncValidators(ValidateEmailNotTaken.createValidator(ePersonServiceWithEperson)); component.formGroup.controls.email.setAsyncValidators(ValidateEmailNotTaken.createValidator(ePersonServiceWithEperson));
fixture.detectChanges(); fixture.detectChanges();
}); });
it('email should not be valid because email is already taken', waitForAsync(() => { it('email should not be valid because email is already taken', () => {
fixture.whenStable().then(() => {
expect(component.formGroup.controls.email.valid).toBeFalse(); expect(component.formGroup.controls.email.valid).toBeFalse();
expect(component.formGroup.controls.email.errors.emailTaken).toBeTruthy(); expect(component.formGroup.controls.email.errors.emailTaken).toBeTruthy();
}); });
}));
}); });
@@ -366,12 +401,12 @@ describe('EPersonFormComponent', () => {
metadata: { metadata: {
'eperson.firstname': [ 'eperson.firstname': [
{ {
value: firstName value: firstName,
} },
], ],
'eperson.lastname': [ 'eperson.lastname': [
{ {
value: lastName value: lastName,
}, },
], ],
}, },
@@ -393,11 +428,9 @@ describe('EPersonFormComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should emit a new eperson using the correct values', waitForAsync(() => { it('should emit a new eperson using the correct values', () => {
fixture.whenStable().then(() => {
expect(component.submitForm.emit).toHaveBeenCalledWith(expected); expect(component.submitForm.emit).toHaveBeenCalledWith(expected);
}); });
}));
}); });
describe('with an active eperson', () => { describe('with an active eperson', () => {
@@ -409,30 +442,28 @@ describe('EPersonFormComponent', () => {
metadata: { metadata: {
'eperson.firstname': [ 'eperson.firstname': [
{ {
value: firstName value: firstName,
} },
], ],
'eperson.lastname': [ 'eperson.lastname': [
{ {
value: lastName value: lastName,
}, },
], ],
}, },
email: email, email: email,
canLogIn: canLogIn, canLogIn: canLogIn,
requireCertificate: requireCertificate, requireCertificate: requireCertificate,
_links: undefined _links: undefined,
}); });
spyOn(ePersonDataServiceStub, 'getActiveEPerson').and.returnValue(observableOf(expectedWithId)); spyOn(ePersonDataServiceStub, 'getActiveEPerson').and.returnValue(observableOf(expectedWithId));
component.onSubmit(); component.onSubmit();
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should emit the existing eperson using the correct values', waitForAsync(() => { it('should emit the existing eperson using the correct values', () => {
fixture.whenStable().then(() => {
expect(component.submitForm.emit).toHaveBeenCalledWith(expectedWithId); expect(component.submitForm.emit).toHaveBeenCalledWith(expectedWithId);
}); });
}));
}); });
}); });
@@ -443,7 +474,7 @@ describe('EPersonFormComponent', () => {
spyOn(authService, 'impersonate').and.callThrough(); spyOn(authService, 'impersonate').and.callThrough();
ePersonId = 'testEPersonId'; ePersonId = 'testEPersonId';
component.epersonInitial = Object.assign(new EPerson(), { component.epersonInitial = Object.assign(new EPerson(), {
id: ePersonId id: ePersonId,
}); });
component.impersonate(); component.impersonate();
}); });
@@ -491,16 +522,16 @@ describe('EPersonFormComponent', () => {
}); });
it('the delete button should be active if the eperson can be deleted', () => { it('the delete button should be visible if the ePerson can be deleted', () => {
const deleteButton = fixture.debugElement.query(By.css('.delete-button')); const deleteButton = fixture.debugElement.query(By.css('.delete-button'));
expect(deleteButton.nativeElement.disabled).toBe(false); expect(deleteButton).not.toBeNull();
}); });
it('the delete button should be disabled if the eperson cannot be deleted', () => { it('the delete button should be hidden if the ePerson cannot be deleted', () => {
component.canDelete$ = observableOf(false); component.canDelete$ = observableOf(false);
fixture.detectChanges(); fixture.detectChanges();
const deleteButton = fixture.debugElement.query(By.css('.delete-button')); const deleteButton = fixture.debugElement.query(By.css('.delete-button'));
expect(deleteButton.nativeElement.disabled).toBe(true); expect(deleteButton).toBeNull();
}); });
it('should call the epersonFormComponent delete when clicked on the button', () => { it('should call the epersonFormComponent delete when clicked on the button', () => {
@@ -531,7 +562,7 @@ describe('EPersonFormComponent', () => {
ePersonEmail = 'person.email@4science.it'; ePersonEmail = 'person.email@4science.it';
component.epersonInitial = Object.assign(new EPerson(), { component.epersonInitial = Object.assign(new EPerson(), {
id: ePersonId, id: ePersonId,
email: ePersonEmail email: ePersonEmail,
}); });
component.resetPassword(); component.resetPassword();
}); });

View File

@@ -1,47 +1,99 @@
import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; import {
AsyncPipe,
NgClass,
NgFor,
NgIf,
} from '@angular/common';
import {
ChangeDetectorRef,
Component,
EventEmitter,
OnDestroy,
OnInit,
Output,
} from '@angular/core';
import { UntypedFormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import {
ActivatedRoute,
Router,
RouterLink,
} from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { import {
DynamicCheckboxModel, DynamicCheckboxModel,
DynamicFormControlModel, DynamicFormControlModel,
DynamicFormLayout, DynamicFormLayout,
DynamicInputModel DynamicInputModel,
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { TranslateService } from '@ngx-translate/core'; import {
import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; TranslateModule,
import { debounceTime, finalize, map, switchMap, take } from 'rxjs/operators'; TranslateService,
} from '@ngx-translate/core';
import {
combineLatest as observableCombineLatest,
Observable,
of as observableOf,
Subscription,
} from 'rxjs';
import {
debounceTime,
finalize,
map,
switchMap,
take,
} from 'rxjs/operators';
import { AuthService } from '../../../core/auth/auth.service';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { PaginatedList } from '../../../core/data/paginated-list.model'; import { PaginatedList } from '../../../core/data/paginated-list.model';
import { RemoteData } from '../../../core/data/remote-data'; import { RemoteData } from '../../../core/data/remote-data';
import { RequestService } from '../../../core/data/request.service';
import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../../core/eperson/group-data.service'; import { GroupDataService } from '../../../core/eperson/group-data.service';
import { EPerson } from '../../../core/eperson/models/eperson.model'; import { EPerson } from '../../../core/eperson/models/eperson.model';
import { Group } from '../../../core/eperson/models/group.model'; import { Group } from '../../../core/eperson/models/group.model';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { NoContent } from '../../../core/shared/NoContent.model';
import { import {
getFirstCompletedRemoteData, getFirstCompletedRemoteData,
getFirstSucceededRemoteData, getFirstSucceededRemoteData,
getRemoteDataPayload getRemoteDataPayload,
} from '../../../core/shared/operators'; } from '../../../core/shared/operators';
import { PageInfo } from '../../../core/shared/page-info.model';
import { Registration } from '../../../core/shared/registration.model';
import { TYPE_REQUEST_FORGOT } from '../../../register-email-form/register-email-form.component';
import { ConfirmationModalComponent } from '../../../shared/confirmation-modal/confirmation-modal.component';
import { hasValue } from '../../../shared/empty.util'; import { hasValue } from '../../../shared/empty.util';
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
import { FormComponent } from '../../../shared/form/form.component';
import { ThemedLoadingComponent } from '../../../shared/loading/themed-loading.component';
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { PaginationComponent } from '../../../shared/pagination/pagination.component';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { AuthService } from '../../../core/auth/auth.service';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { ConfirmationModalComponent } from '../../../shared/confirmation-modal/confirmation-modal.component';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { RequestService } from '../../../core/data/request.service';
import { NoContent } from '../../../core/shared/NoContent.model';
import { PaginationService } from '../../../core/pagination/pagination.service';
import { followLink } from '../../../shared/utils/follow-link-config.model'; import { followLink } from '../../../shared/utils/follow-link-config.model';
import { HasNoValuePipe } from '../../../shared/utils/has-no-value.pipe';
import { getEPersonsRoute } from '../../access-control-routing-paths';
import { ValidateEmailNotTaken } from './validators/email-taken.validator'; import { ValidateEmailNotTaken } from './validators/email-taken.validator';
import { Registration } from '../../../core/shared/registration.model';
import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service';
import { TYPE_REQUEST_FORGOT } from '../../../register-email-form/register-email-form.component';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-eperson-form', selector: 'ds-eperson-form',
templateUrl: './eperson-form.component.html', templateUrl: './eperson-form.component.html',
imports: [
FormComponent,
NgIf,
NgFor,
AsyncPipe,
TranslateModule,
NgClass,
ThemedLoadingComponent,
PaginationComponent,
RouterLink,
HasNoValuePipe,
],
standalone: true,
}) })
/** /**
* A form used for creating and editing EPeople * A form used for creating and editing EPeople
@@ -81,28 +133,28 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
formLayout: DynamicFormLayout = { formLayout: DynamicFormLayout = {
firstName: { firstName: {
grid: { grid: {
host: 'row' host: 'row',
} },
}, },
lastName: { lastName: {
grid: { grid: {
host: 'row' host: 'row',
} },
}, },
email: { email: {
grid: { grid: {
host: 'row' host: 'row',
} },
}, },
canLogIn: { canLogIn: {
grid: { grid: {
host: 'col col-sm-6 d-inline-block' host: 'col col-sm-6 d-inline-block',
} },
}, },
requireCertificate: { requireCertificate: {
grid: { grid: {
host: 'col col-sm-6 d-inline-block' host: 'col col-sm-6 d-inline-block',
} },
}, },
}; };
@@ -145,7 +197,12 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
/** /**
* A list of all the groups this EPerson is a member of * A list of all the groups this EPerson is a member of
*/ */
groups: Observable<RemoteData<PaginatedList<Group>>>; groups$: Observable<RemoteData<PaginatedList<Group>>>;
/**
* The pagination of the {@link groups$} list.
*/
groupsPageInfoState$: Observable<PageInfo>;
/** /**
* Pagination config used to display the list of groups * Pagination config used to display the list of groups
@@ -153,7 +210,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
id: 'gem', id: 'gem',
pageSize: 5, pageSize: 5,
currentPage: 1 currentPage: 1,
}); });
/** /**
@@ -194,6 +251,8 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
public requestService: RequestService, public requestService: RequestService,
private epersonRegistrationService: EpersonRegistrationService, private epersonRegistrationService: EpersonRegistrationService,
public dsoNameService: DSONameService, public dsoNameService: DSONameService,
protected route: ActivatedRoute,
protected router: Router,
) { ) {
this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => { this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => {
this.epersonInitial = eperson; this.epersonInitial = eperson;
@@ -213,7 +272,9 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
* This method will initialise the page * This method will initialise the page
*/ */
initialisePage() { initialisePage() {
this.subs.push(this.epersonService.findById(this.route.snapshot.params.id).subscribe((ePersonRD: RemoteData<EPerson>) => {
this.epersonService.editEPerson(ePersonRD.payload);
}));
observableCombineLatest([ observableCombineLatest([
this.translateService.get(`${this.messagePrefix}.firstName`), this.translateService.get(`${this.messagePrefix}.firstName`),
this.translateService.get(`${this.messagePrefix}.lastName`), this.translateService.get(`${this.messagePrefix}.lastName`),
@@ -251,23 +312,23 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
required: true, required: true,
errorMessages: { errorMessages: {
emailTaken: 'error.validation.emailTaken', emailTaken: 'error.validation.emailTaken',
pattern: 'error.validation.NotValidEmail' pattern: 'error.validation.NotValidEmail',
}, },
hint: emailHint hint: emailHint,
}); });
this.canLogIn = new DynamicCheckboxModel( this.canLogIn = new DynamicCheckboxModel(
{ {
id: 'canLogIn', id: 'canLogIn',
label: canLogIn, label: canLogIn,
name: 'canLogIn', name: 'canLogIn',
value: (this.epersonInitial != null ? this.epersonInitial.canLogIn : true) value: (this.epersonInitial != null ? this.epersonInitial.canLogIn : true),
}); });
this.requireCertificate = new DynamicCheckboxModel( this.requireCertificate = new DynamicCheckboxModel(
{ {
id: 'requireCertificate', id: 'requireCertificate',
label: requireCertificate, label: requireCertificate,
name: 'requireCertificate', name: 'requireCertificate',
value: (this.epersonInitial != null ? this.epersonInitial.requireCertificate : false) value: (this.epersonInitial != null ? this.epersonInitial.requireCertificate : false),
}); });
this.formModel = [ this.formModel = [
this.firstName, this.firstName,
@@ -279,9 +340,9 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
this.formGroup = this.formBuilderService.createFormGroup(this.formModel); this.formGroup = this.formBuilderService.createFormGroup(this.formModel);
this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => { this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => {
if (eperson != null) { if (eperson != null) {
this.groups = this.groupsDataService.findListByHref(eperson._links.groups.href, { this.groups$ = this.groupsDataService.findListByHref(eperson._links.groups.href, {
currentPage: 1, currentPage: 1,
elementsPerPage: this.config.pageSize elementsPerPage: this.config.pageSize,
}); });
} }
this.formGroup.patchValue({ this.formGroup.patchValue({
@@ -289,7 +350,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
lastName: eperson != null ? eperson.firstMetadataValue('eperson.lastname') : '', lastName: eperson != null ? eperson.firstMetadataValue('eperson.lastname') : '',
email: eperson != null ? eperson.email : '', email: eperson != null ? eperson.email : '',
canLogIn: eperson != null ? eperson.canLogIn : true, canLogIn: eperson != null ? eperson.canLogIn : true,
requireCertificate: eperson != null ? eperson.requireCertificate : false requireCertificate: eperson != null ? eperson.requireCertificate : false,
}); });
if (eperson === null && !!this.formGroup.controls.email) { if (eperson === null && !!this.formGroup.controls.email) {
@@ -302,11 +363,11 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
const activeEPerson$ = this.epersonService.getActiveEPerson(); const activeEPerson$ = this.epersonService.getActiveEPerson();
this.groups = activeEPerson$.pipe( this.groups$ = activeEPerson$.pipe(
switchMap((eperson) => { switchMap((eperson) => {
return observableCombineLatest([observableOf(eperson), this.paginationService.getFindListOptions(this.config.id, { return observableCombineLatest([observableOf(eperson), this.paginationService.getFindListOptions(this.config.id, {
currentPage: 1, currentPage: 1,
elementsPerPage: this.config.pageSize elementsPerPage: this.config.pageSize,
})]); })]);
}), }),
switchMap(([eperson, findListOptions]) => { switchMap(([eperson, findListOptions]) => {
@@ -314,7 +375,11 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
return this.groupsDataService.findListByHref(eperson._links.groups.href, findListOptions, true, true, followLink('object')); return this.groupsDataService.findListByHref(eperson._links.groups.href, findListOptions, true, true, followLink('object'));
} }
return observableOf(undefined); return observableOf(undefined);
}) }),
);
this.groupsPageInfoState$ = this.groups$.pipe(
map(groupsRD => groupsRD.payload.pageInfo),
); );
this.canImpersonate$ = activeEPerson$.pipe( this.canImpersonate$ = activeEPerson$.pipe(
@@ -324,10 +389,10 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
} else { } else {
return observableOf(false); return observableOf(false);
} }
}) }),
); );
this.canDelete$ = activeEPerson$.pipe( this.canDelete$ = activeEPerson$.pipe(
switchMap((eperson) => this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined)) switchMap((eperson) => this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined)),
); );
this.canReset$ = observableOf(true); this.canReset$ = observableOf(true);
}); });
@@ -339,6 +404,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
onCancel() { onCancel() {
this.epersonService.cancelEditEPerson(); this.epersonService.cancelEditEPerson();
this.cancelForm.emit(); this.cancelForm.emit();
void this.router.navigate([getEPersonsRoute()]);
} }
/** /**
@@ -354,12 +420,12 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
metadata: { metadata: {
'eperson.firstname': [ 'eperson.firstname': [
{ {
value: this.firstName.value value: this.firstName.value,
} },
], ],
'eperson.lastname': [ 'eperson.lastname': [
{ {
value: this.lastName.value value: this.lastName.value,
}, },
], ],
}, },
@@ -372,7 +438,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
} else { } else {
this.editEPerson(ePerson, values); this.editEPerson(ePerson, values);
} }
} },
); );
} }
@@ -385,11 +451,13 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
const response = this.epersonService.create(ePersonToCreate); const response = this.epersonService.create(ePersonToCreate);
response.pipe( response.pipe(
getFirstCompletedRemoteData() getFirstCompletedRemoteData(),
).subscribe((rd: RemoteData<EPerson>) => { ).subscribe((rd: RemoteData<EPerson>) => {
if (rd.hasSucceeded) { if (rd.hasSucceeded) {
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', { name: this.dsoNameService.getName(ePersonToCreate) })); this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', { name: this.dsoNameService.getName(ePersonToCreate) }));
this.submitForm.emit(ePersonToCreate); this.submitForm.emit(ePersonToCreate);
this.epersonService.clearEPersonRequests();
void this.router.navigateByUrl(getEPersonsRoute());
} else { } else {
this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure', { name: this.dsoNameService.getName(ePersonToCreate) })); this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure', { name: this.dsoNameService.getName(ePersonToCreate) }));
this.cancelForm.emit(); this.cancelForm.emit();
@@ -409,12 +477,12 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
metadata: { metadata: {
'eperson.firstname': [ 'eperson.firstname': [
{ {
value: (this.firstName.value ? this.firstName.value : ePerson.firstMetadataValue('eperson.firstname')) value: (this.firstName.value ? this.firstName.value : ePerson.firstMetadataValue('eperson.firstname')),
} },
], ],
'eperson.lastname': [ 'eperson.lastname': [
{ {
value: (this.lastName.value ? this.lastName.value : ePerson.firstMetadataValue('eperson.lastname')) value: (this.lastName.value ? this.lastName.value : ePerson.firstMetadataValue('eperson.lastname')),
}, },
], ],
}, },
@@ -429,6 +497,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
if (rd.hasSucceeded) { if (rd.hasSucceeded) {
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.edited.success', { name: this.dsoNameService.getName(editedEperson) })); this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.edited.success', { name: this.dsoNameService.getName(editedEperson) }));
this.submitForm.emit(editedEperson); this.submitForm.emit(editedEperson);
void this.router.navigateByUrl(getEPersonsRoute());
} else { } else {
this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.edited.failure', { name: this.dsoNameService.getName(editedEperson) })); this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.edited.failure', { name: this.dsoNameService.getName(editedEperson) }));
this.cancelForm.emit(); this.cancelForm.emit();
@@ -447,7 +516,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
onPageChange(event) { onPageChange(event) {
this.updateGroups({ this.updateGroups({
currentPage: event, currentPage: event,
elementsPerPage: this.config.pageSize elementsPerPage: this.config.pageSize,
}); });
} }
@@ -468,7 +537,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
take(1), take(1),
switchMap((eperson: EPerson) => { switchMap((eperson: EPerson) => {
const modalRef = this.modalService.open(ConfirmationModalComponent); const modalRef = this.modalService.open(ConfirmationModalComponent);
modalRef.componentInstance.dso = eperson; modalRef.componentInstance.name = this.dsoNameService.getName(eperson);
modalRef.componentInstance.headerLabel = 'confirmation-modal.delete-eperson.header'; modalRef.componentInstance.headerLabel = 'confirmation-modal.delete-eperson.header';
modalRef.componentInstance.infoLabel = 'confirmation-modal.delete-eperson.info'; modalRef.componentInstance.infoLabel = 'confirmation-modal.delete-eperson.info';
modalRef.componentInstance.cancelLabel = 'confirmation-modal.delete-eperson.cancel'; modalRef.componentInstance.cancelLabel = 'confirmation-modal.delete-eperson.cancel';
@@ -483,18 +552,19 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
this.canDelete$ = observableOf(false); this.canDelete$ = observableOf(false);
return this.epersonService.deleteEPerson(eperson).pipe( return this.epersonService.deleteEPerson(eperson).pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
map((restResponse: RemoteData<NoContent>) => ({ restResponse, eperson })) map((restResponse: RemoteData<NoContent>) => ({ restResponse, eperson })),
); );
} else { } else {
return observableOf(null); return observableOf(null);
} }
}), }),
finalize(() => this.canDelete$ = observableOf(true)) finalize(() => this.canDelete$ = observableOf(true)),
); );
}) }),
).subscribe(({ restResponse, eperson }: { restResponse: RemoteData<NoContent> | null, eperson: EPerson }) => { ).subscribe(({ restResponse, eperson }: { restResponse: RemoteData<NoContent> | null, eperson: EPerson }) => {
if (restResponse?.hasSucceeded) { if (restResponse?.hasSucceeded) {
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { name: this.dsoNameService.getName(eperson) })); this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { name: this.dsoNameService.getName(eperson) }));
void this.router.navigate([getEPersonsRoute()]);
} else { } else {
this.notificationsService.error(`Error occurred when trying to delete EPerson with id: ${eperson?.id} with code: ${restResponse?.statusCode} and message: ${restResponse?.errorMessage}`); this.notificationsService.error(`Error occurred when trying to delete EPerson with id: ${eperson?.id} with code: ${restResponse?.statusCode} and message: ${restResponse?.errorMessage}`);
} }
@@ -525,7 +595,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
this.notificationsService.error(this.translateService.get('forgot-email.form.error.head'), this.notificationsService.error(this.translateService.get('forgot-email.form.error.head'),
this.translateService.get('forgot-email.form.error.content', { email: this.epersonInitial.email })); this.translateService.get('forgot-email.form.error.content', { email: this.epersonInitial.email }));
} }
} },
); );
} }
} }
@@ -541,16 +611,6 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
} }
} }
/**
* This method will ensure that the page gets reset and that the cache is cleared
*/
reset() {
this.epersonService.getActiveEPerson().pipe(take(1)).subscribe((eperson: EPerson) => {
this.requestService.removeByHrefSubstring(eperson.self);
});
this.initialisePage();
}
/** /**
* Checks for the given ePerson if there is already an ePerson in the system with that email * Checks for the given ePerson if there is already an ePerson in the system with that email
* and shows notification if this is the case * and shows notification if this is the case
@@ -561,13 +621,13 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
// Relevant message for email in use // Relevant message for email in use
this.subs.push(this.epersonService.searchByScope('email', ePerson.email, { this.subs.push(this.epersonService.searchByScope('email', ePerson.email, {
currentPage: 1, currentPage: 1,
elementsPerPage: 0 elementsPerPage: 0,
}).pipe(getFirstSucceededRemoteData(), getRemoteDataPayload()) }).pipe(getFirstSucceededRemoteData(), getRemoteDataPayload())
.subscribe((list: PaginatedList<EPerson>) => { .subscribe((list: PaginatedList<EPerson>) => {
if (list.totalElements > 0) { if (list.totalElements > 0) {
this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.' + notificationSection + '.failure.emailInUse', { this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.' + notificationSection + '.failure.emailInUse', {
name: this.dsoNameService.getName(ePerson), name: this.dsoNameService.getName(ePerson),
email: ePerson.email email: ePerson.email,
})); }));
} }
})); }));
@@ -578,7 +638,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
*/ */
private updateGroups(options) { private updateGroups(options) {
this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => { this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => {
this.groups = this.groupsDataService.findListByHref(eperson._links.groups.href, options); this.groups$ = this.groupsDataService.findListByHref(eperson._links.groups.href, options);
})); }));
} }
} }

View File

@@ -1,9 +1,12 @@
import { AbstractControl, ValidationErrors } from '@angular/forms'; import {
AbstractControl,
ValidationErrors,
} from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service';
import { getFirstSucceededRemoteData, } from '../../../../core/shared/operators'; import { getFirstSucceededRemoteData } from '../../../../core/shared/operators';
export class ValidateEmailNotTaken { export class ValidateEmailNotTaken {
@@ -17,8 +20,8 @@ export class ValidateEmailNotTaken {
.pipe( .pipe(
getFirstSucceededRemoteData(), getFirstSucceededRemoteData(),
map(res => { map(res => {
return !!res.payload ? { emailTaken: true } : null; return res.payload ? { emailTaken: true } : null;
}) }),
); );
}; };
} }

View File

@@ -0,0 +1,60 @@
import { Injectable } from '@angular/core';
import {
ActivatedRouteSnapshot,
RouterStateSnapshot,
} from '@angular/router';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { RemoteData } from '../../core/data/remote-data';
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { EPerson } from '../../core/eperson/models/eperson.model';
import { ResolvedAction } from '../../core/resolving/resolver.actions';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
import {
followLink,
FollowLinkConfig,
} from '../../shared/utils/follow-link-config.model';
export const EPERSON_EDIT_FOLLOW_LINKS: FollowLinkConfig<EPerson>[] = [
followLink('groups'),
];
/**
* This class represents a resolver that requests a specific {@link EPerson} before the route is activated
*/
@Injectable({
providedIn: 'root',
})
export class EPersonResolver {
constructor(
protected ePersonService: EPersonDataService,
protected store: Store<any>,
) {
}
/**
* Method for resolving a {@link EPerson} based on the parameters in the current route
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
* @returns `Observable<<RemoteData<EPerson>>` Emits the found {@link EPerson} based on the parameters in the current
* route, or an error if something went wrong
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<EPerson>> {
const ePersonRD$: Observable<RemoteData<EPerson>> = this.ePersonService.findById(route.params.id,
true,
false,
...EPERSON_EDIT_FOLLOW_LINKS,
).pipe(
getFirstCompletedRemoteData(),
);
ePersonRD$.subscribe((ePersonRD: RemoteData<EPerson>) => {
this.store.dispatch(new ResolvedAction(state.url, ePersonRD.payload));
});
return ePersonRD$;
}
}

View File

@@ -2,14 +2,14 @@
<div class="group-form row"> <div class="group-form row">
<div class="col-12"> <div class="col-12">
<div *ngIf="groupDataService.getActiveGroup() | async; then editheader; else createHeader"></div> <div *ngIf="groupDataService.getActiveGroup() | async; then editHeader; else createHeader"></div>
<ng-template #createHeader> <ng-template #createHeader>
<h2 class="border-bottom pb-2">{{messagePrefix + '.head.create' | translate}}</h2> <h1 class="border-bottom pb-2">{{messagePrefix + '.head.create' | translate}}</h1>
</ng-template> </ng-template>
<ng-template #editheader> <ng-template #editHeader>
<h2 class="border-bottom pb-2"> <h1 class="border-bottom pb-2">
<span <span
*dsContextHelp="{ *dsContextHelp="{
content: 'admin.access-control.groups.form.tooltip.editGroupPage', content: 'admin.access-control.groups.form.tooltip.editGroupPage',
@@ -20,12 +20,12 @@
> >
{{messagePrefix + '.head.edit' | translate}} {{messagePrefix + '.head.edit' | translate}}
</span> </span>
</h2> </h1>
</ng-template> </ng-template>
<ds-alert *ngIf="groupBeingEdited?.permanent" [type]="AlertTypeEnum.Warning" <ds-alert *ngIf="groupBeingEdited?.permanent" [type]="AlertTypeEnum.Warning"
[content]="messagePrefix + '.alert.permanent'"></ds-alert> [content]="messagePrefix + '.alert.permanent'"></ds-alert>
<ds-alert *ngIf="!(canEdit$ | async) && (groupDataService.getActiveGroup() | async)" [type]="AlertTypeEnum.Warning" <ds-alert *ngIf="(canEdit$ | async) !== true && (groupDataService.getActiveGroup() | async)" [type]="AlertTypeEnum.Warning"
[content]="(messagePrefix + '.alert.workflowGroup' | translate:{ name: dsoNameService.getName((getLinkedDSO(groupBeingEdited) | async)?.payload), comcol: (getLinkedDSO(groupBeingEdited) | async)?.payload?.type, comcolEditRolesRoute: (getLinkedEditRolesRoute(groupBeingEdited) | async) })"> [content]="(messagePrefix + '.alert.workflowGroup' | translate:{ name: dsoNameService.getName((getLinkedDSO(groupBeingEdited) | async)?.payload), comcol: (getLinkedDSO(groupBeingEdited) | async)?.payload?.type, comcolEditRolesRoute: (getLinkedEditRolesRoute(groupBeingEdited) | async) })">
</ds-alert> </ds-alert>
@@ -36,22 +36,21 @@
[displayCancel]="false" [displayCancel]="false"
(submitForm)="onSubmit()"> (submitForm)="onSubmit()">
<div before class="btn-group"> <div before class="btn-group">
<button (click)="onCancel()" <button (click)="onCancel()" type="button"
class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}</button> class="btn btn-outline-secondary"><i class="fas fa-arrow-left"></i> {{messagePrefix + '.return' | translate}}</button>
</div> </div>
<div after *ngIf="groupBeingEdited != null" class="btn-group"> <div after *ngIf="(canEdit$ | async) && !groupBeingEdited?.permanent" class="btn-group">
<button class="btn btn-danger delete-button" [disabled]="!(canEdit$ | async) || groupBeingEdited.permanent" <button (click)="delete()" class="btn btn-danger delete-button" type="button">
(click)="delete()">
<i class="fa fa-trash"></i> {{ messagePrefix + '.actions.delete' | translate}} <i class="fa fa-trash"></i> {{ messagePrefix + '.actions.delete' | translate}}
</button> </button>
</div> </div>
</ds-form> </ds-form>
<div class="mb-5"> <div class="mb-5">
<ds-members-list *ngIf="groupBeingEdited != null" <ds-members-list *ngIf="groupBeingEdited !== undefined"
[messagePrefix]="messagePrefix + '.members-list'"></ds-members-list> [messagePrefix]="messagePrefix + '.members-list'"></ds-members-list>
</div> </div>
<ds-subgroups-list *ngIf="groupBeingEdited != null" <ds-subgroups-list *ngIf="groupBeingEdited !== undefined"
[messagePrefix]="messagePrefix + '.subgroups-list'"></ds-subgroups-list> [messagePrefix]="messagePrefix + '.subgroups-list'"></ds-subgroups-list>

View File

@@ -1,43 +1,79 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import {
import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; ComponentFixture,
import { BrowserModule, By } from '@angular/platform-browser'; TestBed,
import { ActivatedRoute, Router } from '@angular/router'; waitForAsync,
} from '@angular/core/testing';
import {
FormsModule,
ReactiveFormsModule,
UntypedFormControl,
UntypedFormGroup,
Validators,
} from '@angular/forms';
import {
BrowserModule,
By,
} from '@angular/platform-browser';
import {
ActivatedRoute,
Router,
} from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import {
import { Observable, of as observableOf } from 'rxjs'; TranslateLoader,
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import { Operation } from 'fast-json-patch';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service'; import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../../../core/cache/object-cache.service'; import { ObjectCacheService } from '../../../core/cache/object-cache.service';
import { DSOChangeAnalyzer } from '../../../core/data/dso-change-analyzer.service'; import { DSOChangeAnalyzer } from '../../../core/data/dso-change-analyzer.service';
import { DSpaceObjectDataService } from '../../../core/data/dspace-object-data.service'; import { DSpaceObjectDataService } from '../../../core/data/dspace-object-data.service';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model'; import {
buildPaginatedList,
PaginatedList,
} from '../../../core/data/paginated-list.model';
import { RemoteData } from '../../../core/data/remote-data'; import { RemoteData } from '../../../core/data/remote-data';
import { EPersonDataService } from '../../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../../core/eperson/group-data.service'; import { GroupDataService } from '../../../core/eperson/group-data.service';
import { Group } from '../../../core/eperson/models/group.model'; import { Group } from '../../../core/eperson/models/group.model';
import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; import { HALEndpointService } from '../../../core/shared/hal-endpoint.service';
import { NoContent } from '../../../core/shared/NoContent.model';
import { PageInfo } from '../../../core/shared/page-info.model'; import { PageInfo } from '../../../core/shared/page-info.model';
import { UUIDService } from '../../../core/shared/uuid.service'; import { UUIDService } from '../../../core/shared/uuid.service';
import { XSRFService } from '../../../core/xsrf/xsrf.service';
import { AlertComponent } from '../../../shared/alert/alert.component';
import { ContextHelpDirective } from '../../../shared/context-help.directive';
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { FormComponent } from '../../../shared/form/form.component';
import { GroupMock, GroupMock2 } from '../../../shared/testing/group-mock';
import { GroupFormComponent } from './group-form.component';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock';
import { getMockTranslateService } from '../../../shared/mocks/translate.service.mock';
import { TranslateLoaderMock } from '../../../shared/testing/translate-loader.mock';
import { RouterMock } from '../../../shared/mocks/router.mock';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
import { Operation } from 'fast-json-patch';
import { ValidateGroupExists } from './validators/group-exists.validator';
import { NoContent } from '../../../core/shared/NoContent.model';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../../../shared/mocks/dso-name.service.mock'; import { DSONameServiceMock } from '../../../shared/mocks/dso-name.service.mock';
import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock';
import { RouterMock } from '../../../shared/mocks/router.mock';
import { getMockTranslateService } from '../../../shared/mocks/translate.service.mock';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils';
import {
GroupMock,
GroupMock2,
} from '../../../shared/testing/group-mock';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
import { TranslateLoaderMock } from '../../../shared/testing/translate-loader.mock';
import { GroupFormComponent } from './group-form.component';
import { MembersListComponent } from './members-list/members-list.component';
import { SubgroupsListComponent } from './subgroup-list/subgroups-list.component';
import { ValidateGroupExists } from './validators/group-exists.validator';
describe('GroupFormComponent', () => { describe('GroupFormComponent', () => {
let component: GroupFormComponent; let component: GroupFormComponent;
@@ -65,8 +101,8 @@ describe('GroupFormComponent', () => {
metadata: { metadata: {
'dc.description': [ 'dc.description': [
{ {
value: groupDescription value: groupDescription,
} },
], ],
}, },
}); });
@@ -105,7 +141,7 @@ describe('GroupFormComponent', () => {
create(group: Group): Observable<RemoteData<Group>> { create(group: Group): Observable<RemoteData<Group>> {
this.allGroups = [...this.allGroups, group]; this.allGroups = [...this.allGroups, group];
this.createdGroup = Object.assign({}, group, { this.createdGroup = Object.assign({}, group, {
_links: { self: { href: 'group-selflink' } } _links: { self: { href: 'group-selflink' } },
}); });
return createSuccessfulRemoteDataObject$(this.createdGroup); return createSuccessfulRemoteDataObject$(this.createdGroup);
}, },
@@ -114,15 +150,15 @@ describe('GroupFormComponent', () => {
}, },
getGroupEditPageRouterLinkWithID(id: string) { getGroupEditPageRouterLinkWithID(id: string) {
return `group-edit-page-for-${id}`; return `group-edit-page-for-${id}`;
} },
}; };
authorizationService = jasmine.createSpyObj('authorizationService', { authorizationService = jasmine.createSpyObj('authorizationService', {
isAuthorized: observableOf(true) isAuthorized: observableOf(true),
}); });
dsoDataServiceStub = { dsoDataServiceStub = {
findByHref(href: string): Observable<RemoteData<DSpaceObject>> { findByHref(href: string): Observable<RemoteData<DSpaceObject>> {
return null; return null;
} },
}; };
builderService = Object.assign(getMockFormBuilderService(),{ builderService = Object.assign(getMockFormBuilderService(),{
createFormGroup(formModel, options = null) { createFormGroup(formModel, options = null) {
@@ -185,7 +221,7 @@ describe('GroupFormComponent', () => {
}, },
isObject(value) { isObject(value) {
return typeof value === 'object' && value !== null; return typeof value === 'object' && value !== null;
} },
}); });
translateService = getMockTranslateService(); translateService = getMockTranslateService();
router = new RouterMock(); router = new RouterMock();
@@ -195,11 +231,9 @@ describe('GroupFormComponent', () => {
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
provide: TranslateLoader, provide: TranslateLoader,
useClass: TranslateLoaderMock useClass: TranslateLoaderMock,
} },
}), }), GroupFormComponent],
],
declarations: [GroupFormComponent],
providers: [ providers: [
{ provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: EPersonDataService, useValue: ePersonDataServiceStub },
@@ -211,18 +245,29 @@ describe('GroupFormComponent', () => {
{ provide: HttpClient, useValue: {} }, { provide: HttpClient, useValue: {} },
{ provide: ObjectCacheService, useValue: {} }, { provide: ObjectCacheService, useValue: {} },
{ provide: UUIDService, useValue: {} }, { provide: UUIDService, useValue: {} },
{ provide: XSRFService, useValue: {} },
{ provide: Store, useValue: {} }, { provide: Store, useValue: {} },
{ provide: RemoteDataBuildService, useValue: {} }, { provide: RemoteDataBuildService, useValue: {} },
{ provide: HALEndpointService, useValue: {} }, { provide: HALEndpointService, useValue: {} },
{ {
provide: ActivatedRoute, provide: ActivatedRoute,
useValue: { data: observableOf({ dso: { payload: {} } }), params: observableOf({}) } useValue: { data: observableOf({ dso: { payload: {} } }), params: observableOf({}) },
}, },
{ provide: Router, useValue: router }, { provide: Router, useValue: router },
{ provide: AuthorizationDataService, useValue: authorizationService }, { provide: AuthorizationDataService, useValue: authorizationService },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA],
}).compileComponents(); })
.overrideComponent(GroupFormComponent, {
remove: { imports: [
FormComponent,
AlertComponent,
ContextHelpDirective,
MembersListComponent,
SubgroupsListComponent,
] },
})
.compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
@@ -257,8 +302,8 @@ describe('GroupFormComponent', () => {
metadata: { metadata: {
'dc.description': [ 'dc.description': [
{ {
value: groupDescription value: groupDescription,
} },
], ],
}, },
}); });
@@ -273,11 +318,11 @@ describe('GroupFormComponent', () => {
const operations = [{ const operations = [{
op: 'add', op: 'add',
path: '/metadata/dc.description', path: '/metadata/dc.description',
value: 'testDescription' value: 'testDescription',
}, { }, {
op: 'replace', op: 'replace',
path: '/name', path: '/name',
value: 'newGroupName' value: 'newGroupName',
}]; }];
expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations); expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations);
}); });
@@ -289,7 +334,7 @@ describe('GroupFormComponent', () => {
const operations = [{ const operations = [{
op: 'add', op: 'add',
path: '/metadata/dc.description', path: '/metadata/dc.description',
value: 'testDescription' value: 'testDescription',
}]; }];
expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations); expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations);
}); });
@@ -301,7 +346,7 @@ describe('GroupFormComponent', () => {
const operations = [{ const operations = [{
op: 'replace', op: 'replace',
path: '/name', path: '/name',
value: 'newGroupName' value: 'newGroupName',
}]; }];
expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations); expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations);
}); });
@@ -338,8 +383,8 @@ describe('GroupFormComponent', () => {
metadata: { metadata: {
'dc.description': [ 'dc.description': [
{ {
value: groupDescription value: groupDescription,
} },
], ],
}, },
}); });
@@ -376,7 +421,7 @@ describe('GroupFormComponent', () => {
const groupsDataServiceStubWithGroup = Object.assign(groupsDataServiceStub,{ const groupsDataServiceStubWithGroup = Object.assign(groupsDataServiceStub,{
searchGroups(query: string): Observable<RemoteData<PaginatedList<Group>>> { searchGroups(query: string): Observable<RemoteData<PaginatedList<Group>>> {
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [expected])); return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [expected]));
} },
}); });
component.formGroup.controls.groupName.setValue('testName'); component.formGroup.controls.groupName.setValue('testName');
component.formGroup.controls.groupName.setAsyncValidators(ValidateGroupExists.createValidator(groupsDataServiceStubWithGroup)); component.formGroup.controls.groupName.setAsyncValidators(ValidateGroupExists.createValidator(groupsDataServiceStubWithGroup));
@@ -400,7 +445,7 @@ describe('GroupFormComponent', () => {
component.canEdit$ = observableOf(true); component.canEdit$ = observableOf(true);
component.groupBeingEdited = { component.groupBeingEdited = {
permanent: false permanent: false,
} as Group; } as Group;
fixture.detectChanges(); fixture.detectChanges();

View File

@@ -1,24 +1,52 @@
import { Component, EventEmitter, HostListener, OnDestroy, OnInit, Output, ChangeDetectorRef } from '@angular/core'; import {
AsyncPipe,
NgIf,
} from '@angular/common';
import {
ChangeDetectorRef,
Component,
EventEmitter,
HostListener,
OnDestroy,
OnInit,
Output,
} from '@angular/core';
import { UntypedFormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router'; import {
ActivatedRoute,
Router,
} from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { import {
DynamicFormControlModel, DynamicFormControlModel,
DynamicFormLayout, DynamicFormLayout,
DynamicInputModel, DynamicInputModel,
DynamicTextAreaModel DynamicTextAreaModel,
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { TranslateService } from '@ngx-translate/core';
import { import {
ObservedValueOf, TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import { Operation } from 'fast-json-patch';
import {
combineLatest as observableCombineLatest, combineLatest as observableCombineLatest,
Observable, Observable,
of as observableOf, of as observableOf,
Subscription, Subscription,
} from 'rxjs'; } from 'rxjs';
import { catchError, map, switchMap, take, filter, debounceTime } from 'rxjs/operators'; import {
catchError,
debounceTime,
filter,
map,
switchMap,
take,
} from 'rxjs/operators';
import { environment } from '../../../../environments/environment';
import { getCollectionEditRolesRoute } from '../../../collection-page/collection-page-routing-paths'; import { getCollectionEditRolesRoute } from '../../../collection-page/collection-page-routing-paths';
import { getCommunityEditRolesRoute } from '../../../community-page/community-page-routing-paths'; import { getCommunityEditRolesRoute } from '../../../community-page/community-page-routing-paths';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { DSpaceObjectDataService } from '../../../core/data/dspace-object-data.service'; import { DSpaceObjectDataService } from '../../../core/data/dspace-object-data.service';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
@@ -31,27 +59,48 @@ import { Group } from '../../../core/eperson/models/group.model';
import { Collection } from '../../../core/shared/collection.model'; import { Collection } from '../../../core/shared/collection.model';
import { Community } from '../../../core/shared/community.model'; import { Community } from '../../../core/shared/community.model';
import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { NoContent } from '../../../core/shared/NoContent.model';
import { import {
getRemoteDataPayload,
getFirstSucceededRemoteData,
getFirstCompletedRemoteData, getFirstCompletedRemoteData,
getFirstSucceededRemoteDataPayload getFirstSucceededRemoteData,
getFirstSucceededRemoteDataPayload,
getRemoteDataPayload,
} from '../../../core/shared/operators'; } from '../../../core/shared/operators';
import { AlertType } from '../../../shared/alert/aletr-type'; import { AlertComponent } from '../../../shared/alert/alert.component';
import { AlertType } from '../../../shared/alert/alert-type';
import { ConfirmationModalComponent } from '../../../shared/confirmation-modal/confirmation-modal.component'; import { ConfirmationModalComponent } from '../../../shared/confirmation-modal/confirmation-modal.component';
import { hasValue, isNotEmpty, hasValueOperator } from '../../../shared/empty.util'; import { ContextHelpDirective } from '../../../shared/context-help.directive';
import {
hasValue,
hasValueOperator,
isNotEmpty,
} from '../../../shared/empty.util';
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
import { FormComponent } from '../../../shared/form/form.component';
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { followLink } from '../../../shared/utils/follow-link-config.model'; import { followLink } from '../../../shared/utils/follow-link-config.model';
import { NoContent } from '../../../core/shared/NoContent.model'; import {
import { Operation } from 'fast-json-patch'; getGroupEditRoute,
getGroupsRoute,
} from '../../access-control-routing-paths';
import { MembersListComponent } from './members-list/members-list.component';
import { SubgroupsListComponent } from './subgroup-list/subgroups-list.component';
import { ValidateGroupExists } from './validators/group-exists.validator'; import { ValidateGroupExists } from './validators/group-exists.validator';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { environment } from '../../../../environments/environment';
@Component({ @Component({
selector: 'ds-group-form', selector: 'ds-group-form',
templateUrl: './group-form.component.html' templateUrl: './group-form.component.html',
imports: [
FormComponent,
AlertComponent,
NgIf,
AsyncPipe,
TranslateModule,
ContextHelpDirective,
MembersListComponent,
SubgroupsListComponent,
],
standalone: true,
}) })
/** /**
* A form used for creating and editing groups * A form used for creating and editing groups
@@ -83,13 +132,13 @@ export class GroupFormComponent implements OnInit, OnDestroy {
formLayout: DynamicFormLayout = { formLayout: DynamicFormLayout = {
groupName: { groupName: {
grid: { grid: {
host: 'row' host: 'row',
} },
}, },
groupDescription: { groupDescription: {
grid: { grid: {
host: 'row' host: 'row',
} },
}, },
}; };
@@ -165,19 +214,19 @@ export class GroupFormComponent implements OnInit, OnDestroy {
this.canEdit$ = this.groupDataService.getActiveGroup().pipe( this.canEdit$ = this.groupDataService.getActiveGroup().pipe(
hasValueOperator(), hasValueOperator(),
switchMap((group: Group) => { switchMap((group: Group) => {
return observableCombineLatest( return observableCombineLatest([
this.authorizationService.isAuthorized(FeatureID.CanDelete, isNotEmpty(group) ? group.self : undefined), this.authorizationService.isAuthorized(FeatureID.CanDelete, isNotEmpty(group) ? group.self : undefined),
this.hasLinkedDSO(group), this.hasLinkedDSO(group),
(isAuthorized: ObservedValueOf<Observable<boolean>>, hasLinkedDSO: ObservedValueOf<Observable<boolean>>) => { ]).pipe(
return isAuthorized && !hasLinkedDSO; map(([isAuthorized, hasLinkedDSO]: [boolean, boolean]) => isAuthorized && !hasLinkedDSO),
});
})
); );
observableCombineLatest( }),
);
observableCombineLatest([
this.translateService.get(`${this.messagePrefix}.groupName`), this.translateService.get(`${this.messagePrefix}.groupName`),
this.translateService.get(`${this.messagePrefix}.groupCommunity`), this.translateService.get(`${this.messagePrefix}.groupCommunity`),
this.translateService.get(`${this.messagePrefix}.groupDescription`) this.translateService.get(`${this.messagePrefix}.groupDescription`),
).subscribe(([groupName, groupCommunity, groupDescription]) => { ]).subscribe(([groupName, groupCommunity, groupDescription]) => {
this.groupName = new DynamicInputModel({ this.groupName = new DynamicInputModel({
id: 'groupName', id: 'groupName',
label: groupName, label: groupName,
@@ -207,7 +256,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
]; ];
this.formGroup = this.formBuilderService.createFormGroup(this.formModel); this.formGroup = this.formBuilderService.createFormGroup(this.formModel);
if (!!this.formGroup.controls.groupName) { if (this.formGroup.controls.groupName) {
this.formGroup.controls.groupName.setAsyncValidators(ValidateGroupExists.createValidator(this.groupDataService)); this.formGroup.controls.groupName.setAsyncValidators(ValidateGroupExists.createValidator(this.groupDataService));
this.groupNameValueChangeSubscribe = this.groupName.valueChanges.pipe(debounceTime(300)).subscribe(() => { this.groupNameValueChangeSubscribe = this.groupName.valueChanges.pipe(debounceTime(300)).subscribe(() => {
this.changeDetectorRef.detectChanges(); this.changeDetectorRef.detectChanges();
@@ -215,12 +264,12 @@ export class GroupFormComponent implements OnInit, OnDestroy {
} }
this.subs.push( this.subs.push(
observableCombineLatest( observableCombineLatest([
this.groupDataService.getActiveGroup(), this.groupDataService.getActiveGroup(),
this.canEdit$, this.canEdit$,
this.groupDataService.getActiveGroup() this.groupDataService.getActiveGroup()
.pipe(filter((activeGroup) => hasValue(activeGroup)),switchMap((activeGroup) => this.getLinkedDSO(activeGroup).pipe(getFirstSucceededRemoteDataPayload()))) .pipe(filter((activeGroup) => hasValue(activeGroup)),switchMap((activeGroup) => this.getLinkedDSO(activeGroup).pipe(getFirstSucceededRemoteDataPayload()))),
).subscribe(([activeGroup, canEdit, linkedObject]) => { ]).subscribe(([activeGroup, canEdit, linkedObject]) => {
if (activeGroup != null) { if (activeGroup != null) {
@@ -230,12 +279,14 @@ export class GroupFormComponent implements OnInit, OnDestroy {
this.groupBeingEdited = activeGroup; this.groupBeingEdited = activeGroup;
if (linkedObject?.name) { if (linkedObject?.name) {
if (!this.formGroup.controls.groupCommunity) {
this.formBuilderService.insertFormGroupControl(1, this.formGroup, this.formModel, this.groupCommunity); this.formBuilderService.insertFormGroupControl(1, this.formGroup, this.formModel, this.groupCommunity);
this.formGroup.patchValue({ this.formGroup.patchValue({
groupName: activeGroup.name, groupName: activeGroup.name,
groupCommunity: linkedObject?.name ?? '', groupCommunity: linkedObject?.name ?? '',
groupDescription: activeGroup.firstMetadataValue('dc.description'), groupDescription: activeGroup.firstMetadataValue('dc.description'),
}); });
}
} else { } else {
this.formModel = [ this.formModel = [
this.groupName, this.groupName,
@@ -252,7 +303,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
} }
}, 200); }, 200);
} }
}) }),
); );
}); });
} }
@@ -263,7 +314,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
onCancel() { onCancel() {
this.groupDataService.cancelEditGroup(); this.groupDataService.cancelEditGroup();
this.cancelForm.emit(); this.cancelForm.emit();
this.router.navigate([this.groupDataService.getGroupRegistryRouterLink()]); void this.router.navigate([getGroupsRoute()]);
} }
/** /**
@@ -280,9 +331,9 @@ export class GroupFormComponent implements OnInit, OnDestroy {
metadata: { metadata: {
'dc.description': [ 'dc.description': [
{ {
value: this.groupDescription.value value: this.groupDescription.value,
} },
] ],
}, },
}; };
if (group === null) { if (group === null) {
@@ -290,7 +341,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
} else { } else {
this.editGroup(group); this.editGroup(group);
} }
} },
); );
} }
@@ -301,7 +352,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
createNewGroup(values) { createNewGroup(values) {
const groupToCreate = Object.assign(new Group(), values); const groupToCreate = Object.assign(new Group(), values);
this.groupDataService.create(groupToCreate).pipe( this.groupDataService.create(groupToCreate).pipe(
getFirstCompletedRemoteData() getFirstCompletedRemoteData(),
).subscribe((rd: RemoteData<Group>) => { ).subscribe((rd: RemoteData<Group>) => {
if (rd.hasSucceeded) { if (rd.hasSucceeded) {
this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.created.success', { name: groupToCreate.name })); this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.created.success', { name: groupToCreate.name }));
@@ -310,7 +361,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
const groupSelfLink = rd.payload._links.self.href; const groupSelfLink = rd.payload._links.self.href;
this.setActiveGroupWithLink(groupSelfLink); this.setActiveGroupWithLink(groupSelfLink);
this.groupDataService.clearGroupsRequests(); this.groupDataService.clearGroupsRequests();
this.router.navigateByUrl(this.groupDataService.getGroupEditPageRouterLinkWithID(rd.payload.uuid)); void this.router.navigateByUrl(getGroupEditRoute(rd.payload.uuid));
} }
} else { } else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.created.failure', { name: groupToCreate.name })); this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.created.failure', { name: groupToCreate.name }));
@@ -330,7 +381,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
// Relevant message for group name in use // Relevant message for group name in use
this.subs.push(this.groupDataService.searchGroups(group.name, { this.subs.push(this.groupDataService.searchGroups(group.name, {
currentPage: 1, currentPage: 1,
elementsPerPage: 0 elementsPerPage: 0,
}).pipe(getFirstSucceededRemoteData(), getRemoteDataPayload()) }).pipe(getFirstSucceededRemoteData(), getRemoteDataPayload())
.subscribe((list: PaginatedList<Group>) => { .subscribe((list: PaginatedList<Group>) => {
if (list.totalElements > 0) { if (list.totalElements > 0) {
@@ -352,7 +403,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
operations = [...operations, { operations = [...operations, {
op: 'add', op: 'add',
path: '/metadata/dc.description', path: '/metadata/dc.description',
value: this.groupDescription.value value: this.groupDescription.value,
}]; }];
} }
@@ -360,12 +411,12 @@ export class GroupFormComponent implements OnInit, OnDestroy {
operations = [...operations, { operations = [...operations, {
op: 'replace', op: 'replace',
path: '/name', path: '/name',
value: this.groupName.value value: this.groupName.value,
}]; }];
} }
this.groupDataService.patch(group, operations).pipe( this.groupDataService.patch(group, operations).pipe(
getFirstCompletedRemoteData() getFirstCompletedRemoteData(),
).subscribe((rd: RemoteData<Group>) => { ).subscribe((rd: RemoteData<Group>) => {
if (rd.hasSucceeded) { if (rd.hasSucceeded) {
this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.edited.success', { name: this.dsoNameService.getName(rd.payload) })); this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.edited.success', { name: this.dsoNameService.getName(rd.payload) }));
@@ -418,7 +469,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
delete() { delete() {
this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((group: Group) => { this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((group: Group) => {
const modalRef = this.modalService.open(ConfirmationModalComponent); const modalRef = this.modalService.open(ConfirmationModalComponent);
modalRef.componentInstance.dso = group; modalRef.componentInstance.name = this.dsoNameService.getName(group);
modalRef.componentInstance.headerLabel = this.messagePrefix + '.delete-group.modal.header'; modalRef.componentInstance.headerLabel = this.messagePrefix + '.delete-group.modal.header';
modalRef.componentInstance.infoLabel = this.messagePrefix + '.delete-group.modal.info'; modalRef.componentInstance.infoLabel = this.messagePrefix + '.delete-group.modal.info';
modalRef.componentInstance.cancelLabel = this.messagePrefix + '.delete-group.modal.cancel'; modalRef.componentInstance.cancelLabel = this.messagePrefix + '.delete-group.modal.cancel';
@@ -504,7 +555,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
return getCollectionEditRolesRoute(rd.payload.id); return getCollectionEditRolesRoute(rd.payload.id);
} }
} }
}) }),
); );
} }
} }

View File

@@ -1,110 +1,12 @@
<ng-container> <ng-container>
<h3 class="border-bottom pb-2">{{messagePrefix + '.head' | translate}}</h3> <h2 class="border-bottom pb-2">{{messagePrefix + '.head' | translate}}</h2>
<h4 id="search" class="border-bottom pb-2"> <h3>{{messagePrefix + '.headMembers' | translate}}</h3>
<span
*dsContextHelp="{
content: 'admin.access-control.groups.form.tooltip.editGroup.addEpeople',
id: 'edit-group-add-epeople',
iconPlacement: 'right',
tooltipPlacement: ['top', 'right', 'bottom']
}"
>
{{messagePrefix + '.search.head' | translate}}
</span>
</h4>
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between"> <ds-pagination *ngIf="(ePeopleMembersOfGroup | async)?.totalElements > 0"
<div>
<select name="scope" id="scope" formControlName="scope" class="form-control" aria-label="Search scope">
<option value="metadata">{{messagePrefix + '.search.scope.metadata' | translate}}</option>
<option value="email">{{messagePrefix + '.search.scope.email' | translate}}</option>
</select>
</div>
<div class="flex-grow-1 mr-3 ml-3">
<div class="form-group input-group">
<input type="text" name="query" id="query" formControlName="query"
class="form-control" aria-label="Search input">
<span class="input-group-append">
<button type="submit" class="search-button btn btn-primary">
<i class="fas fa-search"></i> {{ messagePrefix + '.search.button' | translate }}</button>
</span>
</div>
</div>
<div>
<button (click)="clearFormAndResetResult();"
class="btn btn-secondary">{{messagePrefix + '.button.see-all' | translate}}</button>
</div>
</form>
<ds-pagination *ngIf="(ePeopleSearchDtos | async)?.totalElements > 0"
[paginationOptions]="configSearch"
[pageInfoState]="(ePeopleSearchDtos | async)"
[collectionSize]="(ePeopleSearchDtos | async)?.totalElements"
[hideGear]="true"
[hidePagerWhenSinglePage]="true">
<div class="table-responsive">
<table id="epersonsSearch" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th scope="col" class="align-middle">{{messagePrefix + '.table.id' | translate}}</th>
<th scope="col" class="align-middle">{{messagePrefix + '.table.name' | translate}}</th>
<th scope="col" class="align-middle">{{messagePrefix + '.table.identity' | translate}}</th>
<th class="align-middle">{{messagePrefix + '.table.edit' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let ePerson of (ePeopleSearchDtos | async)?.page">
<td class="align-middle">{{ePerson.eperson.id}}</td>
<td class="align-middle">
<a (click)="ePersonDataService.startEditingNewEPerson(ePerson.eperson)"
[routerLink]="[ePersonDataService.getEPeoplePageRouterLink()]">
{{ dsoNameService.getName(ePerson.eperson) }}
</a>
</td>
<td class="align-middle">
{{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}<br/>
{{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }}
</td>
<td class="align-middle">
<div class="btn-group edit-field">
<button *ngIf="ePerson.memberOfGroup"
(click)="deleteMemberFromGroup(ePerson)"
[disabled]="actionConfig.remove.disabled"
[ngClass]="['btn btn-sm', actionConfig.remove.css]"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}">
<i [ngClass]="actionConfig.remove.icon"></i>
</button>
<button *ngIf="!ePerson.memberOfGroup"
(click)="addMemberToGroup(ePerson)"
[disabled]="actionConfig.add.disabled"
[ngClass]="['btn btn-sm', actionConfig.add.css]"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}">
<i [ngClass]="actionConfig.add.icon"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</ds-pagination>
<div *ngIf="(ePeopleSearchDtos | async)?.totalElements == 0 && searchDone"
class="alert alert-info w-100 mb-2"
role="alert">
{{messagePrefix + '.no-items' | translate}}
</div>
<h4>{{messagePrefix + '.headMembers' | translate}}</h4>
<ds-pagination *ngIf="(ePeopleMembersOfGroupDtos | async)?.totalElements > 0"
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="(ePeopleMembersOfGroupDtos | async)" [pageInfoState]="(ePeopleMembersOfGroup | async)"
[collectionSize]="(ePeopleMembersOfGroupDtos | async)?.totalElements" [collectionSize]="(ePeopleMembersOfGroup | async)?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">
@@ -119,32 +21,104 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let ePerson of (ePeopleMembersOfGroupDtos | async)?.page"> <tr *ngFor="let eperson of (ePeopleMembersOfGroup | async)?.page">
<td class="align-middle">{{ePerson.eperson.id}}</td> <td class="align-middle">{{eperson.id}}</td>
<td class="align-middle"> <td class="align-middle">
<a (click)="ePersonDataService.startEditingNewEPerson(ePerson.eperson)" <a [routerLink]="getEPersonEditRoute(eperson.id)">
[routerLink]="[ePersonDataService.getEPeoplePageRouterLink()]"> {{ dsoNameService.getName(eperson) }}
{{ dsoNameService.getName(ePerson.eperson) }}
</a> </a>
</td> </td>
<td class="align-middle"> <td class="align-middle">
{{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}<br/> {{messagePrefix + '.table.email' | translate}}: {{ eperson.email ? eperson.email : '-' }}<br/>
{{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }} {{messagePrefix + '.table.netid' | translate}}: {{ eperson.netid ? eperson.netid : '-' }}
</td> </td>
<td class="align-middle"> <td class="align-middle">
<div class="btn-group edit-field"> <div class="btn-group edit-field">
<button *ngIf="ePerson.memberOfGroup" <button (click)="deleteMemberFromGroup(eperson)"
(click)="deleteMemberFromGroup(ePerson)"
[disabled]="actionConfig.remove.disabled" [disabled]="actionConfig.remove.disabled"
[ngClass]="['btn btn-sm', actionConfig.remove.css]" [ngClass]="['btn btn-sm', actionConfig.remove.css]"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}"> title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(eperson) } }}">
<i [ngClass]="actionConfig.remove.icon"></i> <i [ngClass]="actionConfig.remove.icon"></i>
</button> </button>
<button *ngIf="!ePerson.memberOfGroup" </div>
(click)="addMemberToGroup(ePerson)" </td>
</tr>
</tbody>
</table>
</div>
</ds-pagination>
<div *ngIf="(ePeopleMembersOfGroup | async) === undefined || (ePeopleMembersOfGroup | async)?.totalElements === 0" class="alert alert-info w-100 mb-2"
role="alert">
{{messagePrefix + '.no-members-yet' | translate}}
</div>
<h3 id="search" class="border-bottom pb-2">
<span
*dsContextHelp="{
content: 'admin.access-control.groups.form.tooltip.editGroup.addEpeople',
id: 'edit-group-add-epeople',
iconPlacement: 'right',
tooltipPlacement: ['top', 'right', 'bottom']
}"
>
{{messagePrefix + '.search.head' | translate}}
</span>
</h3>
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
<div class="flex-grow-1 mr-3">
<div class="form-group input-group mr-3">
<input type="text" name="query" id="query" formControlName="query"
class="form-control" aria-label="Search input">
<span class="input-group-append">
<button type="submit" class="search-button btn btn-primary">
<i class="fas fa-search"></i> {{ messagePrefix + '.search.button' | translate }}</button>
</span>
</div>
</div>
<div>
<button (click)="clearFormAndResetResult();"
class="btn btn-secondary">{{messagePrefix + '.button.see-all' | translate}}</button>
</div>
</form>
<ds-pagination *ngIf="(ePeopleSearch | async)?.totalElements > 0"
[paginationOptions]="configSearch"
[pageInfoState]="(ePeopleSearch | async)"
[collectionSize]="(ePeopleSearch | async)?.totalElements"
[hideGear]="true"
[hidePagerWhenSinglePage]="true">
<div class="table-responsive">
<table id="epersonsSearch" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th scope="col" class="align-middle">{{messagePrefix + '.table.id' | translate}}</th>
<th scope="col" class="align-middle">{{messagePrefix + '.table.name' | translate}}</th>
<th scope="col" class="align-middle">{{messagePrefix + '.table.identity' | translate}}</th>
<th class="align-middle">{{messagePrefix + '.table.edit' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let eperson of (ePeopleSearch | async)?.page">
<td class="align-middle">{{eperson.id}}</td>
<td class="align-middle">
<a [routerLink]="getEPersonEditRoute(eperson.id)">
{{ dsoNameService.getName(eperson) }}
</a>
</td>
<td class="align-middle">
{{messagePrefix + '.table.email' | translate}}: {{ eperson.email ? eperson.email : '-' }}<br/>
{{messagePrefix + '.table.netid' | translate}}: {{ eperson.netid ? eperson.netid : '-' }}
</td>
<td class="align-middle">
<div class="btn-group edit-field">
<button (click)="addMemberToGroup(eperson)"
[disabled]="actionConfig.add.disabled" [disabled]="actionConfig.add.disabled"
[ngClass]="['btn btn-sm', actionConfig.add.css]" [ngClass]="['btn btn-sm', actionConfig.add.css]"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}"> title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(eperson) } }}">
<i [ngClass]="actionConfig.add.icon"></i> <i [ngClass]="actionConfig.add.icon"></i>
</button> </button>
</div> </div>
@@ -156,9 +130,10 @@
</ds-pagination> </ds-pagination>
<div *ngIf="(ePeopleMembersOfGroupDtos | async) == undefined || (ePeopleMembersOfGroupDtos | async)?.totalElements == 0" class="alert alert-info w-100 mb-2" <div *ngIf="(ePeopleSearch | async)?.totalElements === 0 && searchDone"
class="alert alert-info w-100 mb-2"
role="alert"> role="alert">
{{messagePrefix + '.no-members-yet' | translate}} {{messagePrefix + '.no-items' | translate}}
</div> </div>
</ng-container> </ng-container>

View File

@@ -1,35 +1,74 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import {
import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; DebugElement,
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; NO_ERRORS_SCHEMA,
import { BrowserModule, By } from '@angular/platform-browser'; } from '@angular/core';
import { Router } from '@angular/router'; import {
ComponentFixture,
fakeAsync,
flush,
inject,
TestBed,
tick,
waitForAsync,
} from '@angular/core/testing';
import {
FormsModule,
ReactiveFormsModule,
} from '@angular/forms';
import {
BrowserModule,
By,
} from '@angular/platform-browser';
import {
ActivatedRoute,
Router,
} from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import {
import { Observable, of as observableOf } from 'rxjs'; TranslateLoader,
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
import { RestResponse } from '../../../../core/cache/response.models'; import { RestResponse } from '../../../../core/cache/response.models';
import { buildPaginatedList, PaginatedList } from '../../../../core/data/paginated-list.model'; import {
buildPaginatedList,
PaginatedList,
} from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../../../core/eperson/group-data.service'; import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { EPerson } from '../../../../core/eperson/models/eperson.model'; import { EPerson } from '../../../../core/eperson/models/eperson.model';
import { Group } from '../../../../core/eperson/models/group.model'; import { Group } from '../../../../core/eperson/models/group.model';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { GroupMock, GroupMock2 } from '../../../../shared/testing/group-mock';
import { MembersListComponent } from './members-list.component';
import { EPersonMock, EPersonMock2 } from '../../../../shared/testing/eperson.mock';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock';
import { getMockFormBuilderService } from '../../../../shared/mocks/form-builder-service.mock';
import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
import { RouterMock } from '../../../../shared/mocks/router.mock';
import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationService } from '../../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; import { PageInfo } from '../../../../core/shared/page-info.model';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { ContextHelpDirective } from '../../../../shared/context-help.directive';
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock';
import { getMockFormBuilderService } from '../../../../shared/mocks/form-builder-service.mock';
import { RouterMock } from '../../../../shared/mocks/router.mock';
import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { PaginationComponent } from '../../../../shared/pagination/pagination.component';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { ActivatedRouteStub } from '../../../../shared/testing/active-router.stub';
import {
EPersonMock,
EPersonMock2,
} from '../../../../shared/testing/eperson.mock';
import { GroupMock } from '../../../../shared/testing/group-mock';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub';
import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock';
import { MembersListComponent } from './members-list.component';
// todo: optimize imports
describe('MembersListComponent', () => { describe('MembersListComponent', () => {
let component: MembersListComponent; let component: MembersListComponent;
@@ -39,28 +78,26 @@ describe('MembersListComponent', () => {
let ePersonDataServiceStub: any; let ePersonDataServiceStub: any;
let groupsDataServiceStub: any; let groupsDataServiceStub: any;
let activeGroup; let activeGroup;
let allEPersons: EPerson[];
let allGroups: Group[];
let epersonMembers: EPerson[]; let epersonMembers: EPerson[];
let subgroupMembers: Group[]; let epersonNonMembers: EPerson[];
let paginationService; let paginationService;
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
activeGroup = GroupMock; activeGroup = GroupMock;
epersonMembers = [EPersonMock2]; epersonMembers = [EPersonMock2];
subgroupMembers = [GroupMock2]; epersonNonMembers = [EPersonMock];
allEPersons = [EPersonMock, EPersonMock2];
allGroups = [GroupMock, GroupMock2];
ePersonDataServiceStub = { ePersonDataServiceStub = {
activeGroup: activeGroup, activeGroup: activeGroup,
epersonMembers: epersonMembers, epersonMembers: epersonMembers,
subgroupMembers: subgroupMembers, epersonNonMembers: epersonNonMembers,
// This method is used to get all the current members
findListByHref(_href: string): Observable<RemoteData<PaginatedList<EPerson>>> { findListByHref(_href: string): Observable<RemoteData<PaginatedList<EPerson>>> {
return createSuccessfulRemoteDataObject$(buildPaginatedList<EPerson>(new PageInfo(), groupsDataServiceStub.getEPersonMembers())); return createSuccessfulRemoteDataObject$(buildPaginatedList<EPerson>(new PageInfo(), groupsDataServiceStub.getEPersonMembers()));
}, },
searchByScope(scope: string, query: string): Observable<RemoteData<PaginatedList<EPerson>>> { // This method is used to search across *non-members*
searchNonMembers(query: string, group: string): Observable<RemoteData<PaginatedList<EPerson>>> {
if (query === '') { if (query === '') {
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), allEPersons)); return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), epersonNonMembers));
} }
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), []));
}, },
@@ -70,29 +107,26 @@ describe('MembersListComponent', () => {
clearLinkRequests() { clearLinkRequests() {
// empty // empty
}, },
getEPeoplePageRouterLink(): string {
return '/access-control/epeople';
}
}; };
groupsDataServiceStub = { groupsDataServiceStub = {
activeGroup: activeGroup, activeGroup: activeGroup,
epersonMembers: epersonMembers, epersonMembers: epersonMembers,
subgroupMembers: subgroupMembers, epersonNonMembers: epersonNonMembers,
allGroups: allGroups,
getActiveGroup(): Observable<Group> { getActiveGroup(): Observable<Group> {
return observableOf(activeGroup); return observableOf(activeGroup);
}, },
getEPersonMembers() { getEPersonMembers() {
return this.epersonMembers; return this.epersonMembers;
}, },
searchGroups(query: string): Observable<RemoteData<PaginatedList<Group>>> { addMemberToGroup(parentGroup, epersonToAdd: EPerson): Observable<RestResponse> {
if (query === '') { // Add eperson to list of members
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), this.allGroups)); this.epersonMembers = [...this.epersonMembers, epersonToAdd];
// Remove eperson from list of non-members
this.epersonNonMembers.forEach( (eperson: EPerson, index: number) => {
if (eperson.id === epersonToAdd.id) {
this.epersonNonMembers.splice(index, 1);
} }
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); });
},
addMemberToGroup(parentGroup, eperson: EPerson): Observable<RestResponse> {
this.epersonMembers = [...this.epersonMembers, eperson];
return observableOf(new RestResponse(true, 200, 'Success')); return observableOf(new RestResponse(true, 200, 'Success'));
}, },
clearGroupsRequests() { clearGroupsRequests() {
@@ -105,16 +139,16 @@ describe('MembersListComponent', () => {
return '/access-control/groups/' + group.id; return '/access-control/groups/' + group.id;
}, },
deleteMemberFromGroup(parentGroup, epersonToDelete: EPerson): Observable<RestResponse> { deleteMemberFromGroup(parentGroup, epersonToDelete: EPerson): Observable<RestResponse> {
this.epersonMembers = this.epersonMembers.find((eperson: EPerson) => { // Remove eperson from list of members
if (eperson.id !== epersonToDelete.id) { this.epersonMembers.forEach( (eperson: EPerson, index: number) => {
return eperson; if (eperson.id === epersonToDelete.id) {
this.epersonMembers.splice(index, 1);
} }
}); });
if (this.epersonMembers === undefined) { // Add eperson to list of non-members
this.epersonMembers = []; this.epersonNonMembers = [...this.epersonNonMembers, epersonToDelete];
}
return observableOf(new RestResponse(true, 200, 'Success')); return observableOf(new RestResponse(true, 200, 'Success'));
} },
}; };
builderService = getMockFormBuilderService(); builderService = getMockFormBuilderService();
translateService = getMockTranslateService(); translateService = getMockTranslateService();
@@ -125,11 +159,9 @@ describe('MembersListComponent', () => {
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
provide: TranslateLoader, provide: TranslateLoader,
useClass: TranslateLoaderMock useClass: TranslateLoaderMock,
} },
}), }), MembersListComponent],
],
declarations: [MembersListComponent],
providers: [MembersListComponent, providers: [MembersListComponent,
{ provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: EPersonDataService, useValue: ePersonDataServiceStub },
{ provide: GroupDataService, useValue: groupsDataServiceStub }, { provide: GroupDataService, useValue: groupsDataServiceStub },
@@ -138,9 +170,16 @@ describe('MembersListComponent', () => {
{ provide: Router, useValue: new RouterMock() }, { provide: Router, useValue: new RouterMock() },
{ provide: PaginationService, useValue: paginationService }, { provide: PaginationService, useValue: paginationService },
{ provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA],
}).compileComponents(); })
.overrideComponent(MembersListComponent, {
remove: {
imports: [PaginationComponent, ContextHelpDirective],
},
})
.compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
@@ -160,6 +199,7 @@ describe('MembersListComponent', () => {
expect(comp).toBeDefined(); expect(comp).toBeDefined();
})); }));
describe('current members list', () => {
it('should show list of eperson members of current active group', () => { it('should show list of eperson members of current active group', () => {
const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child')); const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child'));
expect(epersonIdsFound.length).toEqual(1); expect(epersonIdsFound.length).toEqual(1);
@@ -170,55 +210,8 @@ describe('MembersListComponent', () => {
}); });
}); });
describe('search', () => { it('should show a delete button next to each member', () => {
describe('when searching without query', () => { const epersonsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tbody tr'));
let epersonsFound: DebugElement[];
beforeEach(fakeAsync(() => {
spyOn(component, 'isMemberOfGroup').and.callFake((ePerson: EPerson) => {
return observableOf(activeGroup.epersons.includes(ePerson));
});
component.search({ scope: 'metadata', query: '' });
tick();
fixture.detectChanges();
epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr'));
// Stop using the fake spy function (because otherwise the clicking on the buttons will not change anything
// because they don't change the value of activeGroup.epersons)
jasmine.getEnv().allowRespy(true);
spyOn(component, 'isMemberOfGroup').and.callThrough();
}));
it('should display all epersons', () => {
expect(epersonsFound.length).toEqual(2);
});
describe('if eperson is already a eperson', () => {
it('should have delete button, else it should have add button', () => {
const memberIds: string[] = activeGroup.epersons.map((ePerson: EPerson) => ePerson.id);
epersonsFound.map((foundEPersonRowElement: DebugElement) => {
const epersonId: DebugElement = foundEPersonRowElement.query(By.css('td:first-child'));
const addButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-plus'));
const deleteButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-trash-alt'));
if (memberIds.includes(epersonId.nativeElement.textContent)) {
expect(addButton).toBeNull();
expect(deleteButton).not.toBeNull();
} else {
expect(deleteButton).toBeNull();
expect(addButton).not.toBeNull();
}
});
});
});
describe('if first add button is pressed', () => {
beforeEach(fakeAsync(() => {
const addButton: DebugElement = fixture.debugElement.query(By.css('#epersonsSearch tbody .fa-plus'));
addButton.nativeElement.click();
tick();
fixture.detectChanges();
}));
it('then all the ePersons are member of the active group', () => {
epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr'));
expect(epersonsFound.length).toEqual(2);
epersonsFound.map((foundEPersonRowElement: DebugElement) => { epersonsFound.map((foundEPersonRowElement: DebugElement) => {
const addButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-plus')); const addButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-plus'));
const deleteButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-trash-alt')); const deleteButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-trash-alt'));
@@ -226,25 +219,59 @@ describe('MembersListComponent', () => {
expect(deleteButton).not.toBeNull(); expect(deleteButton).not.toBeNull();
}); });
}); });
});
describe('if first delete button is pressed', () => { describe('if first delete button is pressed', () => {
beforeEach(fakeAsync(() => { beforeEach(() => {
const deleteButton: DebugElement = fixture.debugElement.query(By.css('#epersonsSearch tbody .fa-trash-alt')); const deleteButton: DebugElement = fixture.debugElement.query(By.css('#ePeopleMembersOfGroup tbody .fa-trash-alt'));
deleteButton.nativeElement.click(); deleteButton.nativeElement.click();
fixture.detectChanges();
});
it('then no ePerson remains as a member of the active group.', () => {
const epersonsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tbody tr'));
expect(epersonsFound.length).toEqual(0);
});
});
});
describe('search', () => {
describe('when searching without query', () => {
let epersonsFound: DebugElement[];
beforeEach(fakeAsync(() => {
component.search({ scope: 'metadata', query: '' });
tick(); tick();
fixture.detectChanges(); fixture.detectChanges();
}));
it('then no ePerson is member of the active group', () => {
epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr')); epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr'));
expect(epersonsFound.length).toEqual(2); }));
it('should display only non-members of the group', () => {
const epersonIdsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr td:first-child'));
expect(epersonIdsFound.length).toEqual(1);
epersonNonMembers.map((eperson: EPerson) => {
expect(epersonIdsFound.find((foundEl) => {
return (foundEl.nativeElement.textContent.trim() === eperson.uuid);
})).toBeTruthy();
});
});
it('should display an add button next to non-members, not a delete button', () => {
epersonsFound.map((foundEPersonRowElement: DebugElement) => { epersonsFound.map((foundEPersonRowElement: DebugElement) => {
const addButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-plus')); const addButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-plus'));
const deleteButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-trash-alt')); const deleteButton: DebugElement = foundEPersonRowElement.query(By.css('td:last-child .fa-trash-alt'));
expect(deleteButton).toBeNull();
expect(addButton).not.toBeNull(); expect(addButton).not.toBeNull();
expect(deleteButton).toBeNull();
}); });
}); });
describe('if first add button is pressed', () => {
beforeEach(() => {
const addButton: DebugElement = fixture.debugElement.query(By.css('#epersonsSearch tbody .fa-plus'));
addButton.nativeElement.click();
fixture.detectChanges();
});
it('then all (two) ePersons are member of the active group. No non-members left', () => {
epersonsFound = fixture.debugElement.queryAll(By.css('#epersonsSearch tbody tr'));
expect(epersonsFound.length).toEqual(0);
});
}); });
}); });
}); });

View File

@@ -1,41 +1,66 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { UntypedFormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { import {
Observable, AsyncPipe,
of as observableOf, NgClass,
Subscription, NgForOf,
NgIf,
} from '@angular/common';
import {
Component,
Input,
OnDestroy,
OnInit,
} from '@angular/core';
import {
ReactiveFormsModule,
UntypedFormBuilder,
} from '@angular/forms';
import {
Router,
RouterLink,
} from '@angular/router';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import {
BehaviorSubject, BehaviorSubject,
combineLatest as observableCombineLatest, Observable,
ObservedValueOf, Subscription,
} from 'rxjs'; } from 'rxjs';
import { defaultIfEmpty, map, mergeMap, switchMap, take } from 'rxjs/operators'; import {
import { buildPaginatedList, PaginatedList } from '../../../../core/data/paginated-list.model'; map,
switchMap,
take,
} from 'rxjs/operators';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
import { PaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { EPersonDataService } from '../../../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../../../core/eperson/group-data.service'; import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { EPerson } from '../../../../core/eperson/models/eperson.model'; import { EPerson } from '../../../../core/eperson/models/eperson.model';
import { Group } from '../../../../core/eperson/models/group.model'; import { Group } from '../../../../core/eperson/models/group.model';
import {
getFirstSucceededRemoteData,
getFirstCompletedRemoteData,
getAllCompletedRemoteData,
getRemoteDataPayload
} from '../../../../core/shared/operators';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model';
import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationService } from '../../../../core/pagination/pagination.service';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import {
getAllCompletedRemoteData,
getFirstCompletedRemoteData,
getRemoteDataPayload,
} from '../../../../core/shared/operators';
import { ContextHelpDirective } from '../../../../shared/context-help.directive';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { PaginationComponent } from '../../../../shared/pagination/pagination.component';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
import { getEPersonEditRoute } from '../../../access-control-routing-paths';
// todo: optimize imports
/** /**
* Keys to keep track of specific subscriptions * Keys to keep track of specific subscriptions
*/ */
enum SubKey { enum SubKey {
ActiveGroup, ActiveGroup,
MembersDTO, Members,
SearchResultsDTO, SearchResults,
} }
/** /**
@@ -69,7 +94,19 @@ export interface EPersonListActionConfig {
@Component({ @Component({
selector: 'ds-members-list', selector: 'ds-members-list',
templateUrl: './members-list.component.html' templateUrl: './members-list.component.html',
imports: [
TranslateModule,
ContextHelpDirective,
ReactiveFormsModule,
PaginationComponent,
NgIf,
AsyncPipe,
RouterLink,
NgClass,
NgForOf,
],
standalone: true,
}) })
/** /**
* The list of members in the edit group page * The list of members in the edit group page
@@ -89,18 +126,18 @@ export class MembersListComponent implements OnInit, OnDestroy {
remove: { remove: {
css: 'btn-outline-danger', css: 'btn-outline-danger',
disabled: false, disabled: false,
icon: 'fas fa-trash-alt fa-fw' icon: 'fas fa-trash-alt fa-fw',
}, },
}; };
/** /**
* EPeople being displayed in search result, initially all members, after search result of search * EPeople being displayed in search result, initially all members, after search result of search
*/ */
ePeopleSearchDtos: BehaviorSubject<PaginatedList<EpersonDtoModel>> = new BehaviorSubject<PaginatedList<EpersonDtoModel>>(undefined); ePeopleSearch: BehaviorSubject<PaginatedList<EPerson>> = new BehaviorSubject<PaginatedList<EPerson>>(undefined);
/** /**
* List of EPeople members of currently active group being edited * List of EPeople members of currently active group being edited
*/ */
ePeopleMembersOfGroupDtos: BehaviorSubject<PaginatedList<EpersonDtoModel>> = new BehaviorSubject<PaginatedList<EpersonDtoModel>>(undefined); ePeopleMembersOfGroup: BehaviorSubject<PaginatedList<EPerson>> = new BehaviorSubject<PaginatedList<EPerson>>(undefined);
/** /**
* Pagination config used to display the list of EPeople that are result of EPeople search * Pagination config used to display the list of EPeople that are result of EPeople search
@@ -108,7 +145,7 @@ export class MembersListComponent implements OnInit, OnDestroy {
configSearch: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { configSearch: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
id: 'sml', id: 'sml',
pageSize: 5, pageSize: 5,
currentPage: 1 currentPage: 1,
}); });
/** /**
* Pagination config used to display the list of EPerson Membes of active group being edited * Pagination config used to display the list of EPerson Membes of active group being edited
@@ -116,7 +153,7 @@ export class MembersListComponent implements OnInit, OnDestroy {
config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
id: 'ml', id: 'ml',
pageSize: 5, pageSize: 5,
currentPage: 1 currentPage: 1,
}); });
/** /**
@@ -129,7 +166,6 @@ export class MembersListComponent implements OnInit, OnDestroy {
// Current search in edit group - epeople search form // Current search in edit group - epeople search form
currentSearchQuery: string; currentSearchQuery: string;
currentSearchScope: string;
// Whether or not user has done a EPeople search yet // Whether or not user has done a EPeople search yet
searchDone: boolean; searchDone: boolean;
@@ -137,6 +173,8 @@ export class MembersListComponent implements OnInit, OnDestroy {
// current active group being edited // current active group being edited
groupBeingEdited: Group; groupBeingEdited: Group;
readonly getEPersonEditRoute = getEPersonEditRoute;
constructor( constructor(
protected groupDataService: GroupDataService, protected groupDataService: GroupDataService,
public ePersonDataService: EPersonDataService, public ePersonDataService: EPersonDataService,
@@ -148,18 +186,17 @@ export class MembersListComponent implements OnInit, OnDestroy {
public dsoNameService: DSONameService, public dsoNameService: DSONameService,
) { ) {
this.currentSearchQuery = ''; this.currentSearchQuery = '';
this.currentSearchScope = 'metadata';
} }
ngOnInit(): void { ngOnInit(): void {
this.searchForm = this.formBuilder.group(({ this.searchForm = this.formBuilder.group(({
scope: 'metadata',
query: '', query: '',
})); }));
this.subs.set(SubKey.ActiveGroup, this.groupDataService.getActiveGroup().subscribe((activeGroup: Group) => { this.subs.set(SubKey.ActiveGroup, this.groupDataService.getActiveGroup().subscribe((activeGroup: Group) => {
if (activeGroup != null) { if (activeGroup != null) {
this.groupBeingEdited = activeGroup; this.groupBeingEdited = activeGroup;
this.retrieveMembers(this.config.currentPage); this.retrieveMembers(this.config.currentPage);
this.search({ query: '' });
} }
})); }));
} }
@@ -171,14 +208,14 @@ export class MembersListComponent implements OnInit, OnDestroy {
* @private * @private
*/ */
retrieveMembers(page: number): void { retrieveMembers(page: number): void {
this.unsubFrom(SubKey.MembersDTO); this.unsubFrom(SubKey.Members);
this.subs.set(SubKey.MembersDTO, this.subs.set(SubKey.Members,
this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
switchMap((currentPagination) => { switchMap((currentPagination) => {
return this.ePersonDataService.findListByHref(this.groupBeingEdited._links.epersons.href, { return this.ePersonDataService.findListByHref(this.groupBeingEdited._links.epersons.href, {
currentPage: currentPagination.currentPage, currentPage: currentPagination.currentPage,
elementsPerPage: currentPagination.pageSize elementsPerPage: currentPagination.pageSize,
} },
); );
}), }),
getAllCompletedRemoteData(), getAllCompletedRemoteData(),
@@ -189,46 +226,9 @@ export class MembersListComponent implements OnInit, OnDestroy {
return rd; return rd;
} }
}), }),
switchMap((epersonListRD: RemoteData<PaginatedList<EPerson>>) => { getRemoteDataPayload())
const dtos$ = observableCombineLatest([...epersonListRD.payload.page.map((member: EPerson) => { .subscribe((paginatedListOfEPersons: PaginatedList<EPerson>) => {
const dto$: Observable<EpersonDtoModel> = observableCombineLatest( this.ePeopleMembersOfGroup.next(paginatedListOfEPersons);
this.isMemberOfGroup(member), (isMember: ObservedValueOf<Observable<boolean>>) => {
const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel();
epersonDtoModel.eperson = member;
epersonDtoModel.memberOfGroup = isMember;
return epersonDtoModel;
});
return dto$;
})]);
return dtos$.pipe(defaultIfEmpty([]), map((dtos: EpersonDtoModel[]) => {
return buildPaginatedList(epersonListRD.payload.pageInfo, dtos);
}));
}))
.subscribe((paginatedListOfDTOs: PaginatedList<EpersonDtoModel>) => {
this.ePeopleMembersOfGroupDtos.next(paginatedListOfDTOs);
}));
}
/**
* Whether the given ePerson is a member of the group currently being edited
* @param possibleMember EPerson that is a possible member (being tested) of the group currently being edited
*/
isMemberOfGroup(possibleMember: EPerson): Observable<boolean> {
return this.groupDataService.getActiveGroup().pipe(take(1),
mergeMap((group: Group) => {
if (group != null) {
return this.ePersonDataService.findListByHref(group._links.epersons.href, {
currentPage: 1,
elementsPerPage: 9999
})
.pipe(
getFirstSucceededRemoteData(),
getRemoteDataPayload(),
map((listEPeopleInGroup: PaginatedList<EPerson>) => listEPeopleInGroup.page.filter((ePersonInList: EPerson) => ePersonInList.id === possibleMember.id)),
map((epeople: EPerson[]) => epeople.length > 0));
} else {
return observableOf(false);
}
})); }));
} }
@@ -248,14 +248,18 @@ export class MembersListComponent implements OnInit, OnDestroy {
/** /**
* Deletes a given EPerson from the members list of the group currently being edited * Deletes a given EPerson from the members list of the group currently being edited
* @param ePerson EPerson we want to delete as member from group that is currently being edited * @param eperson EPerson we want to delete as member from group that is currently being edited
*/ */
deleteMemberFromGroup(ePerson: EpersonDtoModel) { deleteMemberFromGroup(eperson: EPerson) {
ePerson.memberOfGroup = false;
this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => {
if (activeGroup != null) { if (activeGroup != null) {
const response = this.groupDataService.deleteMemberFromGroup(activeGroup, ePerson.eperson); const response = this.groupDataService.deleteMemberFromGroup(activeGroup, eperson);
this.showNotifications('deleteMember', response, this.dsoNameService.getName(ePerson.eperson), activeGroup); this.showNotifications('deleteMember', response, this.dsoNameService.getName(eperson), activeGroup);
// Reload search results (if there is an active query).
// This will potentially add this deleted subgroup into the list of search results.
if (this.currentSearchQuery != null) {
this.search({ query: this.currentSearchQuery });
}
} else { } else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup'));
} }
@@ -264,14 +268,18 @@ export class MembersListComponent implements OnInit, OnDestroy {
/** /**
* Adds a given EPerson to the members list of the group currently being edited * Adds a given EPerson to the members list of the group currently being edited
* @param ePerson EPerson we want to add as member to group that is currently being edited * @param eperson EPerson we want to add as member to group that is currently being edited
*/ */
addMemberToGroup(ePerson: EpersonDtoModel) { addMemberToGroup(eperson: EPerson) {
ePerson.memberOfGroup = true;
this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => {
if (activeGroup != null) { if (activeGroup != null) {
const response = this.groupDataService.addMemberToGroup(activeGroup, ePerson.eperson); const response = this.groupDataService.addMemberToGroup(activeGroup, eperson);
this.showNotifications('addMember', response, this.dsoNameService.getName(ePerson.eperson), activeGroup); this.showNotifications('addMember', response, this.dsoNameService.getName(eperson), activeGroup);
// Reload search results (if there is an active query).
// This will potentially add this deleted subgroup into the list of search results.
if (this.currentSearchQuery != null) {
this.search({ query: this.currentSearchQuery });
}
} else { } else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup'));
} }
@@ -279,37 +287,25 @@ export class MembersListComponent implements OnInit, OnDestroy {
} }
/** /**
* Search in the EPeople by name, email or metadata * Search all EPeople who are NOT a member of the current group by name, email or metadata
* @param data Contains scope and query param * @param data Contains query param
*/ */
search(data: any) { search(data: any) {
this.unsubFrom(SubKey.SearchResultsDTO); this.unsubFrom(SubKey.SearchResults);
this.subs.set(SubKey.SearchResultsDTO, this.subs.set(SubKey.SearchResults,
this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe( this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe(
switchMap((paginationOptions) => { switchMap((paginationOptions) => {
const query: string = data.query; const query: string = data.query;
const scope: string = data.scope;
if (query != null && this.currentSearchQuery !== query && this.groupBeingEdited) { if (query != null && this.currentSearchQuery !== query && this.groupBeingEdited) {
this.router.navigate([], {
queryParamsHandling: 'merge'
});
this.currentSearchQuery = query; this.currentSearchQuery = query;
this.paginationService.resetPage(this.configSearch.id); this.paginationService.resetPage(this.configSearch.id);
} }
if (scope != null && this.currentSearchScope !== scope && this.groupBeingEdited) {
this.router.navigate([], {
queryParamsHandling: 'merge'
});
this.currentSearchScope = scope;
this.paginationService.resetPage(this.configSearch.id);
}
this.searchDone = true; this.searchDone = true;
return this.ePersonDataService.searchByScope(this.currentSearchScope, this.currentSearchQuery, { return this.ePersonDataService.searchNonMembers(this.currentSearchQuery, this.groupBeingEdited.id, {
currentPage: paginationOptions.currentPage, currentPage: paginationOptions.currentPage,
elementsPerPage: paginationOptions.pageSize elementsPerPage: paginationOptions.pageSize,
}); }, false, true);
}), }),
getAllCompletedRemoteData(), getAllCompletedRemoteData(),
map((rd: RemoteData<any>) => { map((rd: RemoteData<any>) => {
@@ -319,23 +315,9 @@ export class MembersListComponent implements OnInit, OnDestroy {
return rd; return rd;
} }
}), }),
switchMap((epersonListRD: RemoteData<PaginatedList<EPerson>>) => { getRemoteDataPayload())
const dtos$ = observableCombineLatest([...epersonListRD.payload.page.map((member: EPerson) => { .subscribe((paginatedListOfEPersons: PaginatedList<EPerson>) => {
const dto$: Observable<EpersonDtoModel> = observableCombineLatest( this.ePeopleSearch.next(paginatedListOfEPersons);
this.isMemberOfGroup(member), (isMember: ObservedValueOf<Observable<boolean>>) => {
const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel();
epersonDtoModel.eperson = member;
epersonDtoModel.memberOfGroup = isMember;
return epersonDtoModel;
});
return dto$;
})]);
return dtos$.pipe(defaultIfEmpty([]), map((dtos: EpersonDtoModel[]) => {
return buildPaginatedList(epersonListRD.payload.pageInfo, dtos);
}));
}))
.subscribe((paginatedListOfDTOs: PaginatedList<EpersonDtoModel>) => {
this.ePeopleSearchDtos.next(paginatedListOfDTOs);
})); }));
} }

View File

@@ -1,6 +1,55 @@
<ng-container> <ng-container>
<h3 class="border-bottom pb-2">{{messagePrefix + '.head' | translate}}</h3> <h3 class="border-bottom pb-2">{{messagePrefix + '.head' | translate}}</h3>
<h4>{{messagePrefix + '.headSubgroups' | translate}}</h4>
<ds-pagination *ngIf="(subGroups$ | async)?.payload?.totalElements > 0"
[paginationOptions]="config"
[pageInfoState]="(subGroups$ | async)?.payload"
[collectionSize]="(subGroups$ | async)?.payload?.totalElements"
[hideGear]="true"
[hidePagerWhenSinglePage]="true">
<div class="table-responsive">
<table id="subgroupsOfGroup" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th scope="col" class="align-middle">{{messagePrefix + '.table.id' | translate}}</th>
<th scope="col" class="align-middle">{{messagePrefix + '.table.name' | translate}}</th>
<th scope="col" class="align-middle">{{messagePrefix + '.table.collectionOrCommunity' | translate}}</th>
<th>{{messagePrefix + '.table.edit' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let group of (subGroups$ | async)?.payload?.page">
<td class="align-middle">{{group.id}}</td>
<td class="align-middle">
<a (click)="groupDataService.startEditingNewGroup(group)"
[routerLink]="[groupDataService.getGroupEditPageRouterLink(group)]">
{{ dsoNameService.getName(group) }}
</a>
</td>
<td class="align-middle">{{ dsoNameService.getName((group.object | async)?.payload)}}</td>
<td class="align-middle">
<div class="btn-group edit-field">
<button (click)="deleteSubgroupFromGroup(group)"
class="btn btn-outline-danger btn-sm deleteButton"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(group) } }}">
<i class="fas fa-trash-alt fa-fw"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</ds-pagination>
<div *ngIf="(subGroups$ | async)?.payload?.totalElements === 0" class="alert alert-info w-100 mb-2"
role="alert">
{{messagePrefix + '.no-subgroups-yet' | translate}}
</div>
<h4 id="search" class="border-bottom pb-2"> <h4 id="search" class="border-bottom pb-2">
<span *dsContextHelp="{ <span *dsContextHelp="{
content: 'admin.access-control.groups.form.tooltip.editGroup.addSubgroups', content: 'admin.access-control.groups.form.tooltip.editGroup.addSubgroups',
@@ -62,17 +111,7 @@
<td class="align-middle">{{ dsoNameService.getName((group.object | async)?.payload) }}</td> <td class="align-middle">{{ dsoNameService.getName((group.object | async)?.payload) }}</td>
<td class="align-middle"> <td class="align-middle">
<div class="btn-group edit-field"> <div class="btn-group edit-field">
<button *ngIf="(isSubgroupOfGroup(group) | async) && !(isActiveGroup(group) | async)" <button (click)="addSubgroupToGroup(group)"
(click)="deleteSubgroupFromGroup(group)"
class="btn btn-outline-danger btn-sm deleteButton"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(group) } }}">
<i class="fas fa-trash-alt fa-fw"></i>
</button>
<span *ngIf="(isActiveGroup(group) | async)">{{ messagePrefix + '.table.edit.currentGroup' | translate }}</span>
<button *ngIf="!(isSubgroupOfGroup(group) | async) && !(isActiveGroup(group) | async)"
(click)="addSubgroupToGroup(group)"
class="btn btn-outline-primary btn-sm addButton" class="btn btn-outline-primary btn-sm addButton"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(group) } }}"> title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(group) } }}">
<i class="fas fa-plus fa-fw"></i> <i class="fas fa-plus fa-fw"></i>
@@ -85,58 +124,9 @@
</div> </div>
</ds-pagination> </ds-pagination>
<div *ngIf="(searchResults$ | async)?.payload?.totalElements == 0 && searchDone" class="alert alert-info w-100 mb-2" <div *ngIf="(searchResults$ | async)?.payload?.totalElements === 0 && searchDone" class="alert alert-info w-100 mb-2"
role="alert"> role="alert">
{{messagePrefix + '.no-items' | translate}} {{messagePrefix + '.no-items' | translate}}
</div> </div>
<h4>{{messagePrefix + '.headSubgroups' | translate}}</h4>
<ds-pagination *ngIf="(subGroups$ | async)?.payload?.totalElements > 0"
[paginationOptions]="config"
[pageInfoState]="(subGroups$ | async)?.payload"
[collectionSize]="(subGroups$ | async)?.payload?.totalElements"
[hideGear]="true"
[hidePagerWhenSinglePage]="true">
<div class="table-responsive">
<table id="subgroupsOfGroup" class="table table-striped table-hover table-bordered">
<thead>
<tr>
<th scope="col" class="align-middle">{{messagePrefix + '.table.id' | translate}}</th>
<th scope="col" class="align-middle">{{messagePrefix + '.table.name' | translate}}</th>
<th scope="col" class="align-middle">{{messagePrefix + '.table.collectionOrCommunity' | translate}}</th>
<th>{{messagePrefix + '.table.edit' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let group of (subGroups$ | async)?.payload?.page">
<td class="align-middle">{{group.id}}</td>
<td class="align-middle">
<a (click)="groupDataService.startEditingNewGroup(group)"
[routerLink]="[groupDataService.getGroupEditPageRouterLink(group)]">
{{ dsoNameService.getName(group) }}
</a>
</td>
<td class="align-middle">{{ dsoNameService.getName((group.object | async)?.payload)}}</td>
<td class="align-middle">
<div class="btn-group edit-field">
<button (click)="deleteSubgroupFromGroup(group)"
class="btn btn-outline-danger btn-sm deleteButton"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(group) } }}">
<i class="fas fa-trash-alt fa-fw"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</ds-pagination>
<div *ngIf="(subGroups$ | async)?.payload?.totalElements == 0" class="alert alert-info w-100 mb-2"
role="alert">
{{messagePrefix + '.no-subgroups-yet' | translate}}
</div>
</ng-container> </ng-container>

View File

@@ -1,36 +1,69 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NO_ERRORS_SCHEMA, DebugElement } from '@angular/core'; import {
import { ComponentFixture, fakeAsync, flush, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; DebugElement,
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; NO_ERRORS_SCHEMA,
import { BrowserModule, By } from '@angular/platform-browser'; } from '@angular/core';
import { Router } from '@angular/router'; import {
ComponentFixture,
fakeAsync,
flush,
inject,
TestBed,
waitForAsync,
} from '@angular/core/testing';
import {
FormsModule,
ReactiveFormsModule,
} from '@angular/forms';
import {
BrowserModule,
By,
} from '@angular/platform-browser';
import {
ActivatedRoute,
Router,
} from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import {
import { Observable, of as observableOf, BehaviorSubject } from 'rxjs'; TranslateLoader,
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import {
Observable,
of as observableOf,
} from 'rxjs';
import { EPersonMock2 } from 'src/app/shared/testing/eperson.mock';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
import { RestResponse } from '../../../../core/cache/response.models'; import { RestResponse } from '../../../../core/cache/response.models';
import { buildPaginatedList, PaginatedList } from '../../../../core/data/paginated-list.model'; import {
buildPaginatedList,
PaginatedList,
} from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { GroupDataService } from '../../../../core/eperson/group-data.service'; import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { Group } from '../../../../core/eperson/models/group.model'; import { Group } from '../../../../core/eperson/models/group.model';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { GroupMock, GroupMock2 } from '../../../../shared/testing/group-mock';
import { SubgroupsListComponent } from './subgroups-list.component';
import {
createSuccessfulRemoteDataObject$,
createSuccessfulRemoteDataObject
} from '../../../../shared/remote-data.utils';
import { RouterMock } from '../../../../shared/mocks/router.mock';
import { getMockFormBuilderService } from '../../../../shared/mocks/form-builder-service.mock';
import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock';
import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
import { map } from 'rxjs/operators';
import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationService } from '../../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; import { PageInfo } from '../../../../core/shared/page-info.model';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { ContextHelpDirective } from '../../../../shared/context-help.directive';
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock';
import { getMockFormBuilderService } from '../../../../shared/mocks/form-builder-service.mock';
import { RouterMock } from '../../../../shared/mocks/router.mock';
import { getMockTranslateService } from '../../../../shared/mocks/translate.service.mock';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { PaginationComponent } from '../../../../shared/pagination/pagination.component';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { ActivatedRouteStub } from '../../../../shared/testing/active-router.stub';
import {
GroupMock,
GroupMock2,
} from '../../../../shared/testing/group-mock';
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub';
import { TranslateLoaderMock } from '../../../../shared/testing/translate-loader.mock';
import { SubgroupsListComponent } from './subgroups-list.component';
describe('SubgroupsListComponent', () => { describe('SubgroupsListComponent', () => {
let component: SubgroupsListComponent; let component: SubgroupsListComponent;
@@ -39,44 +72,72 @@ describe('SubgroupsListComponent', () => {
let builderService: FormBuilderService; let builderService: FormBuilderService;
let ePersonDataServiceStub: any; let ePersonDataServiceStub: any;
let groupsDataServiceStub: any; let groupsDataServiceStub: any;
let activeGroup; let activeGroup: Group;
let subgroups: Group[]; let subgroups: Group[];
let allGroups: Group[]; let groupNonMembers: Group[];
let routerStub; let routerStub;
let paginationService; let paginationService;
// Define a new mock activegroup for all tests below
let mockActiveGroup: Group = Object.assign(new Group(), {
handle: null,
subgroups: [GroupMock2],
epersons: [EPersonMock2],
selfRegistered: false,
permanent: false,
_links: {
self: {
href: 'https://rest.api/server/api/eperson/groups/activegroupid',
},
subgroups: { href: 'https://rest.api/server/api/eperson/groups/activegroupid/subgroups' },
object: { href: 'https://rest.api/server/api/eperson/groups/activegroupid/object' },
epersons: { href: 'https://rest.api/server/api/eperson/groups/activegroupid/epersons' },
},
_name: 'activegroupname',
id: 'activegroupid',
uuid: 'activegroupid',
type: 'group',
});
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
activeGroup = GroupMock; activeGroup = mockActiveGroup;
subgroups = [GroupMock2]; subgroups = [GroupMock2];
allGroups = [GroupMock, GroupMock2]; groupNonMembers = [GroupMock];
ePersonDataServiceStub = {}; ePersonDataServiceStub = {};
groupsDataServiceStub = { groupsDataServiceStub = {
activeGroup: activeGroup, activeGroup: activeGroup,
subgroups$: new BehaviorSubject(subgroups), subgroups: subgroups,
groupNonMembers: groupNonMembers,
getActiveGroup(): Observable<Group> { getActiveGroup(): Observable<Group> {
return observableOf(this.activeGroup); return observableOf(this.activeGroup);
}, },
getSubgroups(): Group { getSubgroups(): Group {
return this.activeGroup; return this.subgroups;
}, },
// This method is used to get all the current subgroups
findListByHref(_href: string): Observable<RemoteData<PaginatedList<Group>>> { findListByHref(_href: string): Observable<RemoteData<PaginatedList<Group>>> {
return this.subgroups$.pipe( return createSuccessfulRemoteDataObject$(buildPaginatedList<Group>(new PageInfo(), groupsDataServiceStub.getSubgroups()));
map((currentGroups: Group[]) => {
return createSuccessfulRemoteDataObject(buildPaginatedList<Group>(new PageInfo(), currentGroups));
})
);
}, },
getGroupEditPageRouterLink(group: Group): string { getGroupEditPageRouterLink(group: Group): string {
return '/access-control/groups/' + group.id; return '/access-control/groups/' + group.id;
}, },
searchGroups(query: string): Observable<RemoteData<PaginatedList<Group>>> { // This method is used to get all groups which are NOT currently a subgroup member
searchNonMemberGroups(query: string, group: string): Observable<RemoteData<PaginatedList<Group>>> {
if (query === '') { if (query === '') {
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), allGroups)); return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), groupNonMembers));
} }
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])); return createSuccessfulRemoteDataObject$(
buildPaginatedList(new PageInfo(), []),
);
}, },
addSubGroupToGroup(parentGroup, subgroup: Group): Observable<RestResponse> { addSubGroupToGroup(parentGroup, subgroupToAdd: Group): Observable<RestResponse> {
this.subgroups$.next([...this.subgroups$.getValue(), subgroup]); // Add group to list of subgroups
this.subgroups = [...this.subgroups, subgroupToAdd];
// Remove group from list of non-members
this.groupNonMembers.forEach( (group: Group, index: number) => {
if (group.id === subgroupToAdd.id) {
this.groupNonMembers.splice(index, 1);
}
});
return observableOf(new RestResponse(true, 200, 'Success')); return observableOf(new RestResponse(true, 200, 'Success'));
}, },
clearGroupsRequests() { clearGroupsRequests() {
@@ -85,40 +146,59 @@ describe('SubgroupsListComponent', () => {
clearGroupLinkRequests() { clearGroupLinkRequests() {
// empty // empty
}, },
deleteSubGroupFromGroup(parentGroup, subgroup: Group): Observable<RestResponse> { deleteSubGroupFromGroup(parentGroup, subgroupToDelete: Group): Observable<RestResponse> {
this.subgroups$.next(this.subgroups$.getValue().filter((group: Group) => { // Remove group from list of subgroups
if (group.id !== subgroup.id) { this.subgroups.forEach( (group: Group, index: number) => {
return group; if (group.id === subgroupToDelete.id) {
this.subgroups.splice(index, 1);
} }
})); });
// Add group to list of non-members
this.groupNonMembers = [...this.groupNonMembers, subgroupToDelete];
return observableOf(new RestResponse(true, 200, 'Success')); return observableOf(new RestResponse(true, 200, 'Success'));
} },
}; };
routerStub = new RouterMock(); routerStub = new RouterMock();
builderService = getMockFormBuilderService(); builderService = getMockFormBuilderService();
translateService = getMockTranslateService(); translateService = getMockTranslateService();
paginationService = new PaginationServiceStub(); paginationService = new PaginationServiceStub();
TestBed.configureTestingModule({ return TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, imports: [
CommonModule,
NgbModule,
FormsModule,
ReactiveFormsModule,
BrowserModule,
// ContextHelpDirective,
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
provide: TranslateLoader, provide: TranslateLoader,
useClass: TranslateLoaderMock useClass: TranslateLoaderMock,
} },
}), }),
SubgroupsListComponent,
], ],
declarations: [SubgroupsListComponent], providers: [
providers: [SubgroupsListComponent, SubgroupsListComponent,
{ provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: GroupDataService, useValue: groupsDataServiceStub }, { provide: GroupDataService, useValue: groupsDataServiceStub },
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }, {
provide: NotificationsService,
useValue: new NotificationsServiceStub(),
},
{ provide: FormBuilderService, useValue: builderService }, { provide: FormBuilderService, useValue: builderService },
{ provide: Router, useValue: routerStub }, { provide: Router, useValue: routerStub },
{ provide: PaginationService, useValue: paginationService }, { provide: PaginationService, useValue: paginationService },
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA],
}).compileComponents(); })
.overrideComponent(SubgroupsListComponent, {
remove: {
imports: [ContextHelpDirective, PaginationComponent],
},
})
.compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
@@ -137,86 +217,78 @@ describe('SubgroupsListComponent', () => {
expect(comp).toBeDefined(); expect(comp).toBeDefined();
})); }));
describe('current subgroup list', () => {
it('should show list of subgroups of current active group', () => { it('should show list of subgroups of current active group', () => {
const groupIdsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tr td:first-child')); const groupIdsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tr td:first-child'));
expect(groupIdsFound.length).toEqual(1); expect(groupIdsFound.length).toEqual(1);
activeGroup.subgroups.map((group: Group) => { subgroups.map((group: Group) => {
expect(groupIdsFound.find((foundEl) => { expect(groupIdsFound.find((foundEl) => {
return (foundEl.nativeElement.textContent.trim() === group.uuid); return (foundEl.nativeElement.textContent.trim() === group.uuid);
})).toBeTruthy(); })).toBeTruthy();
}); });
}); });
it('should show a delete button next to each subgroup', () => {
const subgroupsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tbody tr'));
subgroupsFound.map((foundGroupRowElement: DebugElement) => {
const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus'));
const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt'));
expect(addButton).toBeNull();
expect(deleteButton).not.toBeNull();
});
});
describe('if first group delete button is pressed', () => { describe('if first group delete button is pressed', () => {
let groupsFound: DebugElement[]; let groupsFound: DebugElement[];
beforeEach(fakeAsync(() => { beforeEach(() => {
const addButton = fixture.debugElement.query(By.css('#subgroupsOfGroup tbody .deleteButton')); const deleteButton = fixture.debugElement.query(By.css('#subgroupsOfGroup tbody .deleteButton'));
addButton.triggerEventHandler('click', { deleteButton.nativeElement.click();
preventDefault: () => {/**/
}
});
tick();
fixture.detectChanges(); fixture.detectChanges();
})); });
it('one less subgroup in list from 1 to 0 (of 2 total groups)', () => { it('then no subgroup remains as a member of the active group', () => {
groupsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tbody tr')); groupsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tbody tr'));
expect(groupsFound.length).toEqual(0); expect(groupsFound.length).toEqual(0);
}); });
}); });
});
describe('search', () => { describe('search', () => {
describe('when searching with empty query', () => { describe('when searching with empty query', () => {
let groupsFound: DebugElement[]; let groupsFound: DebugElement[];
beforeEach(fakeAsync(() => { beforeEach(fakeAsync(() => {
component.search({ query: '' }); component.search({ query: '' });
fixture.detectChanges();
groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr')); groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr'));
})); }));
it('should display all groups', () => { it('should display only non-member groups (i.e. groups that are not a subgroup)', () => {
fixture.detectChanges();
groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr'));
expect(groupsFound.length).toEqual(2);
groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr'));
const groupIdsFound: DebugElement[] = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr td:first-child')); const groupIdsFound: DebugElement[] = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr td:first-child'));
allGroups.map((group: Group) => { expect(groupIdsFound.length).toEqual(1);
groupNonMembers.map((group: Group) => {
expect(groupIdsFound.find((foundEl: DebugElement) => { expect(groupIdsFound.find((foundEl: DebugElement) => {
return (foundEl.nativeElement.textContent.trim() === group.uuid); return (foundEl.nativeElement.textContent.trim() === group.uuid);
})).toBeTruthy(); })).toBeTruthy();
}); });
}); });
describe('if group is already a subgroup', () => { it('should display an add button next to non-member groups, not a delete button', () => {
it('should have delete button, else it should have add button', () => {
fixture.detectChanges();
groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr'));
const getSubgroups = groupsDataServiceStub.getSubgroups().subgroups;
if (getSubgroups !== undefined && getSubgroups.length > 0) {
groupsFound.map((foundGroupRowElement: DebugElement) => { groupsFound.map((foundGroupRowElement: DebugElement) => {
const groupId: DebugElement = foundGroupRowElement.query(By.css('td:first-child'));
const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus')); const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus'));
const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt')); const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt'));
expect(addButton).toBeNull();
if (activeGroup.id === groupId.nativeElement.textContent) {
expect(deleteButton).toBeNull();
} else {
expect(deleteButton).not.toBeNull();
}
});
} else {
const subgroupIds: string[] = activeGroup.subgroups.map((group: Group) => group.id);
groupsFound.map((foundGroupRowElement: DebugElement) => {
const groupId: DebugElement = foundGroupRowElement.query(By.css('td:first-child'));
const addButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-plus'));
const deleteButton: DebugElement = foundGroupRowElement.query(By.css('td:last-child .fa-trash-alt'));
if (subgroupIds.includes(groupId.nativeElement.textContent)) {
expect(addButton).toBeNull();
expect(deleteButton).not.toBeNull();
} else {
expect(deleteButton).toBeNull();
expect(addButton).not.toBeNull(); expect(addButton).not.toBeNull();
} expect(deleteButton).toBeNull();
}); });
} });
describe('if first add button is pressed', () => {
beforeEach(() => {
const addButton: DebugElement = fixture.debugElement.query(By.css('#groupsSearch tbody .fa-plus'));
addButton.nativeElement.click();
fixture.detectChanges();
});
it('then all (two) Groups are subgroups of the active group. No non-members left', () => {
groupsFound = fixture.debugElement.queryAll(By.css('#groupsSearch tbody tr'));
expect(groupsFound.length).toEqual(0);
}); });
}); });
}); });

View File

@@ -1,24 +1,54 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import {
import { UntypedFormBuilder } from '@angular/forms'; AsyncPipe,
import { Router } from '@angular/router'; NgForOf,
import { TranslateService } from '@ngx-translate/core'; NgIf,
import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs'; } from '@angular/common';
import { map, mergeMap, switchMap, take } from 'rxjs/operators'; import {
Component,
Input,
OnDestroy,
OnInit,
} from '@angular/core';
import {
ReactiveFormsModule,
UntypedFormBuilder,
} from '@angular/forms';
import {
Router,
RouterLink,
} from '@angular/router';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import {
BehaviorSubject,
Observable,
Subscription,
} from 'rxjs';
import {
map,
switchMap,
take,
} from 'rxjs/operators';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
import { PaginatedList } from '../../../../core/data/paginated-list.model'; import { PaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { GroupDataService } from '../../../../core/eperson/group-data.service'; import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { Group } from '../../../../core/eperson/models/group.model'; import { Group } from '../../../../core/eperson/models/group.model';
import {
getFirstCompletedRemoteData,
getFirstSucceededRemoteData,
getRemoteDataPayload
} from '../../../../core/shared/operators';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
import { NoContent } from '../../../../core/shared/NoContent.model';
import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationService } from '../../../../core/pagination/pagination.service';
import { NoContent } from '../../../../core/shared/NoContent.model';
import {
getAllCompletedRemoteData,
getFirstCompletedRemoteData,
} from '../../../../core/shared/operators';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { ContextHelpDirective } from '../../../../shared/context-help.directive';
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
import { PaginationComponent } from '../../../../shared/pagination/pagination.component';
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
import { followLink } from '../../../../shared/utils/follow-link-config.model'; import { followLink } from '../../../../shared/utils/follow-link-config.model';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
/** /**
* Keys to keep track of specific subscriptions * Keys to keep track of specific subscriptions
@@ -31,7 +61,18 @@ enum SubKey {
@Component({ @Component({
selector: 'ds-subgroups-list', selector: 'ds-subgroups-list',
templateUrl: './subgroups-list.component.html' templateUrl: './subgroups-list.component.html',
imports: [
RouterLink,
AsyncPipe,
NgForOf,
ContextHelpDirective,
TranslateModule,
ReactiveFormsModule,
PaginationComponent,
NgIf,
],
standalone: true,
}) })
/** /**
* The list of subgroups in the edit group page * The list of subgroups in the edit group page
@@ -50,6 +91,8 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
*/ */
subGroups$: BehaviorSubject<RemoteData<PaginatedList<Group>>> = new BehaviorSubject(undefined); subGroups$: BehaviorSubject<RemoteData<PaginatedList<Group>>> = new BehaviorSubject(undefined);
subGroupsPageInfoState$: Observable<PageInfo>;
/** /**
* Map of active subscriptions * Map of active subscriptions
*/ */
@@ -61,7 +104,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
configSearch: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { configSearch: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
id: 'ssgl', id: 'ssgl',
pageSize: 5, pageSize: 5,
currentPage: 1 currentPage: 1,
}); });
/** /**
* Pagination config used to display the list of subgroups of currently active group being edited * Pagination config used to display the list of subgroups of currently active group being edited
@@ -69,7 +112,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
id: 'sgl', id: 'sgl',
pageSize: 5, pageSize: 5,
currentPage: 1 currentPage: 1,
}); });
// The search form // The search form
@@ -103,8 +146,12 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
if (activeGroup != null) { if (activeGroup != null) {
this.groupBeingEdited = activeGroup; this.groupBeingEdited = activeGroup;
this.retrieveSubGroups(); this.retrieveSubGroups();
this.search({ query: '' });
} }
})); }));
this.subGroupsPageInfoState$ = this.subGroups$.pipe(
map(subGroupsRD => subGroupsRD?.payload?.pageInfo),
);
} }
/** /**
@@ -120,58 +167,17 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
this.paginationService.getCurrentPagination(this.config.id, this.config).pipe( this.paginationService.getCurrentPagination(this.config.id, this.config).pipe(
switchMap((config) => this.groupDataService.findListByHref(this.groupBeingEdited._links.subgroups.href, { switchMap((config) => this.groupDataService.findListByHref(this.groupBeingEdited._links.subgroups.href, {
currentPage: config.currentPage, currentPage: config.currentPage,
elementsPerPage: config.pageSize elementsPerPage: config.pageSize,
}, },
true, true,
true, true,
followLink('object') followLink('object'),
)) )),
).subscribe((rd: RemoteData<PaginatedList<Group>>) => { ).subscribe((rd: RemoteData<PaginatedList<Group>>) => {
this.subGroups$.next(rd); this.subGroups$.next(rd);
})); }));
} }
/**
* Whether or not the given group is a subgroup of the group currently being edited
* @param possibleSubgroup Group that is a possible subgroup (being tested) of the group currently being edited
*/
isSubgroupOfGroup(possibleSubgroup: Group): Observable<boolean> {
return this.groupDataService.getActiveGroup().pipe(take(1),
mergeMap((activeGroup: Group) => {
if (activeGroup != null) {
if (activeGroup.uuid === possibleSubgroup.uuid) {
return observableOf(false);
} else {
return this.groupDataService.findListByHref(activeGroup._links.subgroups.href, {
currentPage: 1,
elementsPerPage: 9999
})
.pipe(
getFirstSucceededRemoteData(),
getRemoteDataPayload(),
map((listTotalGroups: PaginatedList<Group>) => listTotalGroups.page.filter((groupInList: Group) => groupInList.id === possibleSubgroup.id)),
map((groups: Group[]) => groups.length > 0));
}
} else {
return observableOf(false);
}
}));
}
/**
* Whether or not the given group is the current group being edited
* @param group Group that is possibly the current group being edited
*/
isActiveGroup(group: Group): Observable<boolean> {
return this.groupDataService.getActiveGroup().pipe(take(1),
mergeMap((activeGroup: Group) => {
if (activeGroup != null && activeGroup.uuid === group.uuid) {
return observableOf(true);
}
return observableOf(false);
}));
}
/** /**
* Deletes given subgroup from the group currently being edited * Deletes given subgroup from the group currently being edited
* @param subgroup Group we want to delete from the subgroups of the group currently being edited * @param subgroup Group we want to delete from the subgroups of the group currently being edited
@@ -181,6 +187,11 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
if (activeGroup != null) { if (activeGroup != null) {
const response = this.groupDataService.deleteSubGroupFromGroup(activeGroup, subgroup); const response = this.groupDataService.deleteSubGroupFromGroup(activeGroup, subgroup);
this.showNotifications('deleteSubgroup', response, this.dsoNameService.getName(subgroup), activeGroup); this.showNotifications('deleteSubgroup', response, this.dsoNameService.getName(subgroup), activeGroup);
// Reload search results (if there is an active query).
// This will potentially add this deleted subgroup into the list of search results.
if (this.currentSearchQuery != null) {
this.search({ query: this.currentSearchQuery });
}
} else { } else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup'));
} }
@@ -197,6 +208,11 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
if (activeGroup.uuid !== subgroup.uuid) { if (activeGroup.uuid !== subgroup.uuid) {
const response = this.groupDataService.addSubGroupToGroup(activeGroup, subgroup); const response = this.groupDataService.addSubGroupToGroup(activeGroup, subgroup);
this.showNotifications('addSubgroup', response, this.dsoNameService.getName(subgroup), activeGroup); this.showNotifications('addSubgroup', response, this.dsoNameService.getName(subgroup), activeGroup);
// Reload search results (if there is an active query).
// This will potentially remove this added subgroup from search results.
if (this.currentSearchQuery != null) {
this.search({ query: this.currentSearchQuery });
}
} else { } else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.subgroupToAddIsActiveGroup')); this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.subgroupToAddIsActiveGroup'));
} }
@@ -207,26 +223,36 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
} }
/** /**
* Search in the groups (searches by group name and by uuid exact match) * Search all non-member groups (searches by group name and by uuid exact match). Used to search for
* groups that could be added to current group as a subgroup.
* @param data Contains query param * @param data Contains query param
*/ */
search(data: any) { search(data: any) {
this.unsubFrom(SubKey.SearchResults);
this.subs.set(SubKey.SearchResults,
this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe(
switchMap((paginationOptions) => {
const query: string = data.query; const query: string = data.query;
if (query != null && this.currentSearchQuery !== query) { if (query != null && this.currentSearchQuery !== query && this.groupBeingEdited) {
this.router.navigateByUrl(this.groupDataService.getGroupEditPageRouterLink(this.groupBeingEdited));
this.currentSearchQuery = query; this.currentSearchQuery = query;
this.configSearch.currentPage = 1; this.paginationService.resetPage(this.configSearch.id);
} }
this.searchDone = true; this.searchDone = true;
this.unsubFrom(SubKey.SearchResults); return this.groupDataService.searchNonMemberGroups(this.currentSearchQuery, this.groupBeingEdited.id, {
this.subs.set(SubKey.SearchResults, this.paginationService.getCurrentPagination(this.configSearch.id, this.configSearch).pipe( currentPage: paginationOptions.currentPage,
switchMap((config) => this.groupDataService.searchGroups(this.currentSearchQuery, { elementsPerPage: paginationOptions.pageSize,
currentPage: config.currentPage, }, false, true, followLink('object'));
elementsPerPage: config.pageSize }),
}, true, true, followLink('object') getAllCompletedRemoteData(),
)) map((rd: RemoteData<any>) => {
).subscribe((rd: RemoteData<PaginatedList<Group>>) => { if (rd.hasFailed) {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure', { cause: rd.errorMessage }));
} else {
return rd;
}
}))
.subscribe((rd: RemoteData<PaginatedList<Group>>) => {
this.searchResults$.next(rd); this.searchResults$.next(rd);
})); }));
} }

View File

@@ -1,10 +1,13 @@
import { AbstractControl, ValidationErrors } from '@angular/forms'; import {
AbstractControl,
ValidationErrors,
} from '@angular/forms';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { GroupDataService } from '../../../../core/eperson/group-data.service'; import { GroupDataService } from '../../../../core/eperson/group-data.service';
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
import { Group } from '../../../../core/eperson/models/group.model'; import { Group } from '../../../../core/eperson/models/group.model';
import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators';
export class ValidateGroupExists { export class ValidateGroupExists {
@@ -17,7 +20,7 @@ export class ValidateGroupExists {
return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => { return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
return groupDataService.searchGroups(control.value, { return groupDataService.searchGroups(control.value, {
currentPage: 1, currentPage: 1,
elementsPerPage: 100 elementsPerPage: 100,
}) })
.pipe( .pipe(
getFirstSucceededRemoteListPayload(), getFirstSucceededRemoteListPayload(),

View File

@@ -1,10 +1,14 @@
import { GroupPageGuard } from './group-page.guard'; import {
import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; ActivatedRouteSnapshot,
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; Router,
import { ActivatedRouteSnapshot, Router } from '@angular/router'; } from '@angular/router';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { AuthService } from '../../core/auth/auth.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { GroupPageGuard } from './group-page.guard';
describe('GroupPageGuard', () => { describe('GroupPageGuard', () => {
const groupsEndpointUrl = 'https://test.org/api/eperson/groups'; const groupsEndpointUrl = 'https://test.org/api/eperson/groups';
@@ -13,7 +17,7 @@ describe('GroupPageGuard', () => {
const routeSnapshotWithGroupId = { const routeSnapshotWithGroupId = {
params: { params: {
groupId: groupUuid, groupId: groupUuid,
} },
} as unknown as ActivatedRouteSnapshot; } as unknown as ActivatedRouteSnapshot;
let guard: GroupPageGuard; let guard: GroupPageGuard;
@@ -50,10 +54,10 @@ describe('GroupPageGuard', () => {
it('should return true', (done) => { it('should return true', (done) => {
guard.canActivate( guard.canActivate(
routeSnapshotWithGroupId, { url: 'current-url'} as any routeSnapshotWithGroupId, { url: 'current-url' } as any,
).subscribe((result) => { ).subscribe((result) => {
expect(authorizationService.isAuthorized).toHaveBeenCalledWith( expect(authorizationService.isAuthorized).toHaveBeenCalledWith(
FeatureID.CanManageGroup, groupEndpointUrl, undefined FeatureID.CanManageGroup, groupEndpointUrl, undefined,
); );
expect(result).toBeTrue(); expect(result).toBeTrue();
done(); done();
@@ -68,10 +72,10 @@ describe('GroupPageGuard', () => {
it('should not return true', (done) => { it('should not return true', (done) => {
guard.canActivate( guard.canActivate(
routeSnapshotWithGroupId, { url: 'current-url'} as any routeSnapshotWithGroupId, { url: 'current-url' } as any,
).subscribe((result) => { ).subscribe((result) => {
expect(authorizationService.isAuthorized).toHaveBeenCalledWith( expect(authorizationService.isAuthorized).toHaveBeenCalledWith(
FeatureID.CanManageGroup, groupEndpointUrl, undefined FeatureID.CanManageGroup, groupEndpointUrl, undefined,
); );
expect(result).not.toBeTrue(); expect(result).not.toBeTrue();
done(); done();

View File

@@ -1,15 +1,23 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import {
import { Observable, of as observableOf } from 'rxjs'; ActivatedRouteSnapshot,
import { FeatureID } from '../../core/data/feature-authorization/feature-id'; Router,
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; RouterStateSnapshot,
import { AuthService } from '../../core/auth/auth.service'; } from '@angular/router';
import { SomeFeatureAuthorizationGuard } from '../../core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard'; import {
import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; Observable,
of as observableOf,
} from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { AuthService } from '../../core/auth/auth.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { SomeFeatureAuthorizationGuard } from '../../core/data/feature-authorization/feature-authorization-guard/some-feature-authorization.guard';
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root',
}) })
export class GroupPageGuard extends SomeFeatureAuthorizationGuard { export class GroupPageGuard extends SomeFeatureAuthorizationGuard {
@@ -28,7 +36,7 @@ export class GroupPageGuard extends SomeFeatureAuthorizationGuard {
getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> { getObjectUrl(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<string> {
return this.halEndpointService.getEndpoint(this.groupsEndpoint).pipe( return this.halEndpointService.getEndpoint(this.groupsEndpoint).pipe(
map(groupsUrl => `${groupsUrl}/${route?.params?.groupId}`) map(groupsUrl => `${groupsUrl}/${route?.params?.groupId}`),
); );
} }

View File

@@ -1,5 +1,6 @@
/* eslint-disable max-classes-per-file */ /* eslint-disable max-classes-per-file */
import { Action } from '@ngrx/store'; import { Action } from '@ngrx/store';
import { Group } from '../../core/eperson/models/group.model'; import { Group } from '../../core/eperson/models/group.model';
import { type } from '../../shared/ngrx/type'; import { type } from '../../shared/ngrx/type';

View File

@@ -1,6 +1,12 @@
import { GroupMock } from '../../shared/testing/group-mock'; import { GroupMock } from '../../shared/testing/group-mock';
import { GroupRegistryCancelGroupAction, GroupRegistryEditGroupAction } from './group-registry.actions'; import {
import { groupRegistryReducer, GroupRegistryState } from './group-registry.reducers'; GroupRegistryCancelGroupAction,
GroupRegistryEditGroupAction,
} from './group-registry.actions';
import {
groupRegistryReducer,
GroupRegistryState,
} from './group-registry.reducers';
const initialState: GroupRegistryState = { const initialState: GroupRegistryState = {
editGroup: null, editGroup: null,

View File

@@ -1,5 +1,9 @@
import { Group } from '../../core/eperson/models/group.model'; import { Group } from '../../core/eperson/models/group.model';
import { GroupRegistryAction, GroupRegistryActionTypes, GroupRegistryEditGroupAction } from './group-registry.actions'; import {
GroupRegistryAction,
GroupRegistryActionTypes,
GroupRegistryEditGroupAction,
} from './group-registry.actions';
/** /**
* The metadata registry state. * The metadata registry state.
@@ -27,13 +31,13 @@ export function groupRegistryReducer(state = initialState, action: GroupRegistry
case GroupRegistryActionTypes.EDIT_GROUP: { case GroupRegistryActionTypes.EDIT_GROUP: {
return Object.assign({}, state, { return Object.assign({}, state, {
editGroup: (action as GroupRegistryEditGroupAction).group editGroup: (action as GroupRegistryEditGroupAction).group,
}); });
} }
case GroupRegistryActionTypes.CANCEL_EDIT_GROUP: { case GroupRegistryActionTypes.CANCEL_EDIT_GROUP: {
return Object.assign({}, state, { return Object.assign({}, state, {
editGroup: null editGroup: null,
}); });
} }

View File

@@ -2,17 +2,17 @@
<div class="groups-registry row"> <div class="groups-registry row">
<div class="col-12"> <div class="col-12">
<div class="d-flex justify-content-between border-bottom mb-3"> <div class="d-flex justify-content-between border-bottom mb-3">
<h2 id="header" class="pb-2">{{messagePrefix + 'head' | translate}}</h2> <h1 id="header" class="pb-2">{{messagePrefix + 'head' | translate}}</h1>
<div> <div>
<button class="mr-auto btn btn-success" <button class="mr-auto btn btn-success"
[routerLink]="['newGroup']"> [routerLink]="'create'">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
<span class="d-none d-sm-inline ml-1">{{messagePrefix + 'button.add' | translate}}</span> <span class="d-none d-sm-inline ml-1">{{messagePrefix + 'button.add' | translate}}</span>
</button> </button>
</div> </div>
</div> </div>
<h3 id="search" class="border-bottom pb-2">{{messagePrefix + 'search.head' | translate}}</h3> <h2 id="search" class="border-bottom pb-2">{{messagePrefix + 'search.head' | translate}}</h2>
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between"> <form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
<div class="flex-grow-1 mr-3"> <div class="flex-grow-1 mr-3">
<div class="form-group input-group"> <div class="form-group input-group">
@@ -35,7 +35,7 @@
<ds-themed-loading *ngIf="loading$ | async"></ds-themed-loading> <ds-themed-loading *ngIf="loading$ | async"></ds-themed-loading>
<ds-pagination <ds-pagination
*ngIf="(pageInfoState$ | async)?.totalElements > 0 && !(loading$ | async)" *ngIf="(pageInfoState$ | async)?.totalElements > 0 && (loading$ | async) !== true"
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="pageInfoState$" [pageInfoState]="pageInfoState$"
[collectionSize]="(pageInfoState$ | async)?.totalElements" [collectionSize]="(pageInfoState$ | async)?.totalElements"
@@ -91,7 +91,7 @@
</div> </div>
</ds-pagination> </ds-pagination>
<div *ngIf="(pageInfoState$ | async)?.totalElements == 0" class="alert alert-info w-100 mb-2" role="alert"> <div *ngIf="(pageInfoState$ | async)?.totalElements === 0" class="alert alert-info w-100 mb-2" role="alert">
{{messagePrefix + 'no-items' | translate}} {{messagePrefix + 'no-items' | translate}}
</div> </div>

View File

@@ -1,39 +1,79 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync } from '@angular/core/testing'; import {
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; ComponentFixture,
import { BrowserModule, By } from '@angular/platform-browser'; fakeAsync,
import { Router } from '@angular/router'; inject,
TestBed,
tick,
waitForAsync,
} from '@angular/core/testing';
import {
FormsModule,
ReactiveFormsModule,
} from '@angular/forms';
import {
BrowserModule,
By,
} from '@angular/platform-browser';
import {
ActivatedRoute,
Router,
} from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { provideMockStore } from '@ngrx/store/testing';
import { Observable, of as observableOf } from 'rxjs'; import {
TranslateLoader,
TranslateModule,
} from '@ngx-translate/core';
import {
Observable,
of as observableOf,
of,
} from 'rxjs';
import { APP_DATA_SERVICES_MAP } from '../../../config/app-config.interface';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import {
buildPaginatedList,
PaginatedList,
} from '../../core/data/paginated-list.model';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { RequestService } from '../../core/data/request.service'; import { RequestService } from '../../core/data/request.service';
import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../core/eperson/group-data.service'; import { GroupDataService } from '../../core/eperson/group-data.service';
import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPerson } from '../../core/eperson/models/eperson.model';
import { Group } from '../../core/eperson/models/group.model'; import { Group } from '../../core/eperson/models/group.model';
import { PaginationService } from '../../core/pagination/pagination.service';
import { RouteService } from '../../core/services/route.service'; import { RouteService } from '../../core/services/route.service';
import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { PageInfo } from '../../core/shared/page-info.model';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { GroupMock, GroupMock2 } from '../../shared/testing/group-mock';
import { GroupsRegistryComponent } from './groups-registry.component';
import { EPersonMock, EPersonMock2 } from '../../shared/testing/eperson.mock';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { TranslateLoaderMock } from '../../shared/testing/translate-loader.mock';
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { routeServiceStub } from '../../shared/testing/route-service.stub';
import { RouterMock } from '../../shared/mocks/router.mock';
import { PaginationService } from '../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { NoContent } from '../../core/shared/NoContent.model'; import { NoContent } from '../../core/shared/NoContent.model';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { PageInfo } from '../../core/shared/page-info.model';
import { DSONameServiceMock, UNDEFINED_NAME } from '../../shared/mocks/dso-name.service.mock'; import {
DSONameServiceMock,
UNDEFINED_NAME,
} from '../../shared/mocks/dso-name.service.mock';
import { RouterMock } from '../../shared/mocks/router.mock';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
import {
EPersonMock,
EPersonMock2,
} from '../../shared/testing/eperson.mock';
import {
GroupMock,
GroupMock2,
} from '../../shared/testing/group-mock';
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
import { routeServiceStub } from '../../shared/testing/route-service.stub';
import { TranslateLoaderMock } from '../../shared/testing/translate-loader.mock';
import { GroupsRegistryComponent } from './groups-registry.component';
describe('GroupsRegistryComponent', () => { describe('GroupsRegistryComponent', () => {
let component: GroupsRegistryComponent; let component: GroupsRegistryComponent;
@@ -42,6 +82,7 @@ describe('GroupsRegistryComponent', () => {
let groupsDataServiceStub: any; let groupsDataServiceStub: any;
let dsoDataServiceStub: any; let dsoDataServiceStub: any;
let authorizationService: AuthorizationDataService; let authorizationService: AuthorizationDataService;
let configurationDataService: jasmine.SpyObj<ConfigurationDataService>;
let mockGroups; let mockGroups;
let mockEPeople; let mockEPeople;
@@ -78,24 +119,24 @@ describe('GroupsRegistryComponent', () => {
elementsPerPage: 1, elementsPerPage: 1,
totalElements: 0, totalElements: 0,
totalPages: 0, totalPages: 0,
currentPage: 1 currentPage: 1,
}), [])); }), []));
case 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid/epersons': case 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid/epersons':
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo({ return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo({
elementsPerPage: 1, elementsPerPage: 1,
totalElements: 1, totalElements: 1,
totalPages: 1, totalPages: 1,
currentPage: 1 currentPage: 1,
}), [EPersonMock])); }), [EPersonMock]));
default: default:
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo({ return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo({
elementsPerPage: 1, elementsPerPage: 1,
totalElements: 0, totalElements: 0,
totalPages: 0, totalPages: 0,
currentPage: 1 currentPage: 1,
}), [])); }), []));
} }
} },
}; };
groupsDataServiceStub = { groupsDataServiceStub = {
allGroups: mockGroups, allGroups: mockGroups,
@@ -106,21 +147,21 @@ describe('GroupsRegistryComponent', () => {
elementsPerPage: 1, elementsPerPage: 1,
totalElements: 0, totalElements: 0,
totalPages: 0, totalPages: 0,
currentPage: 1 currentPage: 1,
}), [])); }), []));
case 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid/groups': case 'https://dspace.4science.it/dspace-spring-rest/api/eperson/groups/testgroupid/groups':
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo({ return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo({
elementsPerPage: 1, elementsPerPage: 1,
totalElements: 1, totalElements: 1,
totalPages: 1, totalPages: 1,
currentPage: 1 currentPage: 1,
}), [GroupMock2])); }), [GroupMock2]));
default: default:
return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo({ return createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo({
elementsPerPage: 1, elementsPerPage: 1,
totalElements: 0, totalElements: 0,
totalPages: 0, totalPages: 0,
currentPage: 1 currentPage: 1,
}), [])); }), []));
} }
}, },
@@ -136,7 +177,7 @@ describe('GroupsRegistryComponent', () => {
elementsPerPage: this.allGroups.length, elementsPerPage: this.allGroups.length,
totalElements: this.allGroups.length, totalElements: this.allGroups.length,
totalPages: 1, totalPages: 1,
currentPage: 1 currentPage: 1,
}), this.allGroups)); }), this.allGroups));
} }
const result = this.allGroups.find((group: Group) => { const result = this.allGroups.find((group: Group) => {
@@ -146,7 +187,7 @@ describe('GroupsRegistryComponent', () => {
elementsPerPage: [result].length, elementsPerPage: [result].length,
totalElements: [result].length, totalElements: [result].length,
totalPages: 1, totalPages: 1,
currentPage: 1 currentPage: 1,
}), [result])); }), [result]));
}, },
delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> { delete(objectId: string, copyVirtualMetadata?: string[]): Observable<RemoteData<NoContent>> {
@@ -156,9 +197,13 @@ describe('GroupsRegistryComponent', () => {
dsoDataServiceStub = { dsoDataServiceStub = {
findByHref(href: string): Observable<RemoteData<DSpaceObject>> { findByHref(href: string): Observable<RemoteData<DSpaceObject>> {
return createSuccessfulRemoteDataObject$(undefined); return createSuccessfulRemoteDataObject$(undefined);
} },
}; };
configurationDataService = jasmine.createSpyObj('ConfigurationDataService', {
findByPropertyName: of({ payload: { value: 'test' } }),
});
authorizationService = jasmine.createSpyObj('authorizationService', ['isAuthorized']); authorizationService = jasmine.createSpyObj('authorizationService', ['isAuthorized']);
setIsAuthorized(true, true); setIsAuthorized(true, true);
paginationService = new PaginationServiceStub(); paginationService = new PaginationServiceStub();
@@ -167,24 +212,26 @@ describe('GroupsRegistryComponent', () => {
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
provide: TranslateLoader, provide: TranslateLoader,
useClass: TranslateLoaderMock useClass: TranslateLoaderMock,
} },
}), }), GroupsRegistryComponent],
],
declarations: [GroupsRegistryComponent],
providers: [GroupsRegistryComponent, providers: [GroupsRegistryComponent,
{ provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: EPersonDataService, useValue: ePersonDataServiceStub },
{ provide: GroupDataService, useValue: groupsDataServiceStub }, { provide: GroupDataService, useValue: groupsDataServiceStub },
{ provide: DSpaceObjectDataService, useValue: dsoDataServiceStub }, { provide: DSpaceObjectDataService, useValue: dsoDataServiceStub },
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: ConfigurationDataService, useValue: configurationDataService },
{ provide: RouteService, useValue: routeServiceStub }, { provide: RouteService, useValue: routeServiceStub },
{ provide: ActivatedRoute, useValue: new ActivatedRouteStub() },
{ provide: Router, useValue: new RouterMock() }, { provide: Router, useValue: new RouterMock() },
{ provide: AuthorizationDataService, useValue: authorizationService }, { provide: AuthorizationDataService, useValue: authorizationService },
{ provide: PaginationService, useValue: paginationService }, { provide: PaginationService, useValue: paginationService },
{ provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) } { provide: RequestService, useValue: jasmine.createSpyObj('requestService', ['removeByHrefSubstring']) },
{ provide: APP_DATA_SERVICES_MAP, useValue: {} },
provideMockStore(),
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA],
}).compileComponents(); }).compileComponents();
})); }));
@@ -237,16 +284,16 @@ describe('GroupsRegistryComponent', () => {
it('should not check the canManageGroup permissions', () => { it('should not check the canManageGroup permissions', () => {
expect(authorizationService.isAuthorized).not.toHaveBeenCalledWith( expect(authorizationService.isAuthorized).not.toHaveBeenCalledWith(
FeatureID.CanManageGroup, mockGroups[0].self FeatureID.CanManageGroup, mockGroups[0].self,
); );
expect(authorizationService.isAuthorized).not.toHaveBeenCalledWith( expect(authorizationService.isAuthorized).not.toHaveBeenCalledWith(
FeatureID.CanManageGroup, mockGroups[0].self, undefined // treated differently FeatureID.CanManageGroup, mockGroups[0].self, undefined, // treated differently
); );
expect(authorizationService.isAuthorized).not.toHaveBeenCalledWith( expect(authorizationService.isAuthorized).not.toHaveBeenCalledWith(
FeatureID.CanManageGroup, mockGroups[1].self FeatureID.CanManageGroup, mockGroups[1].self,
); );
expect(authorizationService.isAuthorized).not.toHaveBeenCalledWith( expect(authorizationService.isAuthorized).not.toHaveBeenCalledWith(
FeatureID.CanManageGroup, mockGroups[1].self, undefined // treated differently FeatureID.CanManageGroup, mockGroups[1].self, undefined, // treated differently
); );
}); });
}); });

View File

@@ -1,47 +1,94 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import {
import { UntypedFormBuilder } from '@angular/forms'; AsyncPipe,
import { Router } from '@angular/router'; NgForOf,
import { TranslateService } from '@ngx-translate/core'; NgIf,
NgSwitch,
NgSwitchCase,
} from '@angular/common';
import {
Component,
OnDestroy,
OnInit,
} from '@angular/core';
import {
ReactiveFormsModule,
UntypedFormBuilder,
} from '@angular/forms';
import {
Router,
RouterLink,
} from '@angular/router';
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import { import {
BehaviorSubject, BehaviorSubject,
combineLatest as observableCombineLatest, combineLatest as observableCombineLatest,
EMPTY, EMPTY,
Observable, Observable,
of as observableOf, of as observableOf,
Subscription Subscription,
} from 'rxjs'; } from 'rxjs';
import { catchError, defaultIfEmpty, map, switchMap, tap } from 'rxjs/operators'; import {
catchError,
defaultIfEmpty,
map,
switchMap,
tap,
} from 'rxjs/operators';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { buildPaginatedList, PaginatedList } from '../../core/data/paginated-list.model'; import {
buildPaginatedList,
PaginatedList,
} from '../../core/data/paginated-list.model';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { RequestService } from '../../core/data/request.service'; import { RequestService } from '../../core/data/request.service';
import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../core/eperson/group-data.service'; import { GroupDataService } from '../../core/eperson/group-data.service';
import { EPerson } from '../../core/eperson/models/eperson.model'; import { EPerson } from '../../core/eperson/models/eperson.model';
import { GroupDtoModel } from '../../core/eperson/models/group-dto.model';
import { Group } from '../../core/eperson/models/group.model'; import { Group } from '../../core/eperson/models/group.model';
import { GroupDtoModel } from '../../core/eperson/models/group-dto.model';
import { PaginationService } from '../../core/pagination/pagination.service';
import { RouteService } from '../../core/services/route.service'; import { RouteService } from '../../core/services/route.service';
import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { NoContent } from '../../core/shared/NoContent.model';
import { import {
getAllSucceededRemoteData, getAllSucceededRemoteData,
getFirstCompletedRemoteData, getFirstCompletedRemoteData,
getFirstSucceededRemoteData, getFirstSucceededRemoteData,
getRemoteDataPayload getRemoteDataPayload,
} from '../../core/shared/operators'; } from '../../core/shared/operators';
import { PageInfo } from '../../core/shared/page-info.model'; import { PageInfo } from '../../core/shared/page-info.model';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { ThemedLoadingComponent } from '../../shared/loading/themed-loading.component';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { PaginationComponent } from '../../shared/pagination/pagination.component';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { NoContent } from '../../core/shared/NoContent.model';
import { PaginationService } from '../../core/pagination/pagination.service';
import { followLink } from '../../shared/utils/follow-link-config.model'; import { followLink } from '../../shared/utils/follow-link-config.model';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-groups-registry', selector: 'ds-groups-registry',
templateUrl: './groups-registry.component.html', templateUrl: './groups-registry.component.html',
imports: [
ThemedLoadingComponent,
TranslateModule,
RouterLink,
ReactiveFormsModule,
AsyncPipe,
NgIf,
PaginationComponent,
NgSwitch,
NgSwitchCase,
NgbTooltipModule,
NgForOf,
],
standalone: true,
}) })
/** /**
* A component used for managing all existing groups within the repository. * A component used for managing all existing groups within the repository.
@@ -57,7 +104,7 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), { config: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
id: 'gl', id: 'gl',
pageSize: 5, pageSize: 5,
currentPage: 1 currentPage: 1,
}); });
/** /**
@@ -155,7 +202,7 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
this.canManageGroup$(isSiteAdmin, group), this.canManageGroup$(isSiteAdmin, group),
this.hasLinkedDSO(group), this.hasLinkedDSO(group),
this.getSubgroups(group), this.getSubgroups(group),
this.getMembers(group) this.getMembers(group),
]).pipe( ]).pipe(
map(([canDelete, canManageGroup, hasLinkedDSO, subgroups, members]: map(([canDelete, canManageGroup, hasLinkedDSO, subgroups, members]:
[boolean, boolean, boolean, RemoteData<PaginatedList<Group>>, RemoteData<PaginatedList<EPerson>>]) => { [boolean, boolean, boolean, RemoteData<PaginatedList<Group>>, RemoteData<PaginatedList<EPerson>>]) => {
@@ -166,8 +213,8 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
groupDtoModel.subgroups = subgroups.payload; groupDtoModel.subgroups = subgroups.payload;
groupDtoModel.epersons = members.payload; groupDtoModel.epersons = members.payload;
return groupDtoModel; return groupDtoModel;
} },
) ),
); );
} else { } else {
return EMPTY; return EMPTY;
@@ -175,9 +222,9 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
})]).pipe(defaultIfEmpty([]), map((dtos: GroupDtoModel[]) => { })]).pipe(defaultIfEmpty([]), map((dtos: GroupDtoModel[]) => {
return buildPaginatedList(groups.pageInfo, dtos); return buildPaginatedList(groups.pageInfo, dtos);
})); }));
}) }),
); );
}) }),
).subscribe((value: PaginatedList<GroupDtoModel>) => { ).subscribe((value: PaginatedList<GroupDtoModel>) => {
this.groupsDto$.next(value); this.groupsDto$.next(value);
this.pageInfoState$.next(value.pageInfo); this.pageInfoState$.next(value.pageInfo);
@@ -216,18 +263,28 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
/** /**
* Get the members (epersons embedded value of a group) * Get the members (epersons embedded value of a group)
* NOTE: At this time we only grab the *first* member in order to receive the `totalElements` value
* needed for our HTML template.
* @param group * @param group
*/ */
getMembers(group: Group): Observable<RemoteData<PaginatedList<EPerson>>> { getMembers(group: Group): Observable<RemoteData<PaginatedList<EPerson>>> {
return this.ePersonDataService.findListByHref(group._links.epersons.href).pipe(getFirstSucceededRemoteData()); return this.ePersonDataService.findListByHref(group._links.epersons.href, {
currentPage: 1,
elementsPerPage: 1,
}).pipe(getFirstSucceededRemoteData());
} }
/** /**
* Get the subgroups (groups embedded value of a group) * Get the subgroups (groups embedded value of a group)
* NOTE: At this time we only grab the *first* subgroup in order to receive the `totalElements` value
* needed for our HTML template.
* @param group * @param group
*/ */
getSubgroups(group: Group): Observable<RemoteData<PaginatedList<Group>>> { getSubgroups(group: Group): Observable<RemoteData<PaginatedList<Group>>> {
return this.groupService.findListByHref(group._links.subgroups.href).pipe(getFirstSucceededRemoteData()); return this.groupService.findListByHref(group._links.subgroups.href, {
currentPage: 1,
elementsPerPage: 1,
}).pipe(getFirstSucceededRemoteData());
} }
/** /**

View File

@@ -1,4 +1,4 @@
<div class="container"> <div class="container">
<h2>{{'admin.curation-tasks.header' |translate }}</h2> <h1>{{'admin.curation-tasks.header' |translate }}</h1>
<ds-curation-form></ds-curation-form> <ds-curation-form></ds-curation-form>
</div> </div>

View File

@@ -1,7 +1,13 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { AdminCurationTasksComponent } from './admin-curation-tasks.component';
import { TranslateModule } from '@ngx-translate/core';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import {
ComponentFixture,
TestBed,
waitForAsync,
} from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { CurationFormComponent } from '../../curation-form/curation-form.component';
import { AdminCurationTasksComponent } from './admin-curation-tasks.component';
describe('AdminCurationTasksComponent', () => { describe('AdminCurationTasksComponent', () => {
let comp: AdminCurationTasksComponent; let comp: AdminCurationTasksComponent;
@@ -9,10 +15,15 @@ describe('AdminCurationTasksComponent', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()], imports: [TranslateModule.forRoot(), AdminCurationTasksComponent],
declarations: [AdminCurationTasksComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA],
schemas: [CUSTOM_ELEMENTS_SCHEMA] })
}).compileComponents(); .overrideComponent(AdminCurationTasksComponent, {
remove: {
imports: [CurationFormComponent],
},
})
.compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {

View File

@@ -1,4 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { CurationFormComponent } from '../../curation-form/curation-form.component';
/** /**
* Component responsible for rendering the system wide Curation Task UI * Component responsible for rendering the system wide Curation Task UI
@@ -6,6 +9,11 @@ import { Component } from '@angular/core';
@Component({ @Component({
selector: 'ds-admin-curation-task', selector: 'ds-admin-curation-task',
templateUrl: './admin-curation-tasks.component.html', templateUrl: './admin-curation-tasks.component.html',
imports: [
CurationFormComponent,
TranslateModule,
],
standalone: true,
}) })
export class AdminCurationTasksComponent { export class AdminCurationTasksComponent {

View File

@@ -1,5 +1,5 @@
<div class="container"> <div class="container">
<h2 id="header">{{'admin.batch-import.page.header' | translate}}</h2> <h1 id="header">{{'admin.batch-import.page.header' | translate}}</h1>
<p>{{'admin.batch-import.page.help' | translate}}</p> <p>{{'admin.batch-import.page.help' | translate}}</p>
<p *ngIf="dso"> <p *ngIf="dso">
selected collection: <b>{{getDspaceObjectName()}}</b>&nbsp; selected collection: <b>{{getDspaceObjectName()}}</b>&nbsp;

View File

@@ -1,22 +1,32 @@
import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing';
import { BatchImportPageComponent } from './batch-import-page.component';
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { FormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { RouterTestingModule } from '@angular/router/testing';
import { FileValueAccessorDirective } from '../../shared/utils/file-value-accessor.directive';
import { FileValidator } from '../../shared/utils/require-file.validator';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import {
BATCH_IMPORT_SCRIPT_NAME,
ScriptDataService
} from '../../core/data/processes/script-data.service';
import { Router } from '@angular/router';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
ComponentFixture,
fakeAsync,
TestBed,
waitForAsync,
} from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core';
import {
BATCH_IMPORT_SCRIPT_NAME,
ScriptDataService,
} from '../../core/data/processes/script-data.service';
import { ProcessParameter } from '../../process-page/processes/process-parameter.model'; import { ProcessParameter } from '../../process-page/processes/process-parameter.model';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import {
createFailedRemoteDataObject$,
createSuccessfulRemoteDataObject$,
} from '../../shared/remote-data.utils';
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { FileDropzoneNoUploaderComponent } from '../../shared/upload/file-dropzone-no-uploader/file-dropzone-no-uploader.component';
import { FileValueAccessorDirective } from '../../shared/utils/file-value-accessor.directive';
import { FileValidator } from '../../shared/utils/require-file.validator';
import { BatchImportPageComponent } from './batch-import-page.component';
describe('BatchImportPageComponent', () => { describe('BatchImportPageComponent', () => {
let component: BatchImportPageComponent; let component: BatchImportPageComponent;
@@ -31,14 +41,14 @@ describe('BatchImportPageComponent', () => {
notificationService = new NotificationsServiceStub(); notificationService = new NotificationsServiceStub();
scriptService = jasmine.createSpyObj('scriptService', scriptService = jasmine.createSpyObj('scriptService',
{ {
invoke: createSuccessfulRemoteDataObject$({ processId: '46' }) invoke: createSuccessfulRemoteDataObject$({ processId: '46' }),
} },
); );
router = jasmine.createSpyObj('router', { router = jasmine.createSpyObj('router', {
navigateByUrl: jasmine.createSpy('navigateByUrl') navigateByUrl: jasmine.createSpy('navigateByUrl'),
}); });
locationStub = jasmine.createSpyObj('location', { locationStub = jasmine.createSpyObj('location', {
back: jasmine.createSpy('back') back: jasmine.createSpy('back'),
}); });
} }
@@ -48,17 +58,23 @@ describe('BatchImportPageComponent', () => {
imports: [ imports: [
FormsModule, FormsModule,
TranslateModule.forRoot(), TranslateModule.forRoot(),
RouterTestingModule.withRoutes([]) RouterTestingModule.withRoutes([]),
BatchImportPageComponent, FileValueAccessorDirective, FileValidator,
], ],
declarations: [BatchImportPageComponent, FileValueAccessorDirective, FileValidator],
providers: [ providers: [
{ provide: NotificationsService, useValue: notificationService }, { provide: NotificationsService, useValue: notificationService },
{ provide: ScriptDataService, useValue: scriptService }, { provide: ScriptDataService, useValue: scriptService },
{ provide: Router, useValue: router }, { provide: Router, useValue: router },
{ provide: Location, useValue: locationStub }, { provide: Location, useValue: locationStub },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA],
}).compileComponents(); })
.overrideComponent(BatchImportPageComponent, {
remove: {
imports: [FileDropzoneNoUploaderComponent],
},
})
.compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {
@@ -108,7 +124,7 @@ describe('BatchImportPageComponent', () => {
it('metadata-import script is invoked with --zip fileName and the mockFile', () => { it('metadata-import script is invoked with --zip fileName and the mockFile', () => {
const parameterValues: ProcessParameter[] = [ const parameterValues: ProcessParameter[] = [
Object.assign(new ProcessParameter(), { name: '--add' }), Object.assign(new ProcessParameter(), { name: '--add' }),
Object.assign(new ProcessParameter(), { name: '--zip', value: 'filename.zip' }) Object.assign(new ProcessParameter(), { name: '--zip', value: 'filename.zip' }),
]; ];
expect(scriptService.invoke).toHaveBeenCalledWith(BATCH_IMPORT_SCRIPT_NAME, parameterValues, [fileMock]); expect(scriptService.invoke).toHaveBeenCalledWith(BATCH_IMPORT_SCRIPT_NAME, parameterValues, [fileMock]);
}); });
@@ -181,7 +197,7 @@ describe('BatchImportPageComponent', () => {
it('metadata-import script is invoked with --url and the file url', () => { it('metadata-import script is invoked with --url and the file url', () => {
const parameterValues: ProcessParameter[] = [ const parameterValues: ProcessParameter[] = [
Object.assign(new ProcessParameter(), { name: '--add' }), Object.assign(new ProcessParameter(), { name: '--add' }),
Object.assign(new ProcessParameter(), { name: '--url', value: 'example.fileURL.com' }) Object.assign(new ProcessParameter(), { name: '--url', value: 'example.fileURL.com' }),
]; ];
expect(scriptService.invoke).toHaveBeenCalledWith(BATCH_IMPORT_SCRIPT_NAME, parameterValues, [null]); expect(scriptService.invoke).toHaveBeenCalledWith(BATCH_IMPORT_SCRIPT_NAME, parameterValues, [null]);
}); });

View File

@@ -1,26 +1,48 @@
import { Component } from '@angular/core';
import { Location } from '@angular/common';
import { TranslateService } from '@ngx-translate/core';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { BATCH_IMPORT_SCRIPT_NAME, ScriptDataService } from '../../core/data/processes/script-data.service';
import { Router } from '@angular/router';
import { ProcessParameter } from '../../process-page/processes/process-parameter.model';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
import { RemoteData } from '../../core/data/remote-data';
import { Process } from '../../process-page/processes/process.model';
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
import { getProcessDetailRoute } from '../../process-page/process-page-routing.paths';
import { import {
ImportBatchSelectorComponent Location,
} from '../../shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component'; NgIf,
} from '@angular/common';
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import { UiSwitchModule } from 'ngx-ui-switch';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import {
BATCH_IMPORT_SCRIPT_NAME,
ScriptDataService,
} from '../../core/data/processes/script-data.service';
import { RemoteData } from '../../core/data/remote-data';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
import { getProcessDetailRoute } from '../../process-page/process-page-routing.paths';
import { Process } from '../../process-page/processes/process.model';
import { ProcessParameter } from '../../process-page/processes/process-parameter.model';
import { ImportBatchSelectorComponent } from '../../shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component';
import {
isEmpty,
isNotEmpty,
} from '../../shared/empty.util';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { FileDropzoneNoUploaderComponent } from '../../shared/upload/file-dropzone-no-uploader/file-dropzone-no-uploader.component';
@Component({ @Component({
selector: 'ds-batch-import-page', selector: 'ds-batch-import-page',
templateUrl: './batch-import-page.component.html' templateUrl: './batch-import-page.component.html',
imports: [
NgIf,
TranslateModule,
FormsModule,
UiSwitchModule,
FileDropzoneNoUploaderComponent,
],
standalone: true,
}) })
export class BatchImportPageComponent { export class BatchImportPageComponent {
/** /**
@@ -91,7 +113,7 @@ export class BatchImportPageComponent {
} }
} else { } else {
const parameterValues: ProcessParameter[] = [ const parameterValues: ProcessParameter[] = [
Object.assign(new ProcessParameter(), { name: '--add' }) Object.assign(new ProcessParameter(), { name: '--add' }),
]; ];
if (this.isUpload) { if (this.isUpload) {
parameterValues.push(Object.assign(new ProcessParameter(), { name: '--zip', value: this.fileObject.name })); parameterValues.push(Object.assign(new ProcessParameter(), { name: '--zip', value: this.fileObject.name }));

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