Merge branch 'main' into task/main/CST-15591

This commit is contained in:
Alisa Ismailati
2024-10-09 16:51:37 +02:00
170 changed files with 25548 additions and 12844 deletions

View File

@@ -25,4 +25,6 @@ npm-debug.log.*
# Webpack files
webpack.records.json
package-lock.json
# Yarn no longer used
yarn.lock

View File

@@ -165,6 +165,7 @@
"@angular-eslint/no-output-native": "warn",
"@angular-eslint/no-output-on-prefix": "warn",
"@angular-eslint/no-conflicting-lifecycle": "warn",
"@angular-eslint/use-lifecycle-interface": "error",
"@typescript-eslint/no-inferrable-types":[
"error",

View File

@@ -21,8 +21,8 @@ However, reviewers may request that you complete any actions in this list if you
- [ ] My PR is **created against the `main` branch** of code (unless it is a backport or is fixing an issue specific to an older branch).
- [ ] 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 [ESLint](https://eslint.org/)** validation using `yarn lint`
- [ ] My PR **doesn't introduce circular dependencies** (verified via `yarn check-circ-deps`)
- [ ] My PR **passes [ESLint](https://eslint.org/)** validation using `npm run lint`
- [ ] My PR **doesn't introduce circular dependencies** (verified via `npm run 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).
- [ ] My PR **aligns with [Accessibility guidelines](https://wiki.lyrasis.org/display/DSDOC8x/Accessibility)** if it makes changes to the user interface.

View File

@@ -69,39 +69,39 @@ jobs:
fi
google-chrome --version
# https://github.com/actions/cache/blob/main/examples.md#node---yarn
- name: Get Yarn cache directory
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- name: Cache Yarn dependencies
# https://github.com/actions/cache/blob/main/examples.md#node---npm
- name: Get NPM cache directory
id: npm-cache-dir
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
- name: Cache NPM dependencies
uses: actions/cache@v4
with:
# Cache entire Yarn cache directory (see previous step)
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
# Cache key is hash of yarn.lock. Therefore changes to yarn.lock will invalidate cache
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn-
# Cache entire NPM cache directory (see previous step)
path: ${{ steps.npm-cache-dir.outputs.dir }}
# Cache key is hash of package-lock.json. Therefore changes to package-lock.json will invalidate cache
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: ${{ runner.os }}-npm-
- name: Install Yarn dependencies
run: yarn install --frozen-lockfile
- name: Install NPM dependencies
run: npm clean-install
- name: Build lint plugins
run: yarn run build:lint
run: npm run build:lint
- name: Run lint plugin tests
run: yarn run test:lint:nobuild
run: npm run test:lint:nobuild
- name: Run lint
run: yarn run lint:nobuild --quiet
run: npm run lint:nobuild -- --quiet
- name: Check for circular dependencies
run: yarn run check-circ-deps
run: npm run check-circ-deps
- name: Run build
run: yarn run build:prod
run: npm run build:prod
- name: Run specs (unit tests)
run: yarn run test:headless
run: npm run test:headless
# Upload code coverage report to artifact (for one version of Node only),
# so that it can be shared with the 'codecov' job (see below)
@@ -131,7 +131,7 @@ jobs:
# Run tests in Chrome, headless mode (default)
browser: chrome
# Start app before running tests (will be stopped automatically after tests finish)
start: yarn run serve:ssr
start: npm 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://127.0.0.1:8080/server/api/core/sites, http://127.0.0.1:4000
@@ -167,7 +167,7 @@ jobs:
# Start up the app with SSR enabled (run in background)
- name: Start app in SSR (server-side rendering) mode
run: |
nohup yarn run serve:ssr &
nohup npm run serve:ssr &
printf 'Waiting for app to start'
until curl --output /dev/null --silent --head --fail http://127.0.0.1:4000/home; do
printf '.'

4
.gitignore vendored
View File

@@ -28,12 +28,12 @@ webpack.records.json
morgan.log
# Yarn no longer used
yarn.lock
yarn-error.log
*.css
package-lock.json
.java-version
.env

View File

@@ -11,9 +11,7 @@ WORKDIR /app
ADD . /app/
EXPOSE 4000
# We run yarn install with an increased network timeout (5min) to avoid "ESOCKETTIMEDOUT" errors from hub.docker.com
# See, for example https://github.com/yarnpkg/yarn/issues/5540
RUN yarn install --network-timeout 300000
RUN npm install
# When running in dev mode, 4GB of memory is required to build & launch the app.
# This default setting can be overridden as needed in your shell, via an env file or in docker-compose.
@@ -25,4 +23,4 @@ ENV NODE_OPTIONS="--max_old_space_size=4096"
# NOTE: At this time it is only possible to run Docker container in Production mode
# if you have a public URL. See https://github.com/DSpace/dspace-angular/issues/1485
ENV NODE_ENV development
CMD yarn serve --host 0.0.0.0
CMD npm run serve -- --host 0.0.0.0

View File

@@ -11,11 +11,11 @@ FROM node:18-alpine AS build
RUN apk add --update python3 make g++ && rm -rf /var/cache/apk/*
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --network-timeout 300000
COPY package.json package-lock.json ./
RUN npm install
ADD . /app/
RUN yarn build:prod
RUN npm run build:prod
FROM node:18-alpine
RUN npm install --global pm2

View File

@@ -35,7 +35,7 @@ https://wiki.lyrasis.org/display/DSDOC7x/Installing+DSpace
Quick start
-----------
**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`**
**Ensure you're running [Node](https://nodejs.org) `v16.x` or `v18.x`, [npm](https://www.npmjs.com/) >= `v5.x`**
```bash
# clone the repo
@@ -45,10 +45,10 @@ git clone https://github.com/DSpace/dspace-angular.git
cd dspace-angular
# install the local dependencies
yarn install
npm install
# start the server
yarn start
npm start
```
Then go to [http://localhost:4000](http://localhost:4000) in your browser
@@ -77,7 +77,7 @@ Table of Contents
- [Recommended Editors/IDEs](#recommended-editorsides)
- [Collaborating](#collaborating)
- [File Structure](#file-structure)
- [Managing Dependencies (via yarn)](#managing-dependencies-via-yarn)
- [Managing Dependencies (via npm)](#managing-dependencies-via-npm)
- [Frequently asked questions](#frequently-asked-questions)
- [License](#license)
@@ -89,15 +89,15 @@ You can find more information on the technologies used in this project (Angular.
Requirements
------------
- [Node.js](https://nodejs.org) and [yarn](https://yarnpkg.com)
- Ensure you're running node `v16.x` or `v18.x` and yarn == `v1.x`
- [Node.js](https://nodejs.org)
- Ensure you're running node `v16.x` or `v18.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.
Installing
----------
- `yarn install` to install the local dependencies
- `npm install` to install the local dependencies
### Configuring
@@ -202,7 +202,7 @@ import { environment } from '../environment.ts';
Running the app
---------------
After you have installed all dependencies you can now run the app. Run `yarn run start:dev` to start a local server which will watch for changes, rebuild the code, and reload the server for you. You can visit it at `http://localhost:4000`.
After you have installed all dependencies you can now run the app. Run `npm run start:dev` to start a local server which will watch for changes, rebuild the code, and reload the server for you. You can visit it at `http://localhost:4000`.
### Running in production mode
@@ -211,20 +211,20 @@ When building for production we're using Ahead of Time (AoT) compilation. With A
To build the app for production and start the server (in one command) run:
```bash
yarn start
npm start
```
This will run the application in an instance of the Express server, which is included.
If you only want to build for production, without starting, run:
```bash
yarn run build:prod
npm run build:prod
```
This will build the application and put the result in the `dist` folder. You can copy this folder to wherever you need it for your application server. If you will be using the built-in Express server, you'll also need a copy of the `node_modules` folder tucked inside your copy of `dist`.
After building the app for production, it can be started by running:
```bash
yarn run serve:ssr
npm run serve:ssr
```
### Running the application with Docker
@@ -238,14 +238,14 @@ Cleaning
--------
```bash
# clean everything, including node_modules. You'll need to run yarn install again afterwards.
yarn run clean
# clean everything, including node_modules. You'll need to run npm install again afterwards.
npm run clean
# clean files generated by the production build (.ngfactory files, css files, etc)
yarn run clean:prod
npm run clean:prod
# cleans the distribution directory
yarn run clean:dist
npm run clean:dist
```
@@ -259,9 +259,9 @@ If you would like to contribute by testing a Pull Request (PR), here's how to do
1. Pull down the branch that the Pull Request was built from. Easy instructions for doing so can be found on the Pull Request itself.
* Next to the "Merge" button, you'll see a link that says "command line instructions".
* Click it, and follow "Step 1" of those instructions to checkout the pull down the PR branch.
2. `yarn run clean` (This resets your local dependencies to ensure you are up-to-date with this PR)
3. `yarn install` (Updates your local dependencies to those in the PR)
4. `yarn start` (Rebuilds the project, and deploys to localhost:4000, by default)
2. `npm run clean` (This resets your local dependencies to ensure you are up-to-date with this PR)
3. `npm install` (Updates your local dependencies to those in the PR)
4. `npm start` (Rebuilds the project, and deploys to localhost:4000, by default)
5. At this point, the code from the PR will be deployed to http://localhost:4000. Test it out, and ensure that it does what is described in the PR (or fixes the bug described in the ticket linked to the PR).
Once you have tested the Pull Request, please add a comment and/or approval to the PR to let us know whether you found it to be successful (or not). Thanks!
@@ -271,13 +271,13 @@ Once you have tested the Pull Request, please add a comment and/or approval to t
Unit tests use the [Jasmine test framework](https://jasmine.github.io/), and are run via [Karma](https://karma-runner.github.io/).
You can find the Karma configuration file at the same level of this README file:`./karma.conf.js` If you are going to use a remote test environment you need to edit the `./karma.conf.js`. Follow the instructions you will find inside it. To executing tests whenever any file changes you can modify the 'autoWatch' option to 'true' and 'singleRun' option to 'false'. A coverage report is also available at: http://localhost:9876/ after you run: `yarn run coverage`.
You can find the Karma configuration file at the same level of this README file:`./karma.conf.js` If you are going to use a remote test environment you need to edit the `./karma.conf.js`. Follow the instructions you will find inside it. To executing tests whenever any file changes you can modify the 'autoWatch' option to 'true' and 'singleRun' option to 'false'. A coverage report is also available at: http://localhost:9876/ after you run: `npm run coverage`.
The default browser is Google Chrome.
Place your tests in the same location of the application source code files that they test, e.g. ending with `*.component.spec.ts`
and run: `yarn test`
and run: `npm test`
If you run into odd test errors, see the Angular guide to debugging tests: https://angular.io/guide/test-debugging
@@ -357,14 +357,14 @@ Some UI specific configuration documentation is also found in the [`./docs`](doc
To build the code documentation we use [TYPEDOC](http://typedoc.org). TYPEDOC is a documentation generator for TypeScript projects. It extracts information from properly formatted comments that can be written within the code files. Follow the instructions [here](http://typedoc.org/guides/doccomments/) to know how to make those comments.
Run:`yarn run docs` to produce the documentation that will be available in the 'doc' folder.
Run:`npm run docs` to produce the documentation that will be available in the 'doc' folder.
Other commands
--------------
There are many more commands in the `scripts` section of `package.json`. Most of these are executed by one of the commands mentioned above.
A command with a name that starts with `pre` or `post` will be executed automatically before or after the script with the matching name. e.g. if you type `yarn run start` the `prestart` script will run first, then the `start` script will trigger.
A command with a name that starts with `pre` or `post` will be executed automatically before or after the script with the matching name. e.g. if you type `npm run start` the `prestart` script will run first, then the `start` script will trigger.
Recommended Editors/IDEs
------------------------
@@ -456,6 +456,7 @@ dspace-angular
├── LICENSES_THIRD_PARTY *
├── nodemon.json * Nodemon (https://nodemon.io/) configuration
├── package.json * This file describes the npm package for this project, its dependencies, scripts, etc.
├── package-lock.json * npm lockfile (https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json)
├── postcss.config.js * PostCSS (http://postcss.org/) configuration
├── README.md * This document
├── SECURITY.md *
@@ -466,30 +467,29 @@ dspace-angular
├── tsconfig.spec.json * TypeScript config for tests
├── tsconfig.ts-node.json * TypeScript config for using ts-node directly
├── tslint.json * TSLint (https://palantir.github.io/tslint/) configuration
── typedoc.json * TYPEDOC configuration
└── yarn.lock * Yarn lockfile (https://yarnpkg.com/en/docs/yarn-lock)
── typedoc.json * TYPEDOC configuration
```
Managing Dependencies (via yarn)
Managing Dependencies (via npm)
-------------
This project makes use of [`yarn`](https://yarnpkg.com/en/) to ensure that the exact same dependency versions are used every time you install it.
This project makes use of [`npm`](https://docs.npmjs.com/about-npm) to ensure that the exact same dependency versions are used every time you install it.
* `yarn` creates a [`yarn.lock`](https://yarnpkg.com/en/docs/yarn-lock) to track those versions. That file is updated automatically by whenever dependencies are added/updated/removed via yarn.
* **Adding new dependencies**: To install/add a new dependency (third party library), use [`yarn add`](https://yarnpkg.com/en/docs/cli/add). For example: `yarn add some-lib`.
* If you are adding a new build tool dependency (to `devDependencies`), use `yarn add some-lib --dev`
* **Upgrading existing dependencies**: To upgrade existing dependencies, you can use [`yarn upgrade`](https://yarnpkg.com/en/docs/cli/upgrade). For example: `yarn upgrade some-lib` or `yarn upgrade some-lib@version`
* **Removing dependencies**: If a dependency is no longer needed, or replaced, use [`yarn remove`](https://yarnpkg.com/en/docs/cli/remove) to remove it.
* `npm` creates a [`package-lock.json`](https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json) to track those versions. That file is updated automatically by whenever dependencies are added/updated/removed via npm.
* **Adding new dependencies**: To install/add a new dependency (third party library), use [`npm install`](https://docs.npmjs.com/cli/v10/commands/npm-install). For example: `npm install some-lib`.
* If you are adding a new build tool dependency (to `devDependencies`), use `npm install some-lib --save--dev`
* **Upgrading existing dependencies**: To upgrade existing dependencies, you can use [`npm update`](https://docs.npmjs.com/cli/v10/commands/npm-update). For example: `npm update some-lib` or `npm update some-lib@version`
* **Removing dependencies**: If a dependency is no longer needed, or replaced, use [`npm uninstall`](https://docs.npmjs.com/cli/v10/commands/npm-uninstall) to remove it.
As you can see above, using `yarn` commandline tools means that you should never need to modify the `package.json` manually. *We recommend always using `yarn` to keep dependencies updated / in sync.*
As you can see above, using `npm` commandline tools means that you should never need to modify the `package.json` manually. *We recommend always using `npm` to keep dependencies updated / in sync.*
### Adding Typings for libraries
If the library does not include typings, you can install them using yarn:
If the library does not include typings, you can install them using npm:
```bash
yarn add d3
yarn add @types/d3 --dev
npm install d3
npm install @types/d3 --save-dev
```
If the library doesn't have typings available at `@types/`, you can still use it by manually adding typings for it:
@@ -527,13 +527,13 @@ Frequently asked questions
- What are the naming conventions for Angular?
- See [the official angular style guide](https://angular.io/styleguide)
- Why is the size of my app larger in development?
- The production build uses a whole host of techniques (ahead-of-time compilation, rollup to remove unreachable code, minification, etc.) to reduce the size, that aren't used during development in the intrest of build speed.
- node-pre-gyp ERR in yarn install (Windows)
- The production build uses a whole host of techniques (ahead-of-time compilation, rollup to remove unreachable code, minification, etc.) to reduce the size, that aren't used during development in the interest of build speed.
- node-pre-gyp ERR in npm install (Windows)
- install Python x86 version between 2.5 and 3.0 on windows. See [this issue](https://github.com/AngularClass/angular2-webpack-starter/issues/626)
- How do I handle merge conflicts in yarn.lock?
- first check out the yarn.lock file from the branch you're merging in to yours: e.g. `git checkout --theirs yarn.lock`
- now run `yarn install` again. Yarn will create a new lockfile that contains both sets of changes.
- then run `git add yarn.lock` to stage the lockfile for commit
- How do I handle merge conflicts in package-lock.json?
- first check out the package-lock.json file from the branch you're merging in to yours: e.g. `git checkout --theirs package-lock.json`
- now run `npm install` again. NPM will create a new lockfile that contains both sets of changes.
- then run `git add package-lock.json` to stage the lockfile for commit
- and `git commit` to conclude the merge
Getting Help

View File

@@ -59,7 +59,7 @@ cache:
# Set to true to see all cache hits/misses/refreshes in your console logs. Useful for debugging SSR caching issues.
debug: false
# When enabled (i.e. max > 0), known bots will be sent pages from a server side cache specific for bots.
# (Keep in mind, bot detection cannot be guarranteed. It is possible some bots will bypass this cache.)
# (Keep in mind, bot detection cannot be guaranteed. It is possible some bots will bypass this cache.)
botCache:
# Maximum number of pages to cache for known bots. Set to zero (0) to disable server side caching for bots.
# Default is 1000, which means the 1000 most recently accessed public pages will be cached.
@@ -503,6 +503,16 @@ notifyMetrics:
description: 'admin-notify-dashboard.NOTIFY.outgoing.delivered.description'
# Live Region configuration
# Live Region as defined by w3c, https://www.w3.org/TR/wai-aria-1.1/#terms:
# Live regions are perceivable regions of a web page that are typically updated as a
# result of an external event when user focus may be elsewhere.
#
# The DSpace live region is a component present at the bottom of all pages that is invisible by default, but is useful
# for screen readers. Any message pushed to the live region will be announced by the screen reader. These messages
# usually contain information about changes on the page that might not be in focus.
liveRegion:
# The duration after which messages disappear from the live region in milliseconds
messageTimeOutDurationMs: 30000
# The visibility of the live region. Setting this to true is only useful for debugging purposes.
isVisible: false

View File

@@ -17,7 +17,7 @@ describe('Site Statistics Page', () => {
cy.visit('/statistics');
// <ds-site-statistics-page> tag must be visable
// <ds-site-statistics-page> tag must be visible
cy.get('ds-site-statistics-page').should('be.visible');
// Verify / wait until "Total Visits" table's *last* label is non-empty

View File

@@ -142,7 +142,7 @@ describe('Login Modal', () => {
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
// Open user menu, verify user menu accessibility
page.openUserMenu();
cy.get('ds-user-menu').should('be.visible');
testA11y('ds-user-menu');

View File

@@ -1,7 +1,7 @@
import { testA11y } from 'cypress/support/utils';
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 does not exist', () => {
// request an invalid page (UUIDs at root path aren't valid)
cy.visit('/e9019a69-d4f1-4773-b6a3-bd362caa46f2', { failOnStatusCode: false });
cy.get('ds-pagenotfound').should('be.visible');

View File

@@ -34,7 +34,7 @@ describe('New Submission page', () => {
// 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".
// All panels are accordions & 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 },
@@ -192,7 +192,7 @@ describe('New Submission page', () => {
testA11y('ds-submission-edit',
{
rules: {
// All panels are accordians & fail "aria-required-children" and "nested-interactive".
// All panels are accordions & 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 },

View File

@@ -15,7 +15,7 @@ DSPACE_APP_CONFIG_PATH=/usr/local/dspace/config/config.yml
Configuration options can be overridden by setting environment variables.
## Nodejs server
When you start dspace-angular on node, it spins up an http server on which it listens for incoming connections. You can define the ip address and port the server should bind itsself to, and if ssl should be enabled not. By default it listens on `localhost:4000`. If you want it to listen on all your network connections, configure it to bind itself to `0.0.0.0`.
When you start dspace-angular on node, it spins up an http server on which it listens for incoming connections. You can define the ip address and port the server should bind itself to, and if ssl should be enabled not. By default it listens on `localhost:4000`. If you want it to listen on all your network connections, configure it to bind itself to `0.0.0.0`.
To change this configuration, change the options `ui.host`, `ui.port` and `ui.ssl` in the appropriate configuration file (see above):

View File

@@ -7,10 +7,8 @@
*/
import { TmplAstElement } from '@angular-eslint/bundled-angular-compiler';
import { TemplateParserServices } from '@angular-eslint/utils';
import {
ESLintUtils,
TSESLint,
} from '@typescript-eslint/utils';
import { ESLintUtils } from '@typescript-eslint/utils';
import { RuleContext } from '@typescript-eslint/utils/ts-eslint';
import { fixture } from '../../../test/fixture';
import {
@@ -52,7 +50,7 @@ The only exception to this rule are unit tests, where we may want to use the bas
export const rule = ESLintUtils.RuleCreator.withoutDocs({
...info,
create(context: TSESLint.RuleContext<Message, unknown[]>) {
create(context: RuleContext<Message, unknown[]>) {
if (getFilename(context).includes('.spec.ts')) {
// skip inline templates in unit tests
return {};

View File

@@ -7,9 +7,9 @@
*/
import {
ESLintUtils,
TSESLint,
TSESTree,
} from '@typescript-eslint/utils';
import { RuleContext } from '@typescript-eslint/utils/ts-eslint';
import { fixture } from '../../../test/fixture';
import {
@@ -57,7 +57,7 @@ export const info = {
export const rule = ESLintUtils.RuleCreator.withoutDocs({
...info,
create(context: TSESLint.RuleContext<Message, unknown[]>) {
create(context: RuleContext<Message, unknown[]>) {
const filename = getFilename(context);
if (filename.endsWith('.spec.ts')) {

View File

@@ -7,9 +7,9 @@
*/
import {
ESLintUtils,
TSESLint,
TSESTree,
} from '@typescript-eslint/utils';
import { RuleContext } from '@typescript-eslint/utils/ts-eslint';
import { fixture } from '../../../test/fixture';
import { getComponentSelectorNode } from '../../util/angular';
@@ -58,7 +58,7 @@ Unit tests are exempt from this rule, because they may redefine components using
export const rule = ESLintUtils.RuleCreator.withoutDocs({
...info,
create(context: TSESLint.RuleContext<Message, unknown[]>) {
create(context: RuleContext<Message, unknown[]>) {
const filename = getFilename(context);
if (filename.endsWith('.spec.ts')) {

View File

@@ -7,9 +7,9 @@
*/
import {
ESLintUtils,
TSESLint,
TSESTree,
} from '@typescript-eslint/utils';
import { RuleContext } from '@typescript-eslint/utils/ts-eslint';
import { fixture } from '../../../test/fixture';
import {
@@ -68,7 +68,7 @@ There are a few exceptions where the base class can still be used:
export const rule = ESLintUtils.RuleCreator.withoutDocs({
...info,
create(context: TSESLint.RuleContext<Message, unknown[]>) {
create(context: RuleContext<Message, unknown[]>) {
const filename = getFilename(context);
function handleUnthemedUsagesInTypescript(node: TSESTree.Identifier) {

View File

@@ -5,13 +5,17 @@
*
* http://www.dspace.org/license/
*/
import { TSESLint } from '@typescript-eslint/utils';
import { RuleTester } from 'eslint';
import {
InvalidTestCase,
RuleMetaData,
RuleModule,
ValidTestCase,
} from '@typescript-eslint/utils/ts-eslint';
import { EnumType } from 'typescript';
export type Meta = TSESLint.RuleMetaData<string>;
export type Valid = TSESLint.ValidTestCase<unknown[]> | RuleTester.ValidTestCase;
export type Invalid = TSESLint.InvalidTestCase<string, unknown[]> | RuleTester.InvalidTestCase;
export type Meta = RuleMetaData<string, unknown[]>;
export type Valid = ValidTestCase<unknown[]>;
export type Invalid = InvalidTestCase<string, unknown[]>;
export interface DSpaceESLintRuleInfo {
name: string;
@@ -28,7 +32,7 @@ export interface NamedTests {
export interface RuleExports {
Message: EnumType,
info: DSpaceESLintRuleInfo,
rule: TSESLint.RuleModule<string>,
rule: RuleModule<string>,
tests: NamedTests,
default: unknown,
}

View File

@@ -5,17 +5,18 @@
*
* http://www.dspace.org/license/
*/
import { TSESTree } from '@typescript-eslint/utils';
import {
TSESLint,
TSESTree,
} from '@typescript-eslint/utils';
RuleContext,
SourceCode,
} from '@typescript-eslint/utils/ts-eslint';
import {
match,
toUnixStylePath,
} from './misc';
export type AnyRuleContext = TSESLint.RuleContext<string, unknown[]>;
export type AnyRuleContext = RuleContext<string, unknown[]>;
/**
* Return the current filename based on the ESLint rule context as a Unix-style path.
@@ -27,7 +28,7 @@ export function getFilename(context: AnyRuleContext): string {
return toUnixStylePath(context.getFilename());
}
export function getSourceCode(context: AnyRuleContext): TSESLint.SourceCode {
export function getSourceCode(context: AnyRuleContext): SourceCode {
// TSESLint claims this is deprecated, but the suggested alternative is undefined (could be a version mismatch between ESLint and TSESlint?)
// eslint-disable-next-line deprecation/deprecation
return context.getSourceCode();

View File

@@ -7,7 +7,7 @@
*/
import { RuleTester as TypeScriptRuleTester } from '@typescript-eslint/rule-tester';
import { RuleTester } from 'eslint';
import { RuleTester } from '@typescript-eslint/utils/ts-eslint';
import { themeableComponents } from '../src/util/theme-support';
import {

24351
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -5,27 +5,27 @@
"ng": "ng",
"config:watch": "nodemon",
"test:rest": "ts-node --project ./tsconfig.ts-node.json scripts/test-rest.ts",
"start": "yarn run start:prod",
"start:dev": "nodemon --exec \"cross-env NODE_ENV=development yarn run serve\"",
"start:prod": "yarn run build:prod && cross-env NODE_ENV=production yarn run serve:ssr",
"start:mirador:prod": "yarn run build:mirador && yarn run start:prod",
"preserve": "yarn base-href",
"start": "npm run start:prod",
"start:dev": "nodemon --exec \"cross-env NODE_ENV=development npm run serve\"",
"start:prod": "npm run build:prod && cross-env NODE_ENV=production npm run serve:ssr",
"start:mirador:prod": "npm run build:mirador && npm run start:prod",
"preserve": "npm run base-href",
"serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts",
"serve:ssr": "node dist/server/main",
"analyze": "webpack-bundle-analyzer dist/browser/stats.json",
"build": "ng build --configuration development",
"build:stats": "ng build --stats-json",
"build:prod": "cross-env NODE_ENV=production yarn run build:ssr",
"build:prod": "cross-env NODE_ENV=production npm run build:ssr",
"build:ssr": "ng build --configuration production && ng run dspace-angular:server:production",
"build:lint": "rimraf 'lint/dist/**/*.js' 'lint/dist/**/*.js.map' && tsc -b lint/tsconfig.json",
"test": "ng test --source-map=true --watch=false --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:lint": "yarn build:lint && yarn test:lint:nobuild",
"test:lint": "npm run build:lint && npm run test:lint:nobuild",
"test:lint:nobuild": "jasmine --config=lint/jasmine.json",
"lint": "yarn build:lint && yarn lint:nobuild",
"lint": "npm run build:lint && npm run lint:nobuild",
"lint:nobuild": "ng lint",
"lint-fix": "yarn build:lint && ng lint --fix=true",
"lint-fix": "npm run build:lint && ng lint --fix=true",
"docs:lint": "ts-node --project ./lint/tsconfig.json ./lint/generate-docs.ts",
"e2e": "cross-env NODE_ENV=production ng e2e",
"clean:dev:config": "rimraf src/assets/config.json",
@@ -36,8 +36,8 @@
"clean:json": "rimraf *.records.json",
"clean:node": "rimraf node_modules",
"clean:cli": "rimraf .angular/cache",
"clean:prod": "yarn run clean:dist && yarn run clean:log && yarn run clean:doc && yarn run clean:coverage && yarn run clean:json",
"clean": "yarn run clean:prod && yarn run clean:dev:config && yarn run clean:cli && yarn run clean:node",
"clean:prod": "npm run clean:dist && npm run clean:log && npm run clean:doc && npm run clean:coverage && npm run clean:json",
"clean": "npm run clean:prod && npm run clean:dev:config && npm run clean:cli && npm run clean:node",
"sync-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts",
"build:mirador": "webpack --config webpack/webpack.mirador.config.ts",
"merge-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts",
@@ -46,7 +46,7 @@
"env:yaml": "ts-node --project ./tsconfig.ts-node.json scripts/env-to-yaml.ts",
"base-href": "ts-node --project ./tsconfig.ts-node.json scripts/base-href.ts",
"check-circ-deps": "npx madge --exclude '(bitstream|bundle|collection|config-submission-form|eperson|item|version)\\.model\\.ts$' --circular --extensions ts ./",
"postinstall": "yarn build:lint || echo 'Skipped DSpace ESLint plugins.'"
"postinstall": "npm run build:lint || echo 'Skipped DSpace ESLint plugins.'"
},
"browser": {
"fs": false,
@@ -55,10 +55,45 @@
"https": false
},
"private": true,
"resolutions": {
"minimist": "^1.2.5",
"webdriver-manager": "^12.1.8",
"ts-node": "10.2.1"
"overrides": {
"@kolkov/ngx-gallery": {
"@angular/animations": "^17.3.11",
"@angular/common": "^17.3.11",
"@angular/core": "^17.3.11"
},
"@ng-bootstrap/ng-bootstrap": {
"@angular/common": "^17.3.11",
"@angular/core": "^17.3.11",
"@angular/forms": "^17.3.11",
"@angular/localize": "^17.3.11"
},
"@ng-dynamic-forms/core": {
"@angular/common": "^17.3.11",
"@angular/core": "^17.3.11",
"@angular/forms": "^17.3.11"
},
"@ng-dynamic-forms/ui-ng-bootstrap": {
"ngx-mask": "14.2.4"
},
"@ngtools/webpack": {
"@angular/compiler-cli": "^17.3.11",
"typescript": "~5.3.3"
},
"@nicky-lenaers/ngx-scroll-to": {
"@angular/common": "^17.3.11",
"@angular/core": "^17.3.11"
},
"eslint-plugin-unused-imports": {
"@typescript-eslint/eslint-plugin": "^7.2.0"
},
"ng2-file-upload": {
"@angular/common": "^17.3.11",
"@angular/core": "^17.3.11"
},
"ngx-infinite-scroll": {
"@angular/common": "^17.3.11",
"@angular/core": "^17.3.11"
}
},
"dependencies": {
"@angular/animations": "^17.3.11",
@@ -67,7 +102,7 @@
"@angular/compiler": "^17.3.11",
"@angular/core": "^17.3.11",
"@angular/forms": "^17.3.11",
"@angular/localize": "17.3.11",
"@angular/localize": "^17.3.11",
"@angular/platform-browser": "^17.3.11",
"@angular/platform-browser-dynamic": "^17.3.11",
"@angular/platform-server": "^17.3.11",
@@ -75,8 +110,6 @@
"@angular/ssr": "^17.3.8",
"@babel/runtime": "7.21.0",
"@kolkov/ngx-gallery": "^2.0.1",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.11.3",
"@ng-bootstrap/ng-bootstrap": "^11.0.0",
"@ng-dynamic-forms/core": "^16.0.0",
"@ng-dynamic-forms/ui-ng-bootstrap": "^16.0.0",
@@ -88,7 +121,7 @@
"@types/grecaptcha": "^3.0.4",
"angular-idle-preload": "3.0.0",
"angulartics2": "^12.2.0",
"axios": "^1.6.0",
"axios": "^1.7.4",
"bootstrap": "^4.6.1",
"cerialize": "0.1.18",
"cli-progress": "^3.12.0",
@@ -100,13 +133,13 @@
"date-fns-tz": "^1.3.7",
"deepmerge": "^4.3.1",
"ejs": "^3.1.10",
"express": "^4.19.2",
"express": "^4.20.0",
"express-rate-limit": "^5.1.3",
"fast-json-patch": "^3.1.1",
"filesize": "^6.1.0",
"http-proxy-middleware": "^1.0.5",
"http-terminator": "^3.2.0",
"isbot": "^3.6.10",
"isbot": "^5.1.17",
"js-cookie": "2.2.1",
"js-yaml": "^4.1.0",
"json5": "^2.2.3",
@@ -128,8 +161,6 @@
"ngx-ui-switch": "^14.1.0",
"nouislider": "^15.7.1",
"pem": "1.14.7",
"prop-types": "^15.8.1",
"react-copy-to-clipboard": "^5.1.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.0",
"sanitize-html": "^2.12.1",
@@ -176,8 +207,8 @@
"deep-freeze": "0.0.1",
"eslint": "^8.39.0",
"eslint-plugin-deprecation": "^1.4.1",
"eslint-plugin-dspace-angular-html": "link:./lint/dist/src/rules/html",
"eslint-plugin-dspace-angular-ts": "link:./lint/dist/src/rules/ts",
"eslint-plugin-dspace-angular-html": "file:./lint/dist/src/rules/html",
"eslint-plugin-dspace-angular-ts": "file:./lint/dist/src/rules/ts",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-import-newlines": "^1.3.1",
"eslint-plugin-jsdoc": "^45.0.0",
@@ -185,7 +216,7 @@
"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": "^3.2.0",
"express-static-gzip": "^2.1.7",
"jasmine": "^3.8.0",
"jasmine-core": "^3.8.0",
@@ -213,7 +244,7 @@
"sass-resources-loader": "^2.2.5",
"ts-node": "^8.10.2",
"typescript": "~5.3.3",
"webpack": "5.90.3",
"webpack": "5.94.0",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"

View File

@@ -38,7 +38,7 @@ parseCliInput();
function parseCliInput() {
program
.option('-d, --output-dir <output-dir>', 'output dir when running script on all language files', projectRoot(LANGUAGE_FILES_LOCATION))
.option('-s, --source-dir <source-dir>', 'source dir of transalations to be merged')
.option('-s, --source-dir <source-dir>', 'source dir of translations to be merged')
.usage('(-s <source-dir> [-d <output-dir>])')
.parse(process.argv);

View File

@@ -27,7 +27,7 @@ import * as expressStaticGzip from 'express-static-gzip';
/* eslint-enable import/no-namespace */
import axios from 'axios';
import LRU from 'lru-cache';
import isbot from 'isbot';
import { isbot } from 'isbot';
import { createCertificate } from 'pem';
import { createServer } from 'https';
import { json } from 'body-parser';
@@ -99,7 +99,7 @@ export function app() {
* If production mode is enabled in the environment file:
* - Enable Angular's production mode
* - Initialize caching of SSR rendered pages (if enabled in config.yml)
* - Enable compression for SSR reponses. See [compression](https://github.com/expressjs/compression)
* - Enable compression for SSR responses. See [compression](https://github.com/expressjs/compression)
*/
if (environment.production) {
enableProdMode();
@@ -428,7 +428,7 @@ function checkCacheForRequest(cacheName: string, cache: LRU<string, any>, req, r
if (environment.cache.serverSide.debug) { console.log(`CACHE EXPIRED FOR ${key} in ${cacheName} cache. Re-rendering...`); }
// Update cached copy by rerendering server-side
// NOTE: In this scenario the currently cached copy will be returned to the current user.
// This re-render is peformed behind the scenes to update cached copy for next user.
// This re-render is performed behind the scenes to update cached copy for next user.
serverSideRender(req, res, next, false);
}
} else {

View File

@@ -2,7 +2,7 @@
* List of services statuses
*/
export enum LdnServiceStatus {
UNKOWN,
UNKNOWN,
DISABLED,
ENABLED,
}

View File

@@ -190,12 +190,12 @@ describe('BitstreamFormatsComponent', () => {
describe('isSelected', () => {
beforeEach(waitForAsync(initAsync));
beforeEach(initBeforeEach);
it('should return an observable of true if the provided bistream is in the list returned by the service', () => {
it('should return an observable of true if the provided bitstream is in the list returned by the service', () => {
const result = comp.isSelected(bitstreamFormat1);
expect(result).toBeObservable(cold('b', { b: true }));
});
it('should return an observable of false if the provided bistream is not in the list returned by the service', () => {
it('should return an observable of false if the provided bitstream is not in the list returned by the service', () => {
const format = new BitstreamFormat();
format.uuid = 'new';

View File

@@ -118,7 +118,7 @@ export class BitstreamFormatsComponent implements OnInit, OnDestroy {
}
/**
* Deselects all selecetd bitstream formats
* Deselects all selected bitstream formats
*/
deselectAll() {
this.bitstreamFormatService.deselectAllBitstreamFormats();

View File

@@ -154,12 +154,12 @@ export class FormatFormComponent implements OnInit {
(fieldModel: DynamicFormControlModel) => {
if (fieldModel.name === 'extensions') {
if (hasValue(this.bitstreamFormat.extensions)) {
const extenstions = this.bitstreamFormat.extensions;
const extensions = this.bitstreamFormat.extensions;
const formArray = (fieldModel as DynamicFormArrayModel);
for (let i = 0; i < extenstions.length; i++) {
for (let i = 0; i < extensions.length; i++) {
formArray.insertGroup(i).group[0] = new DynamicInputModel({
id: `extension-${i}`,
value: extenstions[i],
value: extensions[i],
}, this.arrayInputElementLayout);
}
}
@@ -172,7 +172,7 @@ export class FormatFormComponent implements OnInit {
}
/**
* Creates an updated bistream format based on the current values in the form
* Creates an updated bitstream format based on the current values in the form
* Emits the updated bitstream format trouhg the updatedFormat emitter
*/
onSubmit() {

View File

@@ -4,7 +4,10 @@ import {
NgForOf,
NgIf,
} from '@angular/common';
import { Component } from '@angular/core';
import {
Component,
OnDestroy,
} from '@angular/core';
import {
Router,
RouterLink,
@@ -60,7 +63,7 @@ import { MetadataSchemaFormComponent } from './metadata-schema-form/metadata-sch
* A component used for managing all existing metadata schemas within the repository.
* The admin can create, edit or delete metadata schemas here.
*/
export class MetadataRegistryComponent {
export class MetadataRegistryComponent implements OnDestroy {
/**
* A list of all the current metadata schemas within the repository

View File

@@ -4,6 +4,7 @@ import {
} from '@angular/common';
import {
Component,
OnInit,
ViewChild,
} from '@angular/core';
import {
@@ -40,7 +41,7 @@ import { FilteredCollections } from './filtered-collections.model';
],
standalone: true,
})
export class FilteredCollectionsComponent {
export class FilteredCollectionsComponent implements OnInit {
queryForm: FormGroup;
results: FilteredCollections = new FilteredCollections();

View File

@@ -5,6 +5,7 @@ import {
} from '@angular/common';
import {
Component,
OnInit,
ViewChild,
} from '@angular/core';
import {
@@ -66,7 +67,7 @@ import { QueryPredicate } from './query-predicate.model';
],
standalone: true,
})
export class FilteredItemsComponent {
export class FilteredItemsComponent implements OnInit {
collections: OptionVO[];
presetQueries: PresetQuery[];
@@ -90,7 +91,7 @@ export class FilteredItemsComponent {
private formBuilder: FormBuilder,
private restService: DspaceRestService) {}
ngOnInit() {
ngOnInit(): void {
this.loadCollections();
this.loadPresetQueries();
this.loadMetadataFields();

View File

@@ -1,4 +1,7 @@
import { Component } from '@angular/core';
import {
Component,
OnInit,
} from '@angular/core';
import { RouterLink } from '@angular/router';
import { getCollectionEditRoute } from '../../../../../collection-page/collection-page-routing-paths';
@@ -21,10 +24,10 @@ import { SearchResultGridElementComponent } from '../../../../../shared/object-g
/**
* The component for displaying a list element for a collection search result on the admin search page
*/
export class CollectionAdminSearchResultGridElementComponent extends SearchResultGridElementComponent<CollectionSearchResult, Collection> {
export class CollectionAdminSearchResultGridElementComponent extends SearchResultGridElementComponent<CollectionSearchResult, Collection> implements OnInit {
editPath: string;
ngOnInit() {
ngOnInit(): void {
super.ngOnInit();
this.editPath = getCollectionEditRoute(this.dso.uuid);
}

View File

@@ -1,4 +1,7 @@
import { Component } from '@angular/core';
import {
Component,
OnInit,
} from '@angular/core';
import { RouterLink } from '@angular/router';
import { getCommunityEditRoute } from '../../../../../community-page/community-page-routing-paths';
@@ -21,10 +24,10 @@ import { SearchResultGridElementComponent } from '../../../../../shared/object-g
/**
* The component for displaying a list element for a community search result on the admin search page
*/
export class CommunityAdminSearchResultGridElementComponent extends SearchResultGridElementComponent<CommunitySearchResult, Community> {
export class CommunityAdminSearchResultGridElementComponent extends SearchResultGridElementComponent<CommunitySearchResult, Community> implements OnInit {
editPath: string;
ngOnInit() {
ngOnInit(): void {
super.ngOnInit();
this.editPath = getCommunityEditRoute(this.dso.uuid);
}

View File

@@ -1,4 +1,7 @@
import { Component } from '@angular/core';
import {
Component,
OnInit,
} from '@angular/core';
import { RouterLink } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
@@ -22,10 +25,10 @@ import { SearchResultListElementComponent } from '../../../../../shared/object-l
/**
* The component for displaying a list element for a collection search result on the admin search page
*/
export class CollectionAdminSearchResultListElementComponent extends SearchResultListElementComponent<CollectionSearchResult, Collection> {
export class CollectionAdminSearchResultListElementComponent extends SearchResultListElementComponent<CollectionSearchResult, Collection> implements OnInit {
editPath: string;
ngOnInit() {
ngOnInit(): void {
super.ngOnInit();
this.editPath = getCollectionEditRoute(this.dso.uuid);
}

View File

@@ -1,4 +1,7 @@
import { Component } from '@angular/core';
import {
Component,
OnInit,
} from '@angular/core';
import { RouterLink } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
@@ -22,10 +25,10 @@ import { SearchResultListElementComponent } from '../../../../../shared/object-l
/**
* The component for displaying a list element for a community search result on the admin search page
*/
export class CommunityAdminSearchResultListElementComponent extends SearchResultListElementComponent<CommunitySearchResult, Community> {
export class CommunityAdminSearchResultListElementComponent extends SearchResultListElementComponent<CommunitySearchResult, Community> implements OnInit {
editPath: string;
ngOnInit() {
ngOnInit(): void {
super.ngOnInit();
this.editPath = getCommunityEditRoute(this.dso.uuid);
}

View File

@@ -154,7 +154,7 @@ export class BrowseByTaxonomyComponent implements OnInit, OnChanges, OnDestroy {
this.facetType = browseDefinition.facetType;
this.vocabularyName = browseDefinition.vocabulary;
this.vocabularyOptions = { name: this.vocabularyName, closed: true };
this.description = this.translate.instant(`browse.metadata.${this.vocabularyName}.tree.descrption`);
this.description = this.translate.instant(`browse.metadata.${this.vocabularyName}.tree.description`);
}));
this.subs.push(this.scope$.subscribe(() => {
this.updateQueryParams();

View File

@@ -1,5 +1,8 @@
import { AsyncPipe } from '@angular/common';
import { Component } from '@angular/core';
import {
Component,
OnInit,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs';
@@ -28,7 +31,7 @@ import { hasValue } from '../../../shared/empty.util';
],
standalone: true,
})
export class CollectionCurateComponent {
export class CollectionCurateComponent implements OnInit {
dsoRD$: Observable<RemoteData<Collection>>;
collectionName$: Observable<string>;

View File

@@ -84,7 +84,7 @@ export class CollectionMetadataComponent extends ComcolMetadataComponent<Collect
}
/**
* Cheking if the navigation is done and if so, initialize the collection's item template,
* Checking if the navigation is done and if so, initialize the collection's item template,
* to ensure that the item template is always up to date.
* Check when a NavigationEnd event (URL change) or a Scroll event followed by a NavigationEnd event (refresh event), occurs
*/

View File

@@ -95,7 +95,7 @@ export class CollectionSourceControlsComponent implements OnInit, OnDestroy {
) {
}
ngOnInit() {
ngOnInit(): void {
// ensure the contentSource gets updated after being set to stale
this.contentSource$ = this.collectionService.findByHref(this.collection._links.self.href, false).pipe(
getAllSucceededRemoteDataPayload(),

View File

@@ -304,7 +304,7 @@ function addDependentsObjectCacheState(state: ObjectCacheState, action: AddDepen
/**
* Remove all dependent request UUIDs from a cached object, used to clear out-of-date depedencies
* Remove all dependent request UUIDs from a cached object, used to clear out-of-date dependencies
*
* @param state the current state
* @param action an AddDependentsObjectCacheAction

View File

@@ -63,12 +63,12 @@ describe('AccessStatusDataService', () => {
/**
* Create an AccessStatusDataService used for testing
* @param reponse$ Supply a RemoteData to be returned by the REST API (optional)
* @param response$ Supply a RemoteData to be returned by the REST API (optional)
*/
function createService(reponse$?: Observable<RemoteData<any>>) {
function createService(response$?: Observable<RemoteData<any>>) {
requestService = getMockRequestService();
let buildResponse$ = reponse$;
if (hasNoValue(reponse$)) {
let buildResponse$ = response$;
if (hasNoValue(response$)) {
buildResponse$ = createSuccessfulRemoteDataObject$({});
}
rdbService = jasmine.createSpyObj('rdbService', {

View File

@@ -138,27 +138,27 @@ describe('BitstreamDataService', () => {
describe('findPrimaryBitstreamByItemAndName', () => {
it('should return primary bitstream', () => {
const exprected$ = cold('(a|)', { a: bitstream1 } );
const expected$ = cold('(a|)', { a: bitstream1 } );
const bundle = Object.assign(new Bundle(), {
primaryBitstream: observableOf(createSuccessfulRemoteDataObject(bitstream1)),
});
spyOn(bundleDataService, 'findByItemAndName').and.returnValue(observableOf(createSuccessfulRemoteDataObject(bundle)));
expect(service.findPrimaryBitstreamByItemAndName(ItemMock, 'ORIGINAL')).toBeObservable(exprected$);
expect(service.findPrimaryBitstreamByItemAndName(ItemMock, 'ORIGINAL')).toBeObservable(expected$);
});
it('should return null if primary bitstream has not be succeeded ', () => {
const exprected$ = cold('(a|)', { a: null } );
const expected$ = cold('(a|)', { a: null } );
const bundle = Object.assign(new Bundle(), {
primaryBitstream: observableOf(createFailedRemoteDataObject()),
});
spyOn(bundleDataService, 'findByItemAndName').and.returnValue(observableOf(createSuccessfulRemoteDataObject(bundle)));
expect(service.findPrimaryBitstreamByItemAndName(ItemMock, 'ORIGINAL')).toBeObservable(exprected$);
expect(service.findPrimaryBitstreamByItemAndName(ItemMock, 'ORIGINAL')).toBeObservable(expected$);
});
it('should return EMPTY if nothing where found', () => {
const exprected$ = cold('(|)', {} );
const expected$ = cold('(|)', {} );
spyOn(bundleDataService, 'findByItemAndName').and.returnValue(observableOf(createFailedRemoteDataObject<Bundle>()));
expect(service.findPrimaryBitstreamByItemAndName(ItemMock, 'ORIGINAL')).toBeObservable(exprected$);
expect(service.findPrimaryBitstreamByItemAndName(ItemMock, 'ORIGINAL')).toBeObservable(expected$);
});
});

View File

@@ -242,7 +242,7 @@ export class BitstreamDataService extends IdentifiableDataService<Bitstream> imp
* @param reRequestOnStale Whether or not the request should automatically be re-
* requested after the response becomes stale
* @return {Observable<Bitstream | null>}
* Return an observable that constains primary bitstream information or null
* Return an observable that contains primary bitstream information or null
*/
public findPrimaryBitstreamByItemAndName(item: Item, bundleName: string, useCachedVersionIfAvailable = true, reRequestOnStale = true): Observable<Bitstream | null> {
return this.bundleService.findByItemAndName(item, bundleName, useCachedVersionIfAvailable, reRequestOnStale, followLink('primaryBitstream')).pipe(

View File

@@ -151,7 +151,7 @@ describe('CollectionDataService', () => {
expect(service.getAuthorizedCollection).toHaveBeenCalledWith(queryString);
});
it('should return a RemoteData<PaginatedList<Colletion>> for the getAuthorizedCollection', () => {
it('should return a RemoteData<PaginatedList<Collection>> for the getAuthorizedCollection', () => {
const result = service.getAuthorizedCollection(queryString);
const expected = cold('a|', {
a: paginatedListRD,
@@ -166,7 +166,7 @@ describe('CollectionDataService', () => {
expect(service.getAuthorizedCollectionByCommunity).toHaveBeenCalledWith(communityId, queryString);
});
it('should return a RemoteData<PaginatedList<Colletion>> for the getAuthorizedCollectionByCommunity', () => {
it('should return a RemoteData<PaginatedList<Collection>> for the getAuthorizedCollectionByCommunity', () => {
const result = service.getAuthorizedCollectionByCommunity(communityId, queryString);
const expected = cold('a|', {
a: paginatedListRD,
@@ -206,12 +206,12 @@ describe('CollectionDataService', () => {
/**
* Create a CollectionDataService used for testing
* @param reponse$ Supply a RemoteData to be returned by the REST API (optional)
* @param response$ Supply a RemoteData to be returned by the REST API (optional)
*/
function createService(reponse$?: Observable<RemoteData<any>>) {
function createService(response$?: Observable<RemoteData<any>>) {
requestService = getMockRequestService();
let buildResponse$ = reponse$;
if (hasNoValue(reponse$)) {
let buildResponse$ = response$;
if (hasNoValue(response$)) {
buildResponse$ = createSuccessfulRemoteDataObject$({});
}
rdbService = jasmine.createSpyObj('rdbService', {

View File

@@ -20,7 +20,7 @@ export class LinkHeadService {
/**
* Method to create a Link tag in the HEAD of the html.
* @param tag LinkDefition is the paramaters to define a link tag.
* @param tag LinkDefition is the parameters to define a link tag.
* @returns Link tag that was created
*/
addTag(tag: LinkDefinition) {

View File

@@ -33,7 +33,7 @@ const httpAgent = new HttpAgent(agentOptions);
const httpsAgent = new HttpsAgent(agentOptions);
/**
* Contructs the XMLHttpRequest instances used for all HttpClient requests.
* Constructs the XMLHttpRequest instances used for all HttpClient requests.
* Emulated by https://github.com/pwnall/node-xhr2 on the server.
* This class overrides the built-in Angular implementation to set additional configuration.
*

View File

@@ -31,7 +31,7 @@ export class ClientMathService extends MathService {
protected mathJaxOptions = {
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']],
inlineMath: [['$', '$'], ['$$', '$$'], ['\\(', '\\)']],
},
svg: {
fontCache: 'global',
@@ -108,7 +108,7 @@ export class ClientMathService extends MathService {
*/
render(element: HTMLElement) {
if (environment.markdown.mathjax) {
this._window.nativeWindow.MathJax.typesetPromise([element]);
return (window as any).MathJax.typesetPromise([element]) as Promise<any>;
}
}
}

View File

@@ -22,8 +22,8 @@ export class MockMathService extends MathService {
return of(true);
}
render(element: HTMLElement): void {
return;
render(element: HTMLElement): Promise<any> {
return Promise.resolve();
}
}

View File

@@ -15,5 +15,5 @@ export abstract class MathService {
protected abstract registerMathJaxAsync(config: MathJaxConfig): Promise<any>;
abstract ready(): Observable<boolean>;
abstract render(element: HTMLElement): void;
abstract render(element: HTMLElement): Promise<any>;
}

View File

@@ -3,7 +3,7 @@
*/
export class MetadataConfig {
/**
* A unique indentifier
* A unique identifier
*/
id: string;

View File

@@ -57,7 +57,7 @@ import { SearchConfigurationService } from './search-configuration.service';
* - Overrides {@link BaseDataService.addEmbedParams} in order to make it public
*
* Doesn't use any of the service's dependencies, they are initialized as undefined
* Therefore, equest/response handling methods won't work even though they're defined
* Therefore, request/response handling methods won't work even though they're defined
*/
class SearchDataService extends BaseDataService<any> {
constructor() {

View File

@@ -47,6 +47,6 @@ export class ServerMathService extends MathService {
}
render(element: HTMLElement) {
return;
return Promise.resolve();
}
}

View File

@@ -1,5 +1,8 @@
/* eslint-disable max-classes-per-file */
import { Injectable } from '@angular/core';
import {
Injectable,
OnDestroy,
} from '@angular/core';
import { Observable } from 'rxjs';
import { Duplicate } from '../../shared/object-list/duplicate-data/duplicate.model';
@@ -33,7 +36,8 @@ import { HALEndpointService } from '../shared/hal-endpoint.service';
*
*/
@Injectable({ providedIn: 'root' })
export class SubmissionDuplicateDataService extends BaseDataService<Duplicate> implements SearchData<Duplicate> {
export class SubmissionDuplicateDataService extends BaseDataService<Duplicate>
implements SearchData<Duplicate>, OnDestroy {
/**
* The ResponseParsingService constructor name

View File

@@ -68,7 +68,7 @@ export class SubmissionObjectDataService {
environment.cache.msToLive.default,
now,
RequestEntryState.Error,
'The request couldn\'t be sent. Unable to determine the type of submission object',
'The request could not be sent. Unable to determine the type of submission object',
undefined,
400,
));

View File

@@ -244,7 +244,7 @@ export class DsoEditMetadataComponent implements OnInit, OnDestroy {
/**
* Submit the current changes to the form by retrieving json PATCH operations from the form and sending it to the
* DSpaceObject's data-service
* Display notificiations and reset the form afterwards if successful
* Display notifications and reset the form afterwards if successful
*/
submit(): void {
this.saving$.next(true);

View File

@@ -7,6 +7,7 @@ import {
import {
Component,
Inject,
OnInit,
} from '@angular/core';
import { RouterLink } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
@@ -36,7 +37,7 @@ import { ThemedThumbnailComponent } from '../../../../../thumbnail/themed-thumbn
/**
* The component for displaying a list element for an item search result of the type Person
*/
export class PersonSearchResultListElementComponent extends ItemSearchResultListElementComponent {
export class PersonSearchResultListElementComponent extends ItemSearchResultListElementComponent implements OnInit {
public constructor(
protected truncatableService: TruncatableService,

View File

@@ -23,7 +23,7 @@
[label]="'orgunit.page.city'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="object"
[fields]="['organization.adress.addressCountry']"
[fields]="['organization.address.addressCountry']"
[label]="'orgunit.page.country'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="object"

View File

@@ -23,7 +23,7 @@ const mockItem: Item = Object.assign(new Item(), {
value: 'New York',
},
],
'organization.adress.addressCountry': [
'organization.address.addressCountry': [
{
language: 'en_US',
value: 'USA',

View File

@@ -2,7 +2,10 @@ import {
AsyncPipe,
NgIf,
} from '@angular/common';
import { Component } from '@angular/core';
import {
Component,
OnInit,
} from '@angular/core';
import {
ActivatedRoute,
Router,
@@ -45,7 +48,7 @@ import { BrowserOnlyPipe } from '../../shared/utils/browser-only.pipe';
/**
* Component for a user to enter a new password for a forgot token.
*/
export class ForgotPasswordFormComponent {
export class ForgotPasswordFormComponent implements OnInit {
registration$: Observable<Registration>;

View File

@@ -10,6 +10,7 @@ import {
Component,
ElementRef,
Inject,
OnDestroy,
OnInit,
PLATFORM_ID,
} from '@angular/core';
@@ -62,7 +63,7 @@ import { VarDirective } from '../../shared/utils/var.directive';
standalone: true,
imports: [VarDirective, NgIf, NgClass, NgFor, ListableObjectComponentLoaderComponent, ErrorComponent, ThemedLoadingComponent, AsyncPipe, TranslateModule],
})
export class RecentItemListComponent implements OnInit {
export class RecentItemListComponent implements OnInit, OnDestroy {
itemRD$: Observable<RemoteData<PaginatedList<Item>>>;
paginationConfig: PaginationComponentOptions;
sortConfig: SortOptions;

View File

@@ -50,7 +50,7 @@ import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
* Performs the initialization of the app.
*
* Should be extended to implement server- & browser-specific functionality.
* Initialization steps shared between the server and brower implementations
* Initialization steps shared between the server and browser implementations
* can be included in this class.
*
* Note that the service cannot (indirectly) depend on injection tokens that are only available _after_ APP_INITIALIZER.
@@ -134,7 +134,7 @@ export abstract class InitService {
protected static resolveAppConfig(
transferState: TransferState,
): void {
// overriden in subclasses if applicable
// overridden in subclasses if applicable
}
/**

View File

@@ -253,7 +253,7 @@ export class BitstreamRequestACopyPageComponent implements OnInit, OnDestroy {
}
/**
* Retrieves the link to the bistream download page
* Retrieves the link to the bitstream download page
*/
getBitstreamLink() {
return [getBitstreamDownloadRoute(this.bitstream)];

View File

@@ -1,9 +1,9 @@
<div class="col-3 float-left d-flex h-100 action-label">
<span class="justify-content-center align-self-center">
<div class="col-12 col-md-3 h-auto float-left d-flex action-label">
<span class="justify-content-center align-self-center font-weight-bold">
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.label' | translate}}
</span>
</div>
<div class="col-9 float-left action-button">
<div class="col-12 col-md-9 float-left action-button">
<span *ngIf="operation.authorized">
<button class="btn btn-outline-primary" [disabled]="operation.disabled" [routerLink]="operation.operationUrl" [attr.aria-label]="'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate">
{{'item.edit.tabs.status.buttons.' + operation.operationKey + '.button' | translate}}

View File

@@ -3,7 +3,10 @@ import {
NgForOf,
NgIf,
} from '@angular/common';
import { Component } from '@angular/core';
import {
Component,
OnInit,
} from '@angular/core';
import {
ActivatedRoute,
Router,
@@ -47,7 +50,7 @@ import { AbstractSimpleItemActionComponent } from '../simple-item-action/abstrac
/**
* Component responsible for rendering the Item Register DOI page
*/
export class ItemRegisterDoiComponent extends AbstractSimpleItemActionComponent {
export class ItemRegisterDoiComponent extends AbstractSimpleItemActionComponent implements OnInit {
protected messageKey = 'register-doi';
doiToUpdateMessage = 'item.edit.' + this.messageKey + '.to-update';

View File

@@ -375,7 +375,7 @@ describe('EditRelationshipListComponent', () => {
describe('changes managment for add buttons', () => {
describe('changes management for add buttons', () => {
it('should show enabled add buttons', () => {
const element = de.query(By.css('.btn-success'));

View File

@@ -1,28 +1,28 @@
<p class="mt-2">{{'item.edit.tabs.status.description' | translate}}</p>
<div class="row">
<div *ngFor="let statusKey of statusDataKeys" class="w-100">
<div class="col-3 float-left status-label">
<div *ngFor="let statusKey of statusDataKeys" class="w-100 pt-1">
<div class="col-12 col-md-3 float-left status-label font-weight-bold">
{{'item.edit.tabs.status.labels.' + statusKey | translate}}:
</div>
<div class="col-9 float-left status-data" id="status-{{statusKey}}">
<div class="col-12 col-md-9 float-left status-data" id="status-{{statusKey}}">
{{statusData[statusKey]}}
</div>
</div>
<div *ngFor="let identifier of (identifiers$ | async)" class="w-100">
<div *ngFor="let identifier of (identifiers$ | async)" class="w-100 pt-1">
<div *ngIf="(identifier.identifierType==='doi')">
<div class="col-3 float-left status-label">
<div class="col-12 col-md-3 float-left status-label font-weight-bold">
{{identifier.identifierType.toLocaleUpperCase()}}
</div>
<div class="col-9 float-left status-label">{{identifier.value}}
<div class="col-12 col-md-9 float-left status-label font-weight-bold">{{identifier.value}}
({{"item.edit.identifiers.doi.status."+identifier.identifierStatus|translate}})</div>
</div>
</div>
<div class="col-3 float-left status-label">
<div class="col-12 col-md-3 float-left status-label font-weight-bold">
{{'item.edit.tabs.status.labels.itemPage' | translate}}:
</div>
<div class="col-9 float-left status-data" id="status-itemPage">
<div class="col-12 col-md-9 float-left status-data" id="status-itemPage">
<a [routerLink]="itemPageRoute$ | async">{{itemPageRoute$ | async}}</a>
</div>

View File

@@ -7,6 +7,7 @@ import {
import {
ChangeDetectionStrategy,
Component,
OnDestroy,
OnInit,
} from '@angular/core';
import {
@@ -79,7 +80,7 @@ import { ItemOperation } from '../item-operation/itemOperation.model';
/**
* Component for displaying an item's status
*/
export class ItemStatusComponent implements OnInit {
export class ItemStatusComponent implements OnInit, OnDestroy {
/**
* The item to display the status for

View File

@@ -2,7 +2,10 @@ import {
AsyncPipe,
NgIf,
} from '@angular/common';
import { Component } from '@angular/core';
import {
Component,
OnInit,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@@ -28,7 +31,7 @@ import { ItemVersionsComponent } from '../../versions/item-versions.component';
/**
* Component for listing and managing an item's version history
*/
export class ItemVersionHistoryComponent {
export class ItemVersionHistoryComponent implements OnInit {
/**
* The item to display the version history for
*/

View File

@@ -45,7 +45,7 @@ export class MetadataUriValuesComponent extends MetadataValuesComponent {
@Input() mdValues: MetadataValue[];
/**
* The seperator used to split the metadata values (can contain HTML)
* The separator used to split the metadata values (can contain HTML)
*/
@Input() separator: string;

View File

@@ -51,7 +51,7 @@ export class MetadataValuesComponent implements OnChanges {
@Input() mdValues: MetadataValue[];
/**
* The seperator used to split the metadata values (can contain HTML)
* The separator used to split the metadata values (can contain HTML)
*/
@Input() separator: string;

View File

@@ -17,7 +17,7 @@ import { AuthService } from '../../../core/auth/auth.service';
import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model';
/**
* This componenet render an image gallery for the image viewer
* This component render an image gallery for the image viewer
*/
@Component({
selector: 'ds-base-media-viewer-image',

View File

@@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
import {
Component,
Input,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges,
@@ -56,7 +57,7 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio
],
standalone: true,
})
export class OrcidQueueComponent implements OnInit, OnDestroy {
export class OrcidQueueComponent implements OnInit, OnDestroy, OnChanges {
/**
* The item for which showing the orcid settings

View File

@@ -34,13 +34,13 @@ export class ItemPageCcLicenseFieldComponent implements OnInit {
@Input() variant?: 'small' | 'full' = 'small';
/**
* Filed name containing the CC license URI, as configured in the back-end, in the 'dspace.cfg' file, propertie
* Filed name containing the CC license URI, as configured in the back-end, in the 'dspace.cfg' file, property
* 'cc.license.uri'
*/
@Input() ccLicenseUriField? = 'dc.rights.uri';
/**
* Filed name containing the CC license name, as configured in the back-end, in the 'dspace.cfg' file, propertie
* Filed name containing the CC license name, as configured in the back-end, in the 'dspace.cfg' file, property
* 'cc.license.name'
*/
@Input() ccLicenseNameField? = 'dc.rights';

View File

@@ -1,7 +1,9 @@
<ng-container *ngIf="relationTypes.length > 1">
<ul ngbNav #tabs="ngbNav" [destroyOnHide]="true" [activeId]="activeTab$ | async" (navChange)="onTabChange($event)" class="nav-tabs">
<li *ngFor="let relationType of relationTypes" [ngbNavItem]="relationType.filter" rel="presentation">
<a ngbNavLink>{{'item.page.relationships.' + relationType.label | translate}}</a>
<ul ngbNav #tabs="ngbNav" [destroyOnHide]="true" [activeId]="activeTab$ | async" (navChange)="onTabChange($event)" class="nav-tabs" role="tablist">
<li *ngFor="let relationType of relationTypes" [ngbNavItem]="relationType.filter" role="presentation">
<a ngbNavLink role="tab">
{{'item.page.relationships.' + relationType.label | translate}}
</a>
<ng-template ngbNavContent>
<div class="mt-4">
<ds-related-entities-search [item]="item"

View File

@@ -10,6 +10,7 @@ import {
ElementRef,
Inject,
Input,
OnInit,
PLATFORM_ID,
} from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
@@ -43,7 +44,7 @@ import { AbstractIncrementalListComponent } from '../abstract-incremental-list/a
* This component is used for displaying relations between items
* It expects a parent item and relationship type, as well as a label to display on top
*/
export class RelatedItemsComponent extends AbstractIncrementalListComponent<Observable<RemoteData<PaginatedList<Item>>>> {
export class RelatedItemsComponent extends AbstractIncrementalListComponent<Observable<RemoteData<PaginatedList<Item>>>> implements OnInit {
/**
* The parent of the list of related items to display

View File

@@ -1,16 +1,16 @@
<div *ngVar="(versionsRD$ | async)?.payload as versions">
<div *ngVar="(versionRD$ | async)?.payload as itemVersion">
<div class="mb-2" *ngIf="versions?.page?.length > 0 || displayWhenEmpty">
<div *ngIf="(versionsDTO$ | async) as versionsDTO; else noItemVersion">
<div *ngIf="(versionRD$ | async)?.payload as itemVersion">
<div class="mb-2" *ngIf="versionsDTO.versionDTOs.length > 0 || displayWhenEmpty">
<h2 *ngIf="displayTitle" class="h4">{{"item.version.history.head" | translate}}</h2>
<ds-alert [type]="AlertTypeEnum.Info" *ngIf="itemVersion">
<ds-alert [type]="AlertTypeEnum.Info">
{{ "item.version.history.selected.alert" | translate : {version: itemVersion.version} }}
</ds-alert>
<ds-pagination *ngIf="versions?.page?.length > 0"
<ds-pagination *ngIf="versionsDTO.versionDTOs.length > 0"
(paginationChange)="onPageChange()"
[hideGear]="true"
[hidePagerWhenSinglePage]="true"
[paginationOptions]="options"
[collectionSize]="versions?.totalElements"
[collectionSize]="versionsDTO.totalElements"
[retainScrollPosition]="true">
<table class="table table-striped table-bordered align-middle my-2">
<thead>
@@ -22,10 +22,10 @@
</tr>
</thead>
<tbody>
<tr *ngFor="let version of versions?.page" [id]="'version-row-' + version.id">
<tr *ngFor="let versionDTO of versionsDTO.versionDTOs" [id]="'version-row-' + versionDTO.version.id">
<td class="version-row-element-version">
<ds-item-versions-row-element-version [hasDraftVersion]="hasDraftVersion$ | async"
[version]="version"
[version]="versionDTO.version"
[item]="item" [displayActions]="displayActions"
[itemVersion]="itemVersion"
[versionBeingEditedNumber]="versionBeingEditedNumber"
@@ -33,15 +33,15 @@
></ds-item-versions-row-element-version>
</td>
<td class="version-row-element-editor" *ngIf="(showSubmitter$ | async)">
{{version?.submitterName}}
{{versionDTO.version.submitterName}}
</td>
<td class="version-row-element-date">
{{version?.created | date : 'yyyy-MM-dd HH:mm:ss'}}
{{versionDTO.version.created | date : 'yyyy-MM-dd HH:mm:ss'}}
</td>
<td class="version-row-element-summary">
<div class="float-left">
<ng-container *ngIf="isThisBeingEdited(version); then editSummary else showSummary"></ng-container>
<ng-template #showSummary>{{version?.summary}}</ng-template>
<ng-container *ngIf="isThisBeingEdited(versionDTO.version); then editSummary else showSummary"></ng-container>
<ng-template #showSummary>{{versionDTO.version.summary}}</ng-template>
<ng-template #editSummary>
<input [attr.aria-label]="'item.version.history.table.action.editSummary' | translate"
[(ngModel)]="versionBeingEditedSummary" (keyup.enter)="onSummarySubmit()"
@@ -49,43 +49,44 @@
</ng-template>
</div>
<div class="float-right btn-group edit-field space-children-mr" *ngIf="displayActions">
<div class="float-right btn-group edit-field space-children-mr" *ngIf="displayActions && versionDTO.canEditVersion | async">
<ng-container *ngIf="isThisBeingEdited(versionDTO.version); else notThisBeingEdited">
<!--DISCARD EDIT-->
<ng-container *ngIf="(canEditVersion$(version) | async) && isThisBeingEdited(version)">
<button class="btn btn-sm"
[ngClass]="isThisBeingEdited(version) ? 'btn-outline-warning' : 'btn-outline-primary'"
<button class="btn btn-sm btn-outline-warning"
(click)="disableVersionEditing()"
title="{{'item.version.history.table.action.discardSummary' | translate}}">
<i class="fas fa-undo-alt fa-fw"></i>
</button>
</ng-container>
<!--EDIT / SAVE-->
<ng-container *ngIf="canEditVersion$(version) | async">
<button class="btn btn-outline-primary btn-sm version-row-element-edit"
*ngIf="!isThisBeingEdited(version)"
[disabled]="isAnyBeingEdited()"
(click)="enableVersionEditing(version)"
title="{{'item.version.history.table.action.editSummary' | translate}}">
<i class="fas fa-edit fa-fw"></i>
</button>
<!--SAVE-->
<button class="btn btn-outline-success btn-sm"
*ngIf="isThisBeingEdited(version)"
(click)="onSummarySubmit()"
title="{{'item.version.history.table.action.saveSummary' | translate}}">
<i class="fas fa-check fa-fw"></i>
</button>
</ng-container>
<ng-template #notThisBeingEdited>
<!--EDIT-->
<button class="btn btn-outline-primary btn-sm version-row-element-edit"
[disabled]="isAnyBeingEdited()"
(click)="enableVersionEditing(versionDTO.version)"
title="{{'item.version.history.table.action.editSummary' | translate}}">
<i class="fas fa-edit fa-fw"></i>
</button>
</ng-template>
</div>
</td>
</tr>
</tbody>
</table>
<div>*&nbsp;{{"item.version.history.selected" | translate}}</div>
</ds-pagination>
<ds-alert *ngIf="!itemVersion || versions?.page?.length === 0" [content]="'item.version.history.empty'"
[type]="AlertTypeEnum.Info"></ds-alert>
</div>
</div>
</div>
<ng-template #noItemVersion>
<ds-alert *ngIf="displayWhenEmpty"
[content]="'item.version.history.empty'"
[type]="AlertTypeEnum.Info">
</ds-alert>
</ng-template>

View File

@@ -12,13 +12,11 @@ import {
OnInit,
} from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import {
BehaviorSubject,
combineLatest,
Observable,
Subscription,
@@ -41,7 +39,6 @@ import { PaginationService } from '../../core/pagination/pagination.service';
import { Item } from '../../core/shared/item.model';
import {
getAllSucceededRemoteData,
getAllSucceededRemoteDataPayload,
getFirstCompletedRemoteData,
getFirstSucceededRemoteData,
getFirstSucceededRemoteDataPayload,
@@ -60,16 +57,35 @@ import { PaginationComponent } from '../../shared/pagination/pagination.componen
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
import { followLink } from '../../shared/utils/follow-link-config.model';
import { VarDirective } from '../../shared/utils/var.directive';
import { getItemPageRoute } from '../item-page-routing-paths';
import { ItemVersionsRowElementVersionComponent } from './item-versions-row-element-version/item-versions-row-element-version.component';
interface VersionsDTO {
totalElements: number;
versionDTOs: VersionDTO[];
}
interface VersionDTO {
version: Version;
canEditVersion: Observable<boolean>;
}
@Component({
selector: 'ds-item-versions',
templateUrl: './item-versions.component.html',
styleUrls: ['./item-versions.component.scss'],
standalone: true,
imports: [VarDirective, NgIf, AlertComponent, PaginationComponent, NgFor, RouterLink, NgClass, FormsModule, AsyncPipe, DatePipe, TranslateModule, ItemVersionsRowElementVersionComponent],
imports: [
AlertComponent,
AsyncPipe,
DatePipe,
FormsModule,
ItemVersionsRowElementVersionComponent,
NgClass,
NgFor,
NgIf,
PaginationComponent,
TranslateModule,
],
})
/**
@@ -128,13 +144,7 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
/**
* The version history's list of versions
*/
versionsRD$: BehaviorSubject<RemoteData<PaginatedList<Version>>> = new BehaviorSubject<RemoteData<PaginatedList<Version>>>(null);
/**
* Verify if the list of versions has at least one e-person to display
* Used to hide the "Editor" column when no e-persons are present to display
*/
hasEpersons$: Observable<boolean>;
versionsDTO$: Observable<VersionsDTO>;
/**
* Verify if there is an inprogress submission in the version history
@@ -162,15 +172,6 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
pageSize: this.pageSize,
});
/**
* The routes to the versions their item pages
* Key: Item ID
* Value: Route to item page
*/
itemPageRoutes$: Observable<{
[itemId: string]: string
}>;
/**
* The number of the version whose summary is currently being edited
*/
@@ -186,9 +187,6 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
*/
versionBeingEditedSummary: string;
canCreateVersion$: Observable<boolean>;
createVersionTitle$: Observable<string>;
constructor(private versionHistoryService: VersionHistoryDataService,
private versionService: VersionDataService,
private paginationService: PaginationService,
@@ -257,8 +255,7 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
this.notificationsService.warning(null, this.translateService.get(failureMessageKey, { 'version': this.versionBeingEditedNumber }));
}
this.disableVersionEditing();
},
);
});
}
/**
@@ -305,16 +302,22 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
*/
getAllVersions(versionHistory$: Observable<VersionHistory>): void {
const currentPagination = this.paginationService.getCurrentPagination(this.options.id, this.options);
combineLatest([versionHistory$, currentPagination]).pipe(
this.versionsDTO$ = combineLatest([versionHistory$, currentPagination]).pipe(
switchMap(([versionHistory, options]: [VersionHistory, PaginationComponentOptions]) => {
return this.versionHistoryService.getVersions(versionHistory.id,
new PaginatedSearchOptions({ pagination: Object.assign({}, options, { currentPage: options.currentPage }) }),
false, true, followLink('item'), followLink('eperson'));
}),
getFirstCompletedRemoteData(),
).subscribe((res: RemoteData<PaginatedList<Version>>) => {
this.versionsRD$.next(res);
});
getRemoteDataPayload(),
map((versions: PaginatedList<Version>) => ({
totalElements: versions.totalElements,
versionDTOs: (versions?.page ?? []).map((version: Version) => ({
version: version,
canEditVersion: this.canEditVersion$(version),
})),
})),
);
}
/**
@@ -348,22 +351,6 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
);
this.getAllVersions(this.versionHistory$);
this.hasEpersons$ = this.versionsRD$.pipe(
getAllSucceededRemoteData(),
getRemoteDataPayload(),
hasValueOperator(),
map((versions: PaginatedList<Version>) => versions.page.filter((version: Version) => version.eperson !== undefined).length > 0),
startWith(false),
);
this.itemPageRoutes$ = this.versionsRD$.pipe(
getAllSucceededRemoteDataPayload(),
switchMap((versions) => combineLatest(versions.page.map((version) => version.item.pipe(getAllSucceededRemoteDataPayload())))),
map((versions) => {
const itemPageRoutes = {};
versions.forEach((item) => itemPageRoutes[item.uuid] = getItemPageRoute(item));
return itemPageRoutes;
}),
);
}
}
@@ -380,3 +367,4 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
}
}

View File

@@ -120,7 +120,7 @@ export class MyDSpaceNewSubmissionDropdownComponent implements OnInit, OnDestroy
}
/**
* Method called on clicking the button "New Submition", It opens a dialog for
* Method called on clicking the button "New Submission", It opens a dialog for
* select a collection.
*/
openDialog(entity: ItemType) {

View File

@@ -140,7 +140,7 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
} else {
const modalRef = this.modalService.open(CollectionSelectorComponent);
// When the dialog are closes its takes the collection selected and
// uploads choosed file after adds owningCollection parameter
// uploads chosen file after adds owningCollection parameter
modalRef.result.then( (result) => {
uploader.onBuildItemForm = (fileItem: any, form: any) => {
form.append('owningCollection', result.uuid);

View File

@@ -15,8 +15,7 @@
[attr.aria-expanded]="isActive"
[attr.aria-controls]="expandableNavbarSectionId(section.id)"
class="d-flex flex-row flex-nowrap align-items-center gapx-1 ds-menu-toggler-wrapper"
[class.disabled]="section.model?.disabled"
id="browseDropdown">
[class.disabled]="section.model?.disabled">
<span class="flex-fill">
<ng-container
*ngComponentOutlet="(sectionMap$ | async).get(section.id).component; injector: (sectionMap$ | async).get(section.id).injector;"></ng-container>

View File

@@ -10,7 +10,7 @@
/** Mobile menu styling **/
@media screen and (max-width: map-get($grid-breakpoints, md)-0.02) {
.navbar {
width: 100%;
width: 100vw;
background-color: var(--bs-white);
position: absolute;
overflow: hidden;

View File

@@ -335,7 +335,7 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
}
/**
* Performs the choosen action calling the REST service.
* Performs the chosen action calling the REST service.
*
* @param {string} action
* the action (can be: ACCEPTED, REJECTED, DISCARDED, PENDING)
@@ -454,7 +454,7 @@ export class QualityAssuranceEventsComponent implements OnInit, OnDestroy {
}
/**
* Dispatch the Quality Assurance events retrival.
* Dispatch the Quality Assurance events retrieval.
*/
public getQualityAssuranceEvents(): Observable<QualityAssuranceEventData[]> {
return this.paginationService.getFindListOptions(this.paginationConfig.id, this.defaultConfig).pipe(

View File

@@ -6,6 +6,7 @@ import {
Component,
EventEmitter,
Input,
OnDestroy,
OnInit,
} from '@angular/core';
import { FormsModule } from '@angular/forms';
@@ -110,7 +111,7 @@ export interface QualityAssuranceEventData {
* Component to display a modal window for linking a project to an Quality Assurance event
* Shows information about the selected project and a selectable list.
*/
export class ProjectEntryImportModalComponent implements OnInit {
export class ProjectEntryImportModalComponent implements OnInit, OnDestroy {
/**
* The external source entry
*/
@@ -200,7 +201,7 @@ export class ProjectEntryImportModalComponent implements OnInit {
private selectService: SelectableListService) { }
/**
* Component intitialization.
* Component initialization.
*/
public ngOnInit(): void {
this.pagination = Object.assign(new PaginationComponentOptions(), { id: 'notifications-project-bound', pageSize: this.pageSize });

View File

@@ -5,7 +5,9 @@ import {
NgIf,
} from '@angular/common';
import {
AfterViewInit,
Component,
OnDestroy,
OnInit,
} from '@angular/core';
import { RouterLink } from '@angular/router';
@@ -40,7 +42,7 @@ import { NotificationsStateService } from '../../notifications-state.service';
standalone: true,
imports: [AlertComponent, NgIf, ThemedLoadingComponent, PaginationComponent, NgFor, RouterLink, AsyncPipe, TranslateModule, DatePipe],
})
export class QualityAssuranceSourceComponent implements OnInit {
export class QualityAssuranceSourceComponent implements OnDestroy, OnInit, AfterViewInit {
/**
* The pagination system configuration for HTML listing.
@@ -123,7 +125,7 @@ export class QualityAssuranceSourceComponent implements OnInit {
}
/**
* Dispatch the Quality Assurance source retrival.
* Dispatch the Quality Assurance source retrieval.
*/
public getQualityAssuranceSource(): void {
this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe(

View File

@@ -86,13 +86,13 @@ export class QualityAssuranceTopicsComponent implements OnInit, OnDestroy, After
protected subs: Subscription[] = [];
/**
* This property represents a sourceId which is used to retrive a topic
* This property represents a sourceId which is used to retrieve a topic
* @type {string}
*/
public sourceId: string;
/**
* This property represents a targetId (item-id) which is used to retrive a topic
* This property represents a targetId (item-id) which is used to retrieve a topic
* @type {string}
*/
public targetId: string;
@@ -171,7 +171,7 @@ export class QualityAssuranceTopicsComponent implements OnInit, OnDestroy, After
}
/**
* Dispatch the Quality Assurance topics retrival.
* Dispatch the Quality Assurance topics retrieval.
*/
public getQualityAssuranceTopics(source: string, target?: string): void {
this.subs.push(this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe(

View File

@@ -4,8 +4,10 @@ import {
NgIf,
} from '@angular/common';
import {
AfterViewInit,
Component,
Input,
OnDestroy,
OnInit,
} from '@angular/core';
import {
@@ -50,7 +52,7 @@ import { SuggestionTargetsStateService } from '../suggestion-targets.state.servi
],
standalone: true,
})
export class PublicationClaimComponent implements OnInit {
export class PublicationClaimComponent implements AfterViewInit, OnDestroy, OnInit {
/**
* The source for which to list targets
@@ -157,7 +159,7 @@ export class PublicationClaimComponent implements OnInit {
}
/**
* Dispatch the Suggestion Targets retrival.
* Dispatch the Suggestion Targets retrieval.
*/
public getSuggestionTargets(): void {
this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig).pipe(

View File

@@ -2,6 +2,7 @@ import { NgIf } from '@angular/common';
import {
Component,
Input,
OnInit,
Optional,
} from '@angular/core';
import {
@@ -27,7 +28,7 @@ import { ValueInputComponent } from '../value-input.component';
standalone: true,
imports: [FormsModule, NgIf, TranslateModule],
})
export class DateValueInputComponent extends ValueInputComponent<string> {
export class DateValueInputComponent extends ValueInputComponent<string> implements OnInit {
/**
* The current value of the date string
*/
@@ -38,7 +39,7 @@ export class DateValueInputComponent extends ValueInputComponent<string> {
*/
@Input() initialValue;
ngOnInit() {
ngOnInit(): void {
this.value = this.initialValue;
}

View File

@@ -2,6 +2,7 @@ import { NgIf } from '@angular/common';
import {
Component,
Input,
OnInit,
Optional,
} from '@angular/core';
import {
@@ -27,7 +28,7 @@ import { ValueInputComponent } from '../value-input.component';
standalone: true,
imports: [FormsModule, NgIf, TranslateModule],
})
export class StringValueInputComponent extends ValueInputComponent<string> {
export class StringValueInputComponent extends ValueInputComponent<string> implements OnInit {
/**
* The current value of the string
*/
@@ -38,7 +39,7 @@ export class StringValueInputComponent extends ValueInputComponent<string> {
*/
@Input() initialValue;
ngOnInit() {
ngOnInit(): void {
this.value = this.initialValue;
}

View File

@@ -7,6 +7,7 @@ import {
EventEmitter,
Input,
OnChanges,
OnInit,
Optional,
Output,
SimpleChanges,
@@ -39,7 +40,7 @@ import { ParameterSelectComponent } from './parameter-select/parameter-select.co
standalone: true,
imports: [NgIf, NgFor, ParameterSelectComponent, TranslateModule],
})
export class ProcessParametersComponent implements OnChanges {
export class ProcessParametersComponent implements OnChanges, OnInit {
/**
* The currently selected script
*/
@@ -59,7 +60,7 @@ export class ProcessParametersComponent implements OnChanges {
*/
parameterValues: ProcessParameter[];
ngOnInit() {
ngOnInit(): void {
if (hasValue(this.initialParams)) {
this.parameterValues = this.initialParams;
}

View File

@@ -3,6 +3,7 @@ import {
Component,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
} from '@angular/core';
@@ -44,7 +45,7 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
* Component for a user to edit their security information
* Displays a form containing a password field and a confirmation of the password
*/
export class ProfilePageSecurityFormComponent implements OnInit {
export class ProfilePageSecurityFormComponent implements OnDestroy, OnInit {
/**
* Emits the validity of the password

View File

@@ -31,3 +31,5 @@
<div class="ds-full-screen-loader" *ngIf="shouldShowFullscreenLoader">
<ds-loading [showMessage]="false"></ds-loading>
</div>
<ds-live-region></ds-live-region>

View File

@@ -41,6 +41,7 @@ import { ThemedFooterComponent } from '../footer/themed-footer.component';
import { ThemedHeaderNavbarWrapperComponent } from '../header-nav-wrapper/themed-header-navbar-wrapper.component';
import { slideSidebarPadding } from '../shared/animations/slide';
import { HostWindowService } from '../shared/host-window.service';
import { LiveRegionComponent } from '../shared/live-region/live-region.component';
import { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
import { MenuService } from '../shared/menu/menu.service';
import { MenuID } from '../shared/menu/menu-id.model';
@@ -67,6 +68,7 @@ import { SystemWideAlertBannerComponent } from '../system-wide-alert/alert-banne
ThemedFooterComponent,
NotificationsBoardComponent,
AsyncPipe,
LiveRegionComponent,
],
})
export class RootComponent implements OnInit {

View File

@@ -31,7 +31,7 @@ export class SearchNavbarComponent {
// The search form
searchForm;
// Whether or not the search bar is expanded, boolean for html ngIf, string fo AngularAnimation state change
// Whether or not the search bar is expanded, boolean for html ngIf, string for AngularAnimation state change
searchExpanded = false;
isExpanded = 'collapsed';

View File

@@ -8,6 +8,7 @@ import {
Component,
Inject,
Injector,
OnInit,
} from '@angular/core';
import { Router } from '@angular/router';
import {
@@ -34,7 +35,7 @@ import { MenuService } from '../../../menu/menu.service';
standalone: true,
imports: [NgbDropdownModule, NgbTooltipModule, NgFor, NgIf, NgComponentOutlet, TranslateModule, AsyncPipe],
})
export class DsoEditMenuExpandableSectionComponent extends MenuSectionComponent {
export class DsoEditMenuExpandableSectionComponent extends MenuSectionComponent implements OnInit {
menuID: MenuID = MenuID.DSO_EDIT;
itemModel;

View File

@@ -48,7 +48,7 @@ export class DsoEditMenuSectionComponent extends MenuSectionComponent implements
}
/**
* Activate the section's model funtion
* Activate the section's model function
*/
public activate(event: any) {
event.preventDefault();

View File

@@ -61,7 +61,7 @@ export class DsoWithdrawnReinstateModalService {
* @param correctionType - The type of correction.
* @param reason - The reason for the request.
* Reloads the current page in order to update the withdrawn/reinstate button.
* and desplay a notification box.
* and display a notification box.
*/
sendQARequest(target: string, correctionType: 'request-reinstate' | 'request-withdrawn', reason: string): void {
this.qaEventDataService.postData(target, correctionType, '', reason)

View File

@@ -146,7 +146,7 @@ export class EntityDropdownComponent implements OnInit, OnDestroy {
}
/**
* Method used from infitity scroll for retrive more data on scroll down
* Method used from infitity scroll for retrieve more data on scroll down
*/
public onScrollDown() {
if ( this.hasNextPage ) {

View File

@@ -1,6 +1,8 @@
import {
Component,
Input,
OnDestroy,
OnInit,
} from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { Subscription } from 'rxjs';
@@ -15,7 +17,7 @@ import { AlertType } from '../alert/alert-type';
standalone: true,
imports: [AlertComponent],
})
export class ErrorComponent {
export class ErrorComponent implements OnDestroy, OnInit {
@Input() message = 'Error...';
@@ -31,7 +33,7 @@ export class ErrorComponent {
}
ngOnInit() {
ngOnInit(): void {
if (this.message === undefined) {
this.subscription = this.translate.get('error.default').subscribe((message: string) => {
this.message = message;
@@ -39,7 +41,7 @@ export class ErrorComponent {
}
}
ngOnDestroy() {
ngOnDestroy(): void {
if (this.subscription !== undefined) {
this.subscription.unsubscribe();
}

View File

@@ -6,11 +6,13 @@ import {
NgTemplateOutlet,
} from '@angular/common';
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ComponentFactoryResolver,
ContentChildren,
DoCheck,
EventEmitter,
Inject,
Input,
@@ -143,7 +145,8 @@ import { DsDynamicLookupRelationModalComponent } from './relation-lookup-modal/d
],
standalone: true,
})
export class DsDynamicFormControlContainerComponent extends DynamicFormControlContainerComponent implements OnInit, OnChanges, OnDestroy {
export class DsDynamicFormControlContainerComponent extends DynamicFormControlContainerComponent
implements OnInit, OnChanges, OnDestroy, AfterViewInit, DoCheck {
@ContentChildren(DynamicTemplateDirective) contentTemplateList: QueryList<DynamicTemplateDirective>;
// eslint-disable-next-line @angular-eslint/no-input-rename
@Input('templates') inputTemplateList: QueryList<DynamicTemplateDirective>;

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