diff --git a/.eslintrc.json b/.eslintrc.json index 44f53ecb585..b95b54b979a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,7 +6,7 @@ "eslint-plugin-import", "eslint-plugin-jsdoc", "eslint-plugin-deprecation", - "eslint-plugin-unused-imports", + "unused-imports", "eslint-plugin-lodash" ], "overrides": [ @@ -205,7 +205,7 @@ "import/order": "off", "import/no-deprecated": "warn", "import/no-namespace": "error", - + "unused-imports/no-unused-imports": "error", "lodash/import-scope": [ "error", "method" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index be15b0a507c..e50105b8797 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,7 +1,7 @@ ## References _Add references/links to any related issues or PRs. These may include:_ -* Fixes #[issue-number] -* Requires DSpace/DSpace#[pr-number] (if a REST API PR is required to test this) +* Fixes #`issue-number` (if this fixes an issue ticket) +* Requires DSpace/DSpace#`pr-number` (if a REST API PR is required to test this) ## Description Short summary of changes (1-2 sentences). @@ -19,8 +19,10 @@ List of changes in this PR: _This checklist provides a reminder of what we are going to look for when reviewing your PR. You need not complete this checklist prior to creating your PR (draft PRs are always welcome). If you are unsure about an item in the checklist, don't hesitate to ask. We're here to help!_ - [ ] My PR is small in size (e.g. less than 1,000 lines of code, not including comments & specs/tests), or I have provided reasons as to why that's not possible. -- [ ] My PR passes [TSLint](https://palantir.github.io/tslint/) validation using `yarn run lint` -- [ ] My PR doesn't introduce circular dependencies +- [ ] My PR passes [ESLint](https://eslint.org/) validation using `yarn lint` +- [ ] My PR doesn't introduce circular dependencies (verified via `yarn check-circ-deps`) - [ ] My PR includes [TypeDoc](https://typedoc.org/) comments for _all new (or modified) public methods and classes_. It also includes TypeDoc for large or complex private methods. - [ ] My PR passes all specs/tests and includes new/updated specs or tests based on the [Code Testing Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Testing+Guide). -- [ ] If my PR includes new, third-party dependencies (in `package.json`), I've made sure their licenses align with the [DSpace BSD License](https://github.com/DSpace/DSpace/blob/main/LICENSE) based on the [Licensing of Contributions](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines#CodeContributionGuidelines-LicensingofContributions) documentation. +- [ ] If my PR includes new libraries/dependencies (in `package.json`), I've made sure their licenses align with the [DSpace BSD License](https://github.com/DSpace/DSpace/blob/main/LICENSE) based on the [Licensing of Contributions](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines#CodeContributionGuidelines-LicensingofContributions) documentation. +- [ ] If my PR includes new features or configurations, I've provided basic technical documentation in the PR itself. +- [ ] If my PR fixes an issue ticket, I've [linked them together](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue). diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c58e09edf24..f3b7aff6897 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,17 +15,19 @@ jobs: env: # The ci step will test the dspace-angular code against DSpace REST. # Direct that step to utilize a DSpace REST service that has been started in docker. - DSPACE_REST_HOST: localhost + DSPACE_REST_HOST: 127.0.0.1 DSPACE_REST_PORT: 8080 DSPACE_REST_NAMESPACE: '/server' DSPACE_REST_SSL: false + # Spin up UI on 127.0.0.1 to avoid host resolution issues in e2e tests with Node 18+ + DSPACE_UI_HOST: 127.0.0.1 # When Chrome version is specified, we pin to a specific version of Chrome # Comment this out to use the latest release #CHROME_VERSION: "90.0.4430.212-1" strategy: # Create a matrix of Node versions to test against (in parallel) matrix: - node-version: [14.x, 16.x] + node-version: [16.x, 18.x] # Do NOT exit immediately if one matrix job fails fail-fast: false # These are the actual CI steps to perform per job @@ -112,7 +114,7 @@ jobs: start: yarn run serve:ssr # Wait for backend & frontend to be available # NOTE: We use the 'sites' REST endpoint to also ensure the database is ready - wait-on: http://localhost:8080/server/api/core/sites, http://localhost:4000 + wait-on: http://127.0.0.1:8080/server/api/core/sites, http://127.0.0.1:4000 # Wait for 2 mins max for everything to respond wait-on-timeout: 120 @@ -147,7 +149,7 @@ jobs: run: | nohup yarn run serve:ssr & printf 'Waiting for app to start' - until curl --output /dev/null --silent --head --fail http://localhost:4000/home; do + until curl --output /dev/null --silent --head --fail http://127.0.0.1:4000/home; do printf '.' sleep 2 done @@ -158,7 +160,7 @@ jobs: # This step also prints entire HTML of homepage for easier debugging if grep fails. - name: Verify SSR (server-side rendering) run: | - result=$(wget -O- -q http://localhost:4000/home) + result=$(wget -O- -q http://127.0.0.1:4000/home) echo "$result" echo "$result" | grep -oE "]*>" | grep DSpace diff --git a/.github/workflows/codescan.yml b/.github/workflows/codescan.yml new file mode 100644 index 00000000000..35a2e2d24aa --- /dev/null +++ b/.github/workflows/codescan.yml @@ -0,0 +1,49 @@ +# DSpace CodeQL code scanning configuration for GitHub +# https://docs.github.com/en/code-security/code-scanning +# +# NOTE: Code scanning must be run separate from our default build.yml +# because CodeQL requires a fresh build with all tests *disabled*. +name: "Code Scanning" + +# Run this code scan for all pushes / PRs to main branch. Also run once a week. +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + # Don't run if PR is only updating static documentation + paths-ignore: + - '**/*.md' + - '**/*.txt' + schedule: + - cron: "37 0 * * 1" + +jobs: + analyze: + name: Analyze Code + runs-on: ubuntu-latest + # Limit permissions of this GitHub action. Can only write to security-events + permissions: + actions: read + contents: read + security-events: write + + steps: + # https://github.com/actions/checkout + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + # https://github.com/github/codeql-action + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: javascript + + # Autobuild attempts to build any compiled languages + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # Perform GitHub Code Scanning. + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000000..4e732302f4a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,46 @@ +# How to Contribute + +DSpace is a community built and supported project. We do not have a centralized development or support team, but have a dedicated group of volunteers who help us improve the software, documentation, resources, etc. + +* [Contribute new code via a Pull Request](#contribute-new-code-via-a-pull-request) +* [Contribute documentation](#contribute-documentation) +* [Help others on mailing lists or Slack](#help-others-on-mailing-lists-or-slack) +* [Join a working or interest group](#join-a-working-or-interest-group) + +## Contribute new code via a Pull Request + +We accept [GitHub Pull Requests (PRs)](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) at any time from anyone. +Contributors to each release are recognized in our [Release Notes](https://wiki.lyrasis.org/display/DSDOC7x/Release+Notes). + +Code Contribution Checklist +- [ ] PRs _should_ be smaller in size (ideally less than 1,000 lines of code, not including comments & tests) +- [ ] PRs **must** pass [ESLint](https://eslint.org/) validation using `yarn lint` +- [ ] PRs **must** not introduce circular dependencies (verified via `yarn check-circ-deps`) +- [ ] PRs **must** include [TypeDoc](https://typedoc.org/) comments for _all new (or modified) public methods and classes_. Large or complex private methods should also have TypeDoc. +- [ ] PRs **must** pass all automated pecs/tests and includes new/updated specs or tests based on the [Code Testing Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Testing+Guide). +- [ ] If a PR includes new libraries/dependencies (in `package.json`), then their software licenses **must** align with the [DSpace BSD License](https://github.com/DSpace/dspace-angular/blob/main/LICENSE) based on the [Licensing of Contributions](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines#CodeContributionGuidelines-LicensingofContributions) documentation. +- [ ] Basic technical documentation _should_ be provided for any new features or configuration, either in the PR itself or in the DSpace Wiki documentation. +- [ ] If a PR fixes an issue ticket, please [link them together](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue). + +Additional details on the code contribution process can be found in our [Code Contribution Guidelines](https://wiki.lyrasis.org/display/DSPACE/Code+Contribution+Guidelines) + +## Contribute documentation + +DSpace Documentation is a collaborative effort in a shared Wiki. The latest documentation is at https://wiki.lyrasis.org/display/DSDOC7x + +If you find areas of the DSpace Documentation which you wish to improve, please request a Wiki account by emailing wikihelp@lyrasis.org. +Once you have an account setup, contact @tdonohue (via [Slack](https://wiki.lyrasis.org/display/DSPACE/Slack) or email) for access to edit our Documentation. + +## Help others on mailing lists or Slack + +DSpace has our own [Slack](https://wiki.lyrasis.org/display/DSPACE/Slack) community and [Mailing Lists](https://wiki.lyrasis.org/display/DSPACE/Mailing+Lists) where discussions take place and questions are answered. +Anyone is welcome to join and help others. We just ask you to follow our [Code of Conduct](https://www.lyrasis.org/about/Pages/Code-of-Conduct.aspx) (adopted via LYRASIS). + +## Join a working or interest group + +Most of the work in building/improving DSpace comes via [Working Groups](https://wiki.lyrasis.org/display/DSPACE/DSpace+Working+Groups) or [Interest Groups](https://wiki.lyrasis.org/display/DSPACE/DSpace+Interest+Groups). + +All working/interest groups are open to anyone to join and participate. A few key groups to be aware of include: + +* [DSpace 7 Working Group](https://wiki.lyrasis.org/display/DSPACE/DSpace+7+Working+Group) - This is the main (mostly volunteer) development team. We meet weekly to review our current development [project board](https://github.com/orgs/DSpace/projects), assigning tickets and/or PRs. +* [DSpace Community Advisory Team (DCAT)](https://wiki.lyrasis.org/display/cmtygp/DSpace+Community+Advisory+Team) - This is an interest group for repository managers/administrators. We meet monthly to discuss DSpace, share tips & provide feedback back to developers. \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a7c1640d0ba..8eb3582ba31 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # This image will be published as dspace/dspace-angular # See https://github.com/DSpace/dspace-angular/tree/main/docker for usage details -FROM node:14-alpine +FROM node:18-alpine WORKDIR /app ADD . /app/ EXPOSE 4000 diff --git a/README.md b/README.md index 0ede4d4d193..c90dc1d08fd 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ https://wiki.lyrasis.org/display/DSDOC7x/Installing+DSpace Quick start ----------- -**Ensure you're running [Node](https://nodejs.org) `v14.x` or `v16.x`, [npm](https://www.npmjs.com/) >= `v5.x` and [yarn](https://yarnpkg.com) == `v1.x`** +**Ensure you're running [Node](https://nodejs.org) `v16.x` or `v18.x`, [npm](https://www.npmjs.com/) >= `v5.x` and [yarn](https://yarnpkg.com) == `v1.x`** ```bash # clone the repo @@ -90,7 +90,7 @@ Requirements ------------ - [Node.js](https://nodejs.org) and [yarn](https://yarnpkg.com) -- Ensure you're running node `v14.x` or `v16.x` and yarn == `v1.x` +- Ensure you're running node `v16.x` or `v18.x` and yarn == `v1.x` If you have [`nvm`](https://github.com/creationix/nvm#install-script) or [`nvm-windows`](https://github.com/coreybutler/nvm-windows) installed, which is highly recommended, you can run `nvm install --lts && nvm use` to install and start using the latest Node LTS. @@ -379,10 +379,10 @@ To get the most out of TypeScript, you'll need a TypeScript-aware editor. We've - [Sublime Text](http://www.sublimetext.com/3) - [Typescript-Sublime-Plugin](https://github.com/Microsoft/Typescript-Sublime-plugin#installation) -Collaborating +Contributing ------------- -See [the guide on the wiki](https://wiki.lyrasis.org/display/DSPACE/DSpace+7+-+Angular+UI+Development#DSpace7-AngularUIDevelopment-Howtocontribute) +See [Contributing documentation](CONTRIBUTING.md) File Structure -------------- diff --git a/config/config.example.yml b/config/config.example.yml index e5ef56f1a4e..c15c68353e0 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -170,6 +170,9 @@ languages: - code: el label: Ελληνικά active: true + - code: uk + label: Yкраї́нська + active: true # Browse-By Pages browseBy: @@ -207,6 +210,11 @@ item: undoTimeout: 10000 # 10 seconds # Show the item access status label in items lists showAccessStatuses: false + bitstream: + # Number of entries in the bitstream list in the item view page. + # Rounded to the nearest size in the list of selectable sizes on the + # settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. + pageSize: 5 # Collection Page Config collection: @@ -307,4 +315,4 @@ info: # display in supported metadata fields. By default, only dc.description.abstract is supported. markdown: enabled: false - mathjax: false \ No newline at end of file + mathjax: false diff --git a/cypress.json b/cypress.json index 80358eb6dde..3adf7839c24 100644 --- a/cypress.json +++ b/cypress.json @@ -5,7 +5,7 @@ "screenshotsFolder": "cypress/screenshots", "pluginsFile": "cypress/plugins/index.ts", "fixturesFolder": "cypress/fixtures", - "baseUrl": "http://localhost:4000", + "baseUrl": "http://127.0.0.1:4000", "retries": { "runMode": 2, "openMode": 0 @@ -22,4 +22,4 @@ "DSPACE_TEST_SUBMIT_USER": "dspacedemo+submit@gmail.com", "DSPACE_TEST_SUBMIT_USER_PASSWORD": "dspace" } -} \ No newline at end of file +} diff --git a/cypress/integration/my-dspace.spec.ts b/cypress/integration/my-dspace.spec.ts index fa923dbcbc6..48f44eecb95 100644 --- a/cypress/integration/my-dspace.spec.ts +++ b/cypress/integration/my-dspace.spec.ts @@ -4,10 +4,11 @@ import { testA11y } from 'cypress/support/utils'; describe('My DSpace page', () => { it('should display recent submissions and pass accessibility tests', () => { - cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); - cy.visit('/mydspace'); + // 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.get('ds-my-dspace-page').should('exist'); // At least one recent submission should be displayed @@ -36,10 +37,11 @@ describe('My DSpace page', () => { }); it('should have a working detailed view that passes accessibility tests', () => { - cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); - cy.visit('/mydspace'); + // 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.get('ds-my-dspace-page').should('exist'); // Click button in sidebar to display detailed view @@ -61,9 +63,11 @@ describe('My DSpace page', () => { // NOTE: Deleting existing submissions is exercised by submission.spec.ts it('should let you start a new submission & edit in-progress submissions', () => { - cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.visit('/mydspace'); + // 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); + // Open the New Submission dropdown cy.get('button[data-test="submission-dropdown"]').click(); // Click on the "Item" type in that dropdown @@ -131,9 +135,11 @@ describe('My DSpace page', () => { }); it('should let you import from external sources', () => { - cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); cy.visit('/mydspace'); + // 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); + // Open the New Import dropdown cy.get('button[data-test="import-dropdown"]').click(); // Click on the "Item" type in that dropdown diff --git a/cypress/integration/submission.spec.ts b/cypress/integration/submission.spec.ts index 009c50115b3..9eef596b02b 100644 --- a/cypress/integration/submission.spec.ts +++ b/cypress/integration/submission.spec.ts @@ -6,11 +6,12 @@ describe('New Submission page', () => { // NOTE: We already test that new submissions can be started from MyDSpace in my-dspace.spec.ts it('should create a new submission when using /submit path & pass accessibility', () => { - cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); - // Test that calling /submit with collection & entityType will create a new submission cy.visit('/submit?collection=' + TEST_SUBMIT_COLLECTION_UUID + '&entityType=none'); + // 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); + // Should redirect to /workspaceitems, as we've started a new submission cy.url().should('include', '/workspaceitems'); @@ -33,11 +34,12 @@ describe('New Submission page', () => { }); it('should block submission & show errors if required fields are missing', () => { - cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); - // Create a new submission cy.visit('/submit?collection=' + TEST_SUBMIT_COLLECTION_UUID + '&entityType=none'); + // 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); + // Attempt an immediate deposit without filling out any fields cy.get('button#deposit').click(); @@ -92,11 +94,12 @@ describe('New Submission page', () => { }); it('should allow for deposit if all required fields completed & file uploaded', () => { - cy.login(TEST_SUBMIT_USER, TEST_SUBMIT_USER_PASSWORD); - // Create a new submission cy.visit('/submit?collection=' + TEST_SUBMIT_COLLECTION_UUID + '&entityType=none'); + // 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); + // Fill out all required fields (Title, Date) cy.get('input#dc_title').type('DSpace logo uploaded via e2e tests'); cy.get('input#dc_date_issued_year').type('2022'); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 30951d46f1e..04c217aa0f6 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -19,6 +19,14 @@ declare global { * @param password password to login as */ login(email: string, password: string): typeof login; + + /** + * Login via form before accessing the next page. Useful to fill out login + * form when a cy.visit() call is to an a page which requires authentication. + * @param email email to login as + * @param password password to login as + */ + loginViaForm(email: string, password: string): typeof loginViaForm; } } } @@ -26,6 +34,8 @@ declare global { /** * Login user via REST API directly, and pass authentication token to UI via * the UI's dsAuthInfo cookie. + * WARNING: WHILE THIS METHOD WORKS, OCCASIONALLY RANDOM AUTHENTICATION ERRORS OCCUR. + * At this time "loginViaForm()" seems more consistent/stable. * @param email email to login as * @param password password to login as */ @@ -81,3 +91,20 @@ function login(email: string, password: string): void { } // Add as a Cypress command (i.e. assign to 'cy.login') Cypress.Commands.add('login', login); + + +/** + * Login user via displayed login form + * @param email email to login as + * @param password password to login as + */ + function loginViaForm(email: string, password: string): void { + // Enter email + cy.get('ds-log-in [data-test="email"]').type(email); + // Enter password + cy.get('ds-log-in [data-test="password"]').type(password); + // Click login button + cy.get('ds-log-in [data-test="login-button"]').click(); +} +// Add as a Cypress command (i.e. assign to 'cy.loginViaForm') +Cypress.Commands.add('loginViaForm', loginViaForm); \ No newline at end of file diff --git a/docker/docker-compose-ci.yml b/docker/docker-compose-ci.yml index dbe9500499b..ef84c14f43f 100644 --- a/docker/docker-compose-ci.yml +++ b/docker/docker-compose-ci.yml @@ -24,8 +24,8 @@ services: # __D__ => "-" (e.g. google__D__metadata => google-metadata) # dspace.dir, dspace.server.url and dspace.ui.url dspace__P__dir: /dspace - dspace__P__server__P__url: http://localhost:8080/server - dspace__P__ui__P__url: http://localhost:4000 + dspace__P__server__P__url: http://127.0.0.1:8080/server + dspace__P__ui__P__url: http://127.0.0.1:4000 # db.url: Ensure we are using the 'dspacedb' image for our database db__P__url: 'jdbc:postgresql://dspacedb:5432/dspace' # solr.server: Ensure we are using the 'dspacesolr' image for Solr diff --git a/package.json b/package.json index 3b01ba5c040..993b8e079ef 100644 --- a/package.json +++ b/package.json @@ -162,14 +162,14 @@ "@types/sanitize-html": "^2.6.2", "@typescript-eslint/eslint-plugin": "5.11.0", "@typescript-eslint/parser": "5.11.0", - "axe-core": "^4.3.3", + "axe-core": "^4.4.3", "compression-webpack-plugin": "^9.2.0", "copy-webpack-plugin": "^6.4.1", "cross-env": "^7.0.3", "css-loader": "^6.2.0", "css-minimizer-webpack-plugin": "^3.4.1", "cssnano": "^5.0.6", - "cypress": "9.5.1", + "cypress": "9.7.0", "cypress-axe": "^0.14.0", "debug-loader": "^0.0.1", "deep-freeze": "0.0.1", @@ -177,7 +177,7 @@ "eslint": "^8.2.0", "eslint-plugin-deprecation": "^1.3.2", "eslint-plugin-import": "^2.25.4", - "eslint-plugin-jsdoc": "^38.0.6", + "eslint-plugin-jsdoc": "^39.3.6", "eslint-plugin-lodash": "^7.4.0", "eslint-plugin-unused-imports": "^2.0.0", "express-static-gzip": "^2.1.5", diff --git a/src/app/access-control/access-control.module.ts b/src/app/access-control/access-control.module.ts index 891238bbed1..afb92a91115 100644 --- a/src/app/access-control/access-control.module.ts +++ b/src/app/access-control/access-control.module.ts @@ -10,6 +10,16 @@ import { MembersListComponent } from './group-registry/group-form/members-list/m 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'; + +/** + * 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: [ @@ -26,6 +36,12 @@ import { FormModule } from '../shared/form/form.module'; GroupFormComponent, SubgroupsListComponent, MembersListComponent + ], + providers: [ + { + provide: DYNAMIC_ERROR_MESSAGES_MATCHER, + useValue: ValidateEmailErrorStateMatcher + }, ] }) /** diff --git a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.ts b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.ts index deb16c0d732..4236d152dcb 100644 --- a/src/app/admin/admin-import-metadata-page/metadata-import-page.component.ts +++ b/src/app/admin/admin-import-metadata-page/metadata-import-page.component.ts @@ -1,12 +1,8 @@ import { Location } from '@angular/common'; -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { Observable } from 'rxjs'; -import { map, switchMap } from 'rxjs/operators'; -import { AuthService } from '../../core/auth/auth.service'; import { METADATA_IMPORT_SCRIPT_NAME, ScriptDataService } from '../../core/data/processes/script-data.service'; -import { EPerson } from '../../core/eperson/models/eperson.model'; import { ProcessParameter } from '../../process-page/processes/process-parameter.model'; import { isNotEmpty } from '../../shared/empty.util'; import { NotificationsService } from '../../shared/notifications/notifications.service'; diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts index 7fcc86e25d1..162bf2bdb28 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts @@ -1,12 +1,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import {combineLatest, combineLatest as observableCombineLatest, Observable, of as observableOf, zip} from 'rxjs'; +import { combineLatest as observableCombineLatest, Observable} from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list.model'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { BitstreamFormat } from '../../../core/shared/bitstream-format.model'; import { BitstreamFormatDataService } from '../../../core/data/bitstream-format-data.service'; import { map, mergeMap, switchMap, take, toArray } from 'rxjs/operators'; -import { hasValue } from '../../../shared/empty.util'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts index 74bfc5f0a49..944288a7a51 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts +++ b/src/app/admin/admin-registries/metadata-registry/metadata-registry.component.spec.ts @@ -19,10 +19,7 @@ import { RestResponse } from '../../../core/cache/response.models'; import { MetadataSchema } from '../../../core/metadata/metadata-schema.model'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { PaginationService } from '../../../core/pagination/pagination.service'; -import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; -import { FindListOptions } from '../../../core/data/find-list-options.model'; describe('MetadataRegistryComponent', () => { let comp: MetadataRegistryComponent; diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts index d63b4ed84f5..2b660a63634 100644 --- a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts +++ b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.spec.ts @@ -23,11 +23,8 @@ import { MetadataSchema } from '../../../core/metadata/metadata-schema.model'; import { MetadataField } from '../../../core/metadata/metadata-field.model'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { VarDirective } from '../../../shared/utils/var.directive'; -import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; -import { FindListOptions } from '../../../core/data/find-list-options.model'; describe('MetadataSchemaComponent', () => { let comp: MetadataSchemaComponent; diff --git a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts index cbcfae43d8c..88efd2a711e 100644 --- a/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts +++ b/src/app/admin/admin-sidebar/admin-sidebar.component.spec.ts @@ -16,7 +16,6 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { RouterTestingModule } from '@angular/router/testing'; import { ActivatedRoute } from '@angular/router'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import createSpy = jasmine.createSpy; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { Item } from '../../core/shared/item.model'; diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts index 0ddbefd2532..dff2e506c30 100644 --- a/src/app/admin/admin.module.ts +++ b/src/app/admin/admin.module.ts @@ -10,6 +10,7 @@ import { AdminSearchModule } from './admin-search-page/admin-search.module'; import { AdminSidebarSectionComponent } from './admin-sidebar/admin-sidebar-section/admin-sidebar-section.component'; import { ExpandableAdminSidebarSectionComponent } from './admin-sidebar/expandable-admin-sidebar-section/expandable-admin-sidebar-section.component'; import { BatchImportPageComponent } from './admin-import-batch-page/batch-import-page.component'; +import { UploadModule } from '../shared/upload/upload.module'; const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator @@ -25,7 +26,8 @@ const ENTRY_COMPONENTS = [ AccessControlModule, AdminSearchModule.withEntryComponents(), AdminWorkflowModuleModule.withEntryComponents(), - SharedModule + SharedModule, + UploadModule, ], declarations: [ AdminCurationTasksComponent, diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 40a7e38606e..ba7b7382278 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -31,7 +31,6 @@ import { models } from './core/core.module'; import { ThemeService } from './shared/theme-support/theme.service'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; import { distinctNext } from './core/shared/distinct-next'; -import { ModalBeforeDismiss } from './shared/interfaces/modal-before-dismiss.interface'; @Component({ selector: 'ds-app', diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 392969d0414..750d63beda1 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,14 +1,12 @@ import { APP_BASE_HREF, CommonModule, DOCUMENT } from '@angular/common'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { NgModule } from '@angular/core'; -import { AbstractControl } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { EffectsModule } from '@ngrx/effects'; import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store'; import { MetaReducer, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store'; -import { DYNAMIC_ERROR_MESSAGES_MATCHER, DYNAMIC_MATCHER_PROVIDERS, DynamicErrorMessagesMatcher } from '@ng-dynamic-forms/core'; import { TranslateModule } from '@ngx-translate/core'; import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to'; import { AppRoutingModule } from './app-routing.module'; @@ -28,7 +26,6 @@ import { XsrfInterceptor } from './core/xsrf/xsrf.interceptor'; import { LogInterceptor } from './core/log/log.interceptor'; import { EagerThemesModule } from '../themes/eager-themes.module'; import { APP_CONFIG, AppConfig } from '../config/app-config.interface'; -import { NgxMaskModule } from 'ngx-mask'; import { StoreDevModules } from '../config/store/devtools'; import { RootModule } from './root.module'; @@ -46,14 +43,6 @@ export function getMetaReducers(appConfig: AppConfig): MetaReducer[] { return appConfig.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers; } -/** - * 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 IMPORTS = [ CommonModule, SharedModule, @@ -64,7 +53,6 @@ const IMPORTS = [ ScrollToModule.forRoot(), NgbModule, TranslateModule.forRoot(), - NgxMaskModule.forRoot(), EffectsModule.forRoot(appEffects), StoreModule.forRoot(appReducers, storeModuleConfig), StoreRouterConnectingModule.forRoot(), @@ -113,11 +101,6 @@ const PROVIDERS = [ useClass: LogInterceptor, multi: true }, - { - provide: DYNAMIC_ERROR_MESSAGES_MATCHER, - useValue: ValidateEmailErrorStateMatcher - }, - ...DYNAMIC_MATCHER_PROVIDERS, ]; const DECLARATIONS = [ diff --git a/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts b/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts index 045582cb26e..aec8cd22f44 100644 --- a/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts +++ b/src/app/bitstream-page/legacy-bitstream-url.resolver.spec.ts @@ -1,5 +1,5 @@ import { LegacyBitstreamUrlResolver } from './legacy-bitstream-url.resolver'; -import { of as observableOf, EMPTY } from 'rxjs'; +import { EMPTY } from 'rxjs'; import { BitstreamDataService } from '../core/data/bitstream-data.service'; import { RemoteData } from '../core/data/remote-data'; import { TestScheduler } from 'rxjs/testing'; diff --git a/src/app/browse-by/browse-by-guard.spec.ts b/src/app/browse-by/browse-by-guard.spec.ts index fc483d87e23..933c95a3cb0 100644 --- a/src/app/browse-by/browse-by-guard.spec.ts +++ b/src/app/browse-by/browse-by-guard.spec.ts @@ -1,7 +1,6 @@ import { first } from 'rxjs/operators'; import { BrowseByGuard } from './browse-by-guard'; import { of as observableOf } from 'rxjs'; -import { BrowseDefinitionDataService } from '../core/browse/browse-definition-data.service'; import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; import { BrowseDefinition } from '../core/shared/browse-definition.model'; import { BrowseByDataType } from './browse-by-switcher/browse-by-decorator'; diff --git a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts index 8304b01daaa..db844b588f9 100644 --- a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts +++ b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts @@ -16,7 +16,7 @@ import { Collection } from '../../core/shared/collection.model'; import { RemoteData } from '../../core/data/remote-data'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { ChangeDetectionStrategy, EventEmitter } from '@angular/core'; +import { EventEmitter } from '@angular/core'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { By } from '@angular/platform-browser'; @@ -41,7 +41,7 @@ import { } from '../../shared/remote-data.utils'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; -import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; +import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; import { GroupDataService } from '../../core/eperson/group-data.service'; import { LinkHeadService } from '../../core/services/link-head.service'; diff --git a/src/app/collection-page/collection-page.module.ts b/src/app/collection-page/collection-page.module.ts index c35ebf9021e..ff49b983ff7 100644 --- a/src/app/collection-page/collection-page.module.ts +++ b/src/app/collection-page/collection-page.module.ts @@ -25,7 +25,7 @@ import { ComcolModule } from '../shared/comcol/comcol.module'; StatisticsModule.forRoot(), EditItemPageModule, CollectionFormModule, - ComcolModule + ComcolModule, ], declarations: [ CollectionPageComponent, @@ -38,7 +38,7 @@ import { ComcolModule } from '../shared/comcol/comcol.module'; ], providers: [ SearchService, - ] + ], }) export class CollectionPageModule { diff --git a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts index 51e8d926eb8..512faa53118 100644 --- a/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts +++ b/src/app/collection-page/edit-collection-page/collection-source/collection-source.component.ts @@ -8,8 +8,7 @@ import { DynamicInputModel, DynamicOptionControlModel, DynamicRadioGroupModel, - DynamicSelectModel, - DynamicTextAreaModel + DynamicSelectModel } from '@ng-dynamic-forms/core'; import { Location } from '@angular/common'; import { TranslateService } from '@ngx-translate/core'; diff --git a/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts b/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts index 45612be41a1..18f7feb6998 100644 --- a/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts +++ b/src/app/collection-page/edit-collection-page/edit-collection-page.module.ts @@ -25,7 +25,7 @@ import { ComcolModule } from '../../shared/comcol/comcol.module'; CollectionFormModule, ResourcePoliciesModule, FormModule, - ComcolModule + ComcolModule, ], declarations: [ EditCollectionPageComponent, diff --git a/src/app/community-page/community-page.module.ts b/src/app/community-page/community-page.module.ts index 7cf2c8db8a4..1dd9e824993 100644 --- a/src/app/community-page/community-page.module.ts +++ b/src/app/community-page/community-page.module.ts @@ -36,7 +36,7 @@ const DECLARATIONS = [CommunityPageComponent, CommunityPageRoutingModule, StatisticsModule.forRoot(), CommunityFormModule, - ComcolModule + ComcolModule, ], declarations: [ ...DECLARATIONS diff --git a/src/app/community-page/edit-community-page/edit-community-page.module.ts b/src/app/community-page/edit-community-page/edit-community-page.module.ts index 2b0fc73f2aa..0479ea6bc6f 100644 --- a/src/app/community-page/edit-community-page/edit-community-page.module.ts +++ b/src/app/community-page/edit-community-page/edit-community-page.module.ts @@ -21,7 +21,7 @@ import { ComcolModule } from '../../shared/comcol/comcol.module'; EditCommunityPageRoutingModule, CommunityFormModule, ComcolModule, - ResourcePoliciesModule + ResourcePoliciesModule, ], declarations: [ EditCommunityPageComponent, diff --git a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts b/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts index 6a9de52f1f2..bca3c42a950 100644 --- a/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts +++ b/src/app/community-page/sub-collection-list/community-page-sub-collection-list.component.spec.ts @@ -17,9 +17,6 @@ import { PageInfo } from '../../core/shared/page-info.model'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { of as observableOf } from 'rxjs'; import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; @@ -29,7 +26,6 @@ import { GroupDataService } from '../../core/eperson/group-data.service'; import { LinkHeadService } from '../../core/services/link-head.service'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { SearchConfigurationService } from '../../core/shared/search/search-configuration.service'; -import { SearchServiceStub } from '../../shared/testing/search-service.stub'; import { ConfigurationProperty } from '../../core/shared/configuration-property.model'; import { createPaginatedList } from '../../shared/testing/utils.test'; import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; diff --git a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts b/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts index c75c5b6f6c3..0a14fe6dd14 100644 --- a/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts +++ b/src/app/community-page/sub-community-list/community-page-sub-community-list.component.spec.ts @@ -17,9 +17,6 @@ import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { CommunityDataService } from '../../core/data/community-data.service'; import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; -import { of as observableOf } from 'rxjs'; import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; diff --git a/src/app/core/auth/selectors.ts b/src/app/core/auth/selectors.ts index 1d002b39082..ce8d38d6ba5 100644 --- a/src/app/core/auth/selectors.ts +++ b/src/app/core/auth/selectors.ts @@ -7,7 +7,6 @@ import { createSelector } from '@ngrx/store'; * notation packages up all of the exports into a single object. */ import { AuthState } from './auth.reducer'; -import { AppState } from '../../app.reducer'; import { CoreState } from '../core-state.model'; import { coreSelector } from '../core.selectors'; diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 90cefd54c70..ede23ba43b7 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -2,15 +2,12 @@ import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; -import { DynamicFormLayoutService, DynamicFormService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; import { EffectsModule } from '@ngrx/effects'; import { Action, StoreConfig, StoreModule } from '@ngrx/store'; import { MyDSpaceGuard } from '../my-dspace-page/my-dspace.guard'; import { isNotEmpty } from '../shared/empty.util'; -import { FormBuilderService } from '../shared/form/builder/form-builder.service'; -import { FormService } from '../shared/form/form.service'; import { HostWindowService } from '../shared/host-window.service'; import { MenuService } from '../shared/menu/menu.service'; import { EndpointMockingRestService } from '../shared/mocks/dspace-rest/endpoint-mocking-rest.service'; @@ -24,8 +21,6 @@ import { SelectableListService } from '../shared/object-list/selectable-list/sel import { ObjectSelectService } from '../shared/object-select/object-select.service'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; import { SidebarService } from '../shared/sidebar/sidebar.service'; -import { UploaderService } from '../shared/uploader/uploader.service'; -import { SectionFormOperationsService } from '../submission/sections/form/section-form-operations.service'; import { AuthenticatedGuard } from './auth/authenticated.guard'; import { AuthStatus } from './auth/models/auth-status.model'; import { BrowseService } from './browse/browse.service'; @@ -137,9 +132,6 @@ import { import { Registration } from './shared/registration.model'; import { MetadataSchemaDataService } from './data/metadata-schema-data.service'; import { MetadataFieldDataService } from './data/metadata-field-data.service'; -import { - DsDynamicTypeBindRelationService -} from '../shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service'; import { TokenResponseParsingService } from './auth/token-response-parsing.service'; import { SubmissionCcLicenseDataService } from './submission/submission-cc-license-data.service'; import { SubmissionCcLicence } from './submission/models/submission-cc-license.model'; @@ -149,7 +141,6 @@ import { VocabularyEntry } from './submission/vocabularies/models/vocabulary-ent import { Vocabulary } from './submission/vocabularies/models/vocabulary.model'; import { VocabularyEntryDetail } from './submission/vocabularies/models/vocabulary-entry-detail.model'; import { VocabularyService } from './submission/vocabularies/vocabulary.service'; -import { VocabularyTreeviewService } from '../shared/vocabulary-treeview/vocabulary-treeview.service'; import { ConfigurationDataService } from './data/configuration-data.service'; import { ConfigurationProperty } from './shared/configuration-property.model'; import { ReloadGuard } from './reload/reload.guard'; @@ -210,12 +201,6 @@ const PROVIDERS = [ DSOResponseParsingService, { provide: MOCK_RESPONSE_MAP, useValue: mockResponseMap }, { provide: DspaceRestService, useFactory: restServiceFactory, deps: [MOCK_RESPONSE_MAP, HttpClient] }, - DynamicFormLayoutService, - DynamicFormService, - DynamicFormValidationService, - FormBuilderService, - SectionFormOperationsService, - FormService, EPersonDataService, LinkHeadService, HALEndpointService, @@ -244,12 +229,10 @@ const PROVIDERS = [ SubmissionResponseParsingService, SubmissionJsonPatchOperationsService, JsonPatchOperationsBuilder, - UploaderService, UUIDService, NotificationsService, WorkspaceitemDataService, WorkflowItemDataService, - UploaderService, DSpaceObjectDataService, ConfigurationDataService, DSOChangeAnalyzer, @@ -266,7 +249,6 @@ const PROVIDERS = [ ClaimedTaskDataService, PoolTaskDataService, BitstreamDataService, - DsDynamicTypeBindRelationService, EntityTypeDataService, ContentSourceResponseParsingService, ItemTemplateDataService, @@ -302,7 +284,6 @@ const PROVIDERS = [ VocabularyService, VocabularyDataService, VocabularyEntryDetailsDataService, - VocabularyTreeviewService, SequenceService, GroupDataService, FeedbackDataService, diff --git a/src/app/shared/uploader/uploader.service.ts b/src/app/core/drag.service.ts similarity index 53% rename from src/app/shared/uploader/uploader.service.ts rename to src/app/core/drag.service.ts index 548de34f9c6..d5f329d3621 100644 --- a/src/app/shared/uploader/uploader.service.ts +++ b/src/app/core/drag.service.ts @@ -1,7 +1,17 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ + import { Injectable } from '@angular/core'; -@Injectable() -export class UploaderService { +@Injectable({ + providedIn: 'root' +}) +export class DragService { private _overrideDragOverPage = false; public overrideDragOverPage() { diff --git a/src/app/core/shared/hal-endpoint.service.spec.ts b/src/app/core/shared/hal-endpoint.service.spec.ts index 78a296496a7..56e890b3189 100644 --- a/src/app/core/shared/hal-endpoint.service.spec.ts +++ b/src/app/core/shared/hal-endpoint.service.spec.ts @@ -3,7 +3,7 @@ import { getMockRequestService } from '../../shared/mocks/request.service.mock'; import { RequestService } from '../data/request.service'; import { HALEndpointService } from './hal-endpoint.service'; import { EndpointMapRequest } from '../data/request.models'; -import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; +import { combineLatest as observableCombineLatest, of as observableOf } from 'rxjs'; import { environment } from '../../../environments/environment'; import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts index 2b1c6a573b9..9befd8ea134 100644 --- a/src/app/core/shared/search/search.service.ts +++ b/src/app/core/shared/search/search.service.ts @@ -3,7 +3,6 @@ import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { Injectable, OnDestroy } from '@angular/core'; import { map, switchMap, take } from 'rxjs/operators'; import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; -import { PaginatedList } from '../../data/paginated-list.model'; import { ResponseParsingService } from '../../data/parsing.service'; import { RemoteData } from '../../data/remote-data'; import { GetRequest } from '../../data/request.models'; diff --git a/src/app/correlation-id/correlation-id.service.spec.ts b/src/app/correlation-id/correlation-id.service.spec.ts index 64a4d1068a1..816c3694a5c 100644 --- a/src/app/correlation-id/correlation-id.service.spec.ts +++ b/src/app/correlation-id/correlation-id.service.spec.ts @@ -1,7 +1,7 @@ import { CorrelationIdService } from './correlation-id.service'; import { CookieServiceMock } from '../shared/mocks/cookie.service.mock'; import { UUIDService } from '../core/shared/uuid.service'; -import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { MockStore } from '@ngrx/store/testing'; import { TestBed } from '@angular/core/testing'; import { Store, StoreModule } from '@ngrx/store'; import { appReducers, AppState, storeModuleConfig } from '../app.reducer'; diff --git a/src/app/curation-form/curation-form.component.ts b/src/app/curation-form/curation-form.component.ts index 422c9550373..4b67580e770 100644 --- a/src/app/curation-form/curation-form.component.ts +++ b/src/app/curation-form/curation-form.component.ts @@ -5,7 +5,7 @@ import { getFirstCompletedRemoteData } from '../core/shared/operators'; import { find, map } from 'rxjs/operators'; import { NotificationsService } from '../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; -import { hasValue, isEmpty, isNotEmpty, hasNoValue } from '../shared/empty.util'; +import { hasValue, isEmpty, isNotEmpty } from '../shared/empty.util'; import { RemoteData } from '../core/data/remote-data'; import { Router } from '@angular/router'; import { ProcessDataService } from '../core/data/processes/process-data.service'; diff --git a/src/app/home-page/top-level-community-list/top-level-community-list.component.spec.ts b/src/app/home-page/top-level-community-list/top-level-community-list.component.spec.ts index 5dbf1ff8a27..d1a3d3631f1 100644 --- a/src/app/home-page/top-level-community-list/top-level-community-list.component.spec.ts +++ b/src/app/home-page/top-level-community-list/top-level-community-list.component.spec.ts @@ -17,9 +17,6 @@ import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { CommunityDataService } from '../../core/data/community-data.service'; import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service'; -import { of as observableOf } from 'rxjs'; -import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { PaginationService } from '../../core/pagination/pagination.service'; import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../shared/theme-support/theme.service'; diff --git a/src/app/item-page/bitstreams/upload/upload-bitstream.component.ts b/src/app/item-page/bitstreams/upload/upload-bitstream.component.ts index 1e5295a3476..74019de7cc6 100644 --- a/src/app/item-page/bitstreams/upload/upload-bitstream.component.ts +++ b/src/app/item-page/bitstreams/upload/upload-bitstream.component.ts @@ -4,7 +4,7 @@ import { RemoteData } from '../../../core/data/remote-data'; import { Item } from '../../../core/shared/item.model'; import { map, take, switchMap } from 'rxjs/operators'; import { ActivatedRoute, Router } from '@angular/router'; -import { UploaderOptions } from '../../../shared/uploader/uploader-options.model'; +import { UploaderOptions } from '../../../shared/upload/uploader/uploader-options.model'; import { hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util'; import { ItemDataService } from '../../../core/data/item-data.service'; import { AuthService } from '../../../core/auth/auth.service'; @@ -14,7 +14,7 @@ import { PaginatedList } from '../../../core/data/paginated-list.model'; import { Bundle } from '../../../core/shared/bundle.model'; import { BundleDataService } from '../../../core/data/bundle-data.service'; import { getFirstSucceededRemoteDataPayload, getFirstCompletedRemoteData } from '../../../core/shared/operators'; -import { UploaderComponent } from '../../../shared/uploader/uploader.component'; +import { UploaderComponent } from '../../../shared/upload/uploader/uploader.component'; import { RequestService } from '../../../core/data/request.service'; import { getBitstreamModuleRoute } from '../../../app-routing-paths'; import { getEntityEditRoute } from '../../item-page-routing-paths'; diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts index 2fe8a562c67..5d2afbaf4ce 100644 --- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.spec.ts @@ -1,12 +1,11 @@ -import { Observable } from 'rxjs/internal/Observable'; import { waitForAsync, ComponentFixture, inject, TestBed } from '@angular/core/testing'; import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { of as observableOf, of } from 'rxjs'; +import { of as observableOf } from 'rxjs'; import { TranslateModule } from '@ngx-translate/core'; import { cold } from 'jasmine-marbles'; -import { ItemAuthorizationsComponent, BitstreamMapValue } from './item-authorizations.component'; +import { ItemAuthorizationsComponent } from './item-authorizations.component'; import { Bitstream } from '../../../core/shared/bitstream.model'; import { Bundle } from '../../../core/shared/bundle.model'; import { Item } from '../../../core/shared/item.model'; @@ -14,8 +13,6 @@ import { LinkService } from '../../../core/cache/builders/link.service'; import { getMockLinkService } from '../../../shared/mocks/link-service.mock'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { createPaginatedList, createTestComponent } from '../../../shared/testing/utils.test'; -import { PaginatedList, buildPaginatedList } from '../../../core/data/paginated-list.model'; -import { PageInfo } from '../../../core/shared/page-info.model'; describe('ItemAuthorizationsComponent test suite', () => { let comp: ItemAuthorizationsComponent; diff --git a/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 b/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 index 133b13cb279..7317eb93be0 100644 --- a/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 +++ b/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 @@ -17,10 +17,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote- import { createPaginatedList } from '../../../../../shared/testing/utils.test'; import { RequestService } from '../../../../../core/data/request.service'; import { PaginationService } from '../../../../../core/pagination/pagination.service'; -import { PaginationComponentOptions } from '../../../../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../../../../core/cache/models/sort-options.model'; import { PaginationServiceStub } from '../../../../../shared/testing/pagination-service.stub'; -import { FindListOptions } from '../../../../../core/data/find-list-options.model'; describe('PaginatedDragAndDropBitstreamListComponent', () => { let comp: PaginatedDragAndDropBitstreamListComponent; diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts b/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts index 396e6c32160..6f0e97513f8 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.spec.ts @@ -16,11 +16,10 @@ import { MockBitstreamFormat1 } from '../../../../shared/mocks/item.mock'; import { By } from '@angular/platform-browser'; import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub'; -import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; -import { SortDirection, SortOptions } from '../../../../core/cache/models/sort-options.model'; 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 { APP_CONFIG } from 'src/config/app-config.interface'; +import { environment } from 'src/environments/environment'; describe('FullFileSectionComponent', () => { let comp: FullFileSectionComponent; @@ -72,7 +71,8 @@ describe('FullFileSectionComponent', () => { providers: [ { provide: BitstreamDataService, useValue: bitstreamDataService }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, - { provide: PaginationService, useValue: paginationService } + { provide: PaginationService, useValue: paginationService }, + { provide: APP_CONFIG, useValue: environment }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.ts b/src/app/item-page/full/field-components/file-section/full-file-section.component.ts index e21c1a32eb3..3be0d58c819 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.ts +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; @@ -14,6 +14,7 @@ import { NotificationsService } from '../../../../shared/notifications/notificat import { TranslateService } from '@ngx-translate/core'; import { hasValue, isEmpty } from '../../../../shared/empty.util'; import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; /** * This component renders the file section of the item @@ -34,26 +35,26 @@ export class FullFileSectionComponent extends FileSectionComponent implements On originals$: Observable>>; licenses$: Observable>>; - pageSize = 5; originalOptions = Object.assign(new PaginationComponentOptions(), { id: 'obo', currentPage: 1, - pageSize: this.pageSize + pageSize: this.appConfig.item.bitstream.pageSize }); licenseOptions = Object.assign(new PaginationComponentOptions(), { id: 'lbo', currentPage: 1, - pageSize: this.pageSize + pageSize: this.appConfig.item.bitstream.pageSize }); constructor( bitstreamDataService: BitstreamDataService, protected notificationsService: NotificationsService, protected translateService: TranslateService, - protected paginationService: PaginationService + protected paginationService: PaginationService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(bitstreamDataService, notificationsService, translateService); + super(bitstreamDataService, notificationsService, translateService, appConfig); } ngOnInit(): void { diff --git a/src/app/item-page/item-page.module.ts b/src/app/item-page/item-page.module.ts index c4e86a37fba..de9f2f60c5f 100644 --- a/src/app/item-page/item-page.module.ts +++ b/src/app/item-page/item-page.module.ts @@ -46,6 +46,7 @@ import { OrcidPageComponent } from './orcid-page/orcid-page.component'; import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; import { OrcidSyncSettingsComponent } from './orcid-page/orcid-sync-settings/orcid-sync-settings.component'; import { OrcidQueueComponent } from './orcid-page/orcid-queue/orcid-queue.component'; +import { UploadModule } from '../shared/upload/upload.module'; const ENTRY_COMPONENTS = [ @@ -94,7 +95,8 @@ const DECLARATIONS = [ JournalEntitiesModule.withEntryComponents(), ResearchEntitiesModule.withEntryComponents(), NgxGalleryModule, - NgbAccordionModule + NgbAccordionModule, + UploadModule, ], declarations: [ ...DECLARATIONS, diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts index 2d185aef9ca..83f92d5af8b 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.spec.ts @@ -17,6 +17,8 @@ import { MetadataFieldWrapperComponent } from '../../../field-components/metadat import { createPaginatedList } from '../../../../shared/testing/utils.test'; import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub'; +import { APP_CONFIG } from 'src/config/app-config.interface'; +import { environment } from 'src/environments/environment'; describe('FileSectionComponent', () => { let comp: FileSectionComponent; @@ -65,7 +67,8 @@ describe('FileSectionComponent', () => { declarations: [FileSectionComponent, VarDirective, FileSizePipe, MetadataFieldWrapperComponent], providers: [ { provide: BitstreamDataService, useValue: bitstreamDataService }, - { provide: NotificationsService, useValue: new NotificationsServiceStub() } + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: APP_CONFIG, useValue: environment } ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.ts b/src/app/item-page/simple/field-components/file-section/file-section.component.ts index d28b5799963..08e792fc8b7 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.ts +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; @@ -10,6 +10,7 @@ import { PaginatedList } from '../../../../core/data/paginated-list.model'; import { NotificationsService } from '../../../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; +import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; /** * This component renders the file section of the item @@ -35,13 +36,15 @@ export class FileSectionComponent implements OnInit { isLastPage: boolean; - pageSize = 5; + pageSize: number; constructor( protected bitstreamDataService: BitstreamDataService, protected notificationsService: NotificationsService, - protected translateService: TranslateService + protected translateService: TranslateService, + @Inject(APP_CONFIG) protected appConfig: AppConfig ) { + this.pageSize = this.appConfig.item.bitstream.pageSize; } ngOnInit(): void { diff --git a/src/app/item-page/simple/item-types/shared/item.component.spec.ts b/src/app/item-page/simple/item-types/shared/item.component.spec.ts index b14e56a58e1..c479f048090 100644 --- a/src/app/item-page/simple/item-types/shared/item.component.spec.ts +++ b/src/app/item-page/simple/item-types/shared/item.component.spec.ts @@ -28,7 +28,6 @@ import { TruncatableService } from '../../../../shared/truncatable/truncatable.s import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; import { compareArraysUsing, compareArraysUsingIds } from './item-relationships-utils'; -import { ItemComponent } from './item.component'; import { createPaginatedList } from '../../../../shared/testing/utils.test'; import { RouteService } from '../../../../core/services/route.service'; import { MetadataValue } from '../../../../core/shared/metadata.models'; diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts index b87c77c5e99..669a97764a5 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts @@ -11,10 +11,6 @@ import { cold, hot } from 'jasmine-marbles'; import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type'; import { PaginationServiceStub } from '../shared/testing/pagination-service.stub'; import { Context } from '../core/shared/context.model'; -import { LinkService } from '../core/cache/builders/link.service'; -import { HALEndpointService } from '../core/shared/hal-endpoint.service'; -import { RequestService } from '../core/data/request.service'; -import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service'; import { HALEndpointServiceStub } from '../shared/testing/hal-endpoint-service.stub'; import { getMockRemoteDataBuildService } from '../shared/mocks/remote-data-build.service.mock'; diff --git a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts index fb43c253eb5..ed61fab1d62 100644 --- a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts +++ b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.spec.ts @@ -16,10 +16,10 @@ import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { getMockScrollToService } from '../../shared/mocks/scroll-to-service.mock'; -import { UploaderService } from '../../shared/uploader/uploader.service'; +import { DragService } from '../../core/drag.service'; import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; -import { UploaderComponent } from '../../shared/uploader/uploader.component'; +import { UploaderComponent } from '../../shared/upload/uploader/uploader.component'; import { HttpXsrfTokenExtractor } from '@angular/common/http'; import { CookieService } from '../../core/services/cookie.service'; import { CookieServiceMock } from '../../shared/mocks/cookie.service.mock'; @@ -59,7 +59,7 @@ describe('MyDSpaceNewSubmissionComponent test', () => { NgbModal, ChangeDetectorRef, MyDSpaceNewSubmissionComponent, - UploaderService, + DragService, { provide: HttpXsrfTokenExtractor, useValue: new HttpXsrfTokenExtractorMock('mock-token') }, { provide: CookieService, useValue: new CookieServiceMock() }, { provide: HostWindowService, useValue: new HostWindowServiceStub(800) }, diff --git a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts index b2ba6fe2af7..0694fc63bfe 100644 --- a/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts +++ b/src/app/my-dspace-page/my-dspace-new-submission/my-dspace-new-submission.component.ts @@ -8,13 +8,13 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { AuthService } from '../../core/auth/auth.service'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { UploaderOptions } from '../../shared/uploader/uploader-options.model'; +import { UploaderOptions } from '../../shared/upload/uploader/uploader-options.model'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { hasValue } from '../../shared/empty.util'; import { SearchResult } from '../../shared/search/models/search-result.model'; import { CollectionSelectorComponent } from '../collection-selector/collection-selector.component'; -import { UploaderComponent } from '../../shared/uploader/uploader.component'; -import { UploaderError } from '../../shared/uploader/uploader-error.model'; +import { UploaderComponent } from '../../shared/upload/uploader/uploader.component'; +import { UploaderError } from '../../shared/upload/uploader/uploader-error.model'; import { Router } from '@angular/router'; /** diff --git a/src/app/my-dspace-page/my-dspace-page.module.ts b/src/app/my-dspace-page/my-dspace-page.module.ts index 2ccddd87f74..6ad50af96a5 100644 --- a/src/app/my-dspace-page/my-dspace-page.module.ts +++ b/src/app/my-dspace-page/my-dspace-page.module.ts @@ -14,6 +14,7 @@ import { MyDSpaceNewSubmissionDropdownComponent } from './my-dspace-new-submissi import { MyDSpaceNewExternalDropdownComponent } from './my-dspace-new-submission/my-dspace-new-external-dropdown/my-dspace-new-external-dropdown.component'; import { ThemedMyDSpacePageComponent } from './themed-my-dspace-page.component'; import { SearchModule } from '../shared/search/search.module'; +import { UploadModule } from '../shared/upload/upload.module'; const DECLARATIONS = [ MyDSpacePageComponent, @@ -30,7 +31,8 @@ const DECLARATIONS = [ SharedModule, SearchModule, MyDspacePageRoutingModule, - MyDspaceSearchModule.withEntryComponents() + MyDspaceSearchModule.withEntryComponents(), + UploadModule, ], declarations: DECLARATIONS, providers: [ diff --git a/src/app/navbar/navbar.module.ts b/src/app/navbar/navbar.module.ts index af2bf036bdf..de3244099d7 100644 --- a/src/app/navbar/navbar.module.ts +++ b/src/app/navbar/navbar.module.ts @@ -21,6 +21,7 @@ const effects = [ const ENTRY_COMPONENTS = [ // put only entry components that use custom decorator NavbarSectionComponent, + ExpandableNavbarSectionComponent, ThemedExpandableNavbarSectionComponent, ]; @@ -34,11 +35,9 @@ const ENTRY_COMPONENTS = [ CoreModule.forRoot() ], declarations: [ + ...ENTRY_COMPONENTS, NavbarComponent, ThemedNavbarComponent, - NavbarSectionComponent, - ExpandableNavbarSectionComponent, - ThemedExpandableNavbarSectionComponent, ], providers: [], exports: [ diff --git a/src/app/process-page/detail/process-detail.component.spec.ts b/src/app/process-page/detail/process-detail.component.spec.ts index e4ab7d1082d..8749553eaeb 100644 --- a/src/app/process-page/detail/process-detail.component.spec.ts +++ b/src/app/process-page/detail/process-detail.component.spec.ts @@ -15,7 +15,6 @@ import { } from '@angular/core/testing'; import { VarDirective } from '../../shared/utils/var.directive'; import { TranslateModule } from '@ngx-translate/core'; -import { RouterTestingModule } from '@angular/router/testing'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ProcessDetailFieldComponent } from './process-detail-field/process-detail-field.component'; import { Process } from '../processes/process.model'; diff --git a/src/app/register-email-form/register-email-form.component.spec.ts b/src/app/register-email-form/register-email-form.component.spec.ts index bac922c73b3..cf3b4b13d28 100644 --- a/src/app/register-email-form/register-email-form.component.spec.ts +++ b/src/app/register-email-form/register-email-form.component.spec.ts @@ -95,6 +95,10 @@ describe('RegisterEmailComponent', () => { comp.form.patchValue({email: 'valid@email.org'}); expect(comp.form.invalid).toBeFalse(); }); + it('should be valid when uppercase letters are used', () => { + comp.form.patchValue({email: 'VALID@email.org'}); + expect(comp.form.invalid).toBeFalse(); + }); }); describe('register', () => { it('should send a registration to the service and on success display a message and return to home', () => { diff --git a/src/app/register-email-form/register-email-form.component.ts b/src/app/register-email-form/register-email-form.component.ts index ced87b9e75e..561bd53e674 100644 --- a/src/app/register-email-form/register-email-form.component.ts +++ b/src/app/register-email-form/register-email-form.component.ts @@ -79,7 +79,9 @@ export class RegisterEmailFormComponent implements OnInit { this.form = this.formBuilder.group({ email: new FormControl('', { validators: [Validators.required, - Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$') + // Regex pattern borrowed from HTML5 specs for a valid email address: + // https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address + Validators.pattern('^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$') ], }) }); diff --git a/src/app/register-page/registration.guard.spec.ts b/src/app/register-page/registration.guard.spec.ts index 89eaff7a028..9fb8dd3a338 100644 --- a/src/app/register-page/registration.guard.spec.ts +++ b/src/app/register-page/registration.guard.spec.ts @@ -2,7 +2,6 @@ import { RegistrationGuard } from './registration.guard'; import { EpersonRegistrationService } from '../core/data/eperson-registration.service'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { AuthService } from '../core/auth/auth.service'; -import { Location } from '@angular/common'; import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject, diff --git a/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts b/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts index 29be240753f..23dfca86165 100644 --- a/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts +++ b/src/app/shared/comcol/comcol-forms/comcol-form/comcol-form.component.ts @@ -17,8 +17,8 @@ import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.mod import { ResourceType } from '../../../../core/shared/resource-type'; import { hasValue, isNotEmpty } from '../../../empty.util'; import { NotificationsService } from '../../../notifications/notifications.service'; -import { UploaderOptions } from '../../../uploader/uploader-options.model'; -import { UploaderComponent } from '../../../uploader/uploader.component'; +import { UploaderOptions } from '../../../upload/uploader/uploader-options.model'; +import { UploaderComponent } from '../../../upload/uploader/uploader.component'; import { Operation } from 'fast-json-patch'; import { NoContent } from '../../../../core/shared/NoContent.model'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; diff --git a/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts b/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts index bc73e4134b5..1040e31c579 100644 --- a/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts +++ b/src/app/shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component.spec.ts @@ -11,7 +11,6 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { DeleteComColPageComponent } from './delete-comcol-page.component'; import { NotificationsService } from '../../../notifications/notifications.service'; import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; -import { RequestService } from '../../../../core/data/request.service'; import { getTestScheduler } from 'jasmine-marbles'; import { ComColDataService } from '../../../../core/data/comcol-data.service'; import { createFailedRemoteDataObject$, createNoContentRemoteDataObject$ } from '../../../remote-data.utils'; diff --git a/src/app/shared/comcol/comcol.module.ts b/src/app/shared/comcol/comcol.module.ts index 094387929ae..efbcedf2c67 100644 --- a/src/app/shared/comcol/comcol.module.ts +++ b/src/app/shared/comcol/comcol.module.ts @@ -15,6 +15,7 @@ import { ThemedComcolPageBrowseByComponent } from './comcol-page-browse-by/theme import { ComcolRoleComponent } from './comcol-forms/edit-comcol-page/comcol-role/comcol-role.component'; import { SharedModule } from '../shared.module'; import { FormModule } from '../form/form.module'; +import { UploadModule } from '../upload/upload.module'; const COMPONENTS = [ ComcolPageContentComponent, @@ -28,9 +29,7 @@ const COMPONENTS = [ ComcolPageBrowseByComponent, ThemedComcolPageBrowseByComponent, ComcolRoleComponent, - ThemedComcolPageHandleComponent - ]; @NgModule({ @@ -40,10 +39,12 @@ const COMPONENTS = [ imports: [ CommonModule, FormModule, - SharedModule + SharedModule, + UploadModule, ], exports: [ - ...COMPONENTS + ...COMPONENTS, + UploadModule, ] }) export class ComcolModule { } diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts index 56e371242ba..3e830811b3b 100644 --- a/src/app/shared/cookies/browser-klaro.service.ts +++ b/src/app/shared/cookies/browser-klaro.service.ts @@ -1,5 +1,4 @@ -import { Injectable } from '@angular/core'; -import { setup, show } from 'klaro/dist/klaro-no-translations'; +import { Inject, Injectable, InjectionToken } from '@angular/core'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; import { AuthService } from '../../core/auth/auth.service'; import { TranslateService } from '@ngx-translate/core'; @@ -43,6 +42,17 @@ const cookiePurposeMessagePrefix = 'cookies.consent.purpose.'; */ const updateDebounce = 300; +/** + * By using this injection token instead of importing directly we can keep Klaro out of the main bundle + */ +const LAZY_KLARO = new InjectionToken>( + 'Lazily loaded Klaro', + { + providedIn: 'root', + factory: async () => (await import('klaro')), + } +); + /** * Browser implementation for the KlaroService, representing a service for handling Klaro consent preferences and UI */ @@ -65,7 +75,9 @@ export class BrowserKlaroService extends KlaroService { private authService: AuthService, private ePersonService: EPersonDataService, private configService: ConfigurationDataService, - private cookieService: CookieService) { + private cookieService: CookieService, + @Inject(LAZY_KLARO) private lazyKlaro: Promise, + ) { super(); } @@ -135,8 +147,7 @@ export class BrowserKlaroService extends KlaroService { this.translateConfiguration(); this.klaroConfig.services = this.filterConfigServices(servicesToHide); - - setup(this.klaroConfig); + this.lazyKlaro.then(({ setup }) => setup(this.klaroConfig)); }); } @@ -220,7 +231,7 @@ export class BrowserKlaroService extends KlaroService { * Show the cookie consent form */ showSettings() { - show(this.klaroConfig); + this.lazyKlaro.then(({show}) => show(this.klaroConfig)); } /** diff --git a/src/app/shared/dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component.ts b/src/app/shared/dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component.ts index ab48d058ca6..cc1f9822d67 100644 --- a/src/app/shared/dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component.ts +++ b/src/app/shared/dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component.ts @@ -53,8 +53,9 @@ export class AuthorizedCollectionSelectorComponent extends DSOSelectorComponent * Perform a search for authorized collections with the current query and page * @param query Query to search objects for * @param page Page to retrieve + * @param useCache Whether or not to use the cache */ - search(query: string, page: number): Observable>>> { + search(query: string, page: number, useCache: boolean = true): Observable>>> { let searchListService$: Observable>> = null; const findOptions: FindListOptions = { currentPage: page, @@ -69,7 +70,7 @@ export class AuthorizedCollectionSelectorComponent extends DSOSelectorComponent findOptions); } else { searchListService$ = this.collectionDataService - .getAuthorizedCollection(query, findOptions, true, false, followLink('parentCommunity')); + .getAuthorizedCollection(query, findOptions, useCache, false, followLink('parentCommunity')); } return searchListService$.pipe( getFirstCompletedRemoteData(), diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html index 8abb8ad5584..c4f5dbc4cd6 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html @@ -21,12 +21,12 @@