diff --git a/.browserslistrc b/.browserslistrc deleted file mode 100644 index 427441dc93..0000000000 --- a/.browserslistrc +++ /dev/null @@ -1,17 +0,0 @@ -# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries - -# For the full list of supported browsers by the Angular framework, please see: -# https://angular.io/guide/browser-support - -# You can see what browsers were selected by your queries by running: -# npx browserslist - -last 1 Chrome version -last 1 Firefox version -last 2 Edge major versions -last 2 Safari major versions -last 2 iOS major versions -Firefox ESR -not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6fae54f1f9..219074780e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,6 +31,8 @@ jobs: # 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" + # Bump Node heap size (OOM in CI after upgrading to Angular 15) + NODE_OPTIONS: '--max-old-space-size=4096' strategy: # Create a matrix of Node versions to test against (in parallel) matrix: diff --git a/README.md b/README.md index c90dc1d08f..689c64a292 100644 --- a/README.md +++ b/README.md @@ -413,8 +413,7 @@ dspace-angular │ ├── merge-i18n-files.ts * │ ├── serve.ts * │ ├── sync-i18n-files.ts * -│ ├── test-rest.ts * -│ └── webpack.js * +│ └── test-rest.ts * ├── src * The source of the application │ ├── app * The source code of the application, subdivided by module/page. │ ├── assets * Folder for static resources diff --git a/angular.json b/angular.json index 56c75d7b2d..5e597d4d30 100644 --- a/angular.json +++ b/angular.json @@ -274,9 +274,18 @@ } } }, - "defaultProject": "dspace-angular", "cli": { "analytics": false, - "defaultCollection": "@angular-eslint/schematics" + "schematicCollections": [ + "@angular-eslint/schematics" + ] + }, + "schematics": { + "@angular-eslint/schematics:application": { + "setParserOptionsProject": true + }, + "@angular-eslint/schematics:library": { + "setParserOptionsProject": true + } } } diff --git a/config/config.example.yml b/config/config.example.yml index f1e6be76aa..ea38303fa3 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -187,6 +187,9 @@ languages: - code: gd label: Gàidhlig active: true + - code: it + label: Italiano + active: true - code: lv label: Latviešu active: true @@ -214,6 +217,9 @@ languages: - code: tr label: Türkçe active: true + - code: vi + label: Tiếng Việt + active: true - code: kk label: Қазақ active: true diff --git a/package.json b/package.json index e1bedeb02b..59766e993b 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,9 @@ "build:stats": "ng build --stats-json", "build:prod": "yarn run build:ssr", "build:ssr": "ng build --configuration production && ng run dspace-angular:server:production", - "test": "ng test --sourceMap=true --watch=false --configuration test", - "test:watch": "nodemon --exec \"ng test --sourceMap=true --watch=true --configuration test\"", - "test:headless": "ng test --sourceMap=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage", + "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", "lint": "ng lint", "lint-fix": "ng lint --fix=true", "e2e": "ng e2e", @@ -55,136 +55,136 @@ "ts-node": "10.2.1" }, "dependencies": { - "@angular/animations": "~13.3.12", - "@angular/cdk": "^13.2.6", - "@angular/common": "~13.3.12", - "@angular/compiler": "~13.3.12", - "@angular/core": "~13.3.12", - "@angular/forms": "~13.3.12", - "@angular/localize": "13.3.12", - "@angular/platform-browser": "~13.3.12", - "@angular/platform-browser-dynamic": "~13.3.12", - "@angular/platform-server": "~13.3.12", - "@angular/router": "~13.3.12", - "@babel/runtime": "7.17.2", + "@angular/animations": "^15.2.8", + "@angular/cdk": "^15.2.8", + "@angular/common": "^15.2.8", + "@angular/compiler": "^15.2.8", + "@angular/core": "^15.2.8", + "@angular/forms": "^15.2.8", + "@angular/localize": "15.2.8", + "@angular/platform-browser": "^15.2.8", + "@angular/platform-browser-dynamic": "^15.2.8", + "@angular/platform-server": "^15.2.8", + "@angular/router": "^15.2.8", + "@babel/runtime": "7.21.0", "@kolkov/ngx-gallery": "^2.0.1", "@material-ui/core": "^4.11.0", - "@material-ui/icons": "^4.9.1", + "@material-ui/icons": "^4.11.3", "@ng-bootstrap/ng-bootstrap": "^11.0.0", "@ng-dynamic-forms/core": "^15.0.0", "@ng-dynamic-forms/ui-ng-bootstrap": "^15.0.0", - "@ngrx/effects": "^13.0.2", - "@ngrx/router-store": "^13.0.2", - "@ngrx/store": "^13.0.2", - "@nguniversal/express-engine": "^13.0.2", - "@ngx-translate/core": "^13.0.0", - "@nicky-lenaers/ngx-scroll-to": "^13.0.0", + "@ngrx/effects": "^15.4.0", + "@ngrx/router-store": "^15.4.0", + "@ngrx/store": "^15.4.0", + "@nguniversal/express-engine": "^15.2.1", + "@ngx-translate/core": "^14.0.0", + "@nicky-lenaers/ngx-scroll-to": "^14.0.0", "@types/grecaptcha": "^3.0.4", "angular-idle-preload": "3.0.0", - "angulartics2": "^12.0.0", + "angulartics2": "^12.2.0", "axios": "^0.27.2", "bootstrap": "^4.6.1", "cerialize": "0.1.18", - "cli-progress": "^3.8.0", + "cli-progress": "^3.12.0", "colors": "^1.4.0", "compression": "^1.7.4", - "cookie-parser": "1.4.5", - "core-js": "^3.7.0", + "cookie-parser": "1.4.6", + "core-js": "^3.30.1", "date-fns": "^2.29.3", "date-fns-tz": "^1.3.7", - "deepmerge": "^4.2.2", - "ejs": "^3.1.8", - "express": "^4.17.1", + "deepmerge": "^4.3.1", + "ejs": "^3.1.9", + "express": "^4.18.2", "express-rate-limit": "^5.1.3", - "fast-json-patch": "^3.0.0-1", + "fast-json-patch": "^3.1.1", "filesize": "^6.1.0", "http-proxy-middleware": "^1.0.5", - "isbot": "^3.6.5", + "isbot": "^3.6.10", "js-cookie": "2.2.1", "js-yaml": "^4.1.0", - "json5": "^2.2.2", - "jsonschema": "1.4.0", + "json5": "^2.2.3", + "jsonschema": "1.4.1", "jwt-decode": "^3.1.2", "klaro": "^0.7.18", "lodash": "^4.17.21", "lru-cache": "^7.14.1", "markdown-it": "^13.0.1", - "markdown-it-mathjax3": "^4.3.1", + "markdown-it-mathjax3": "^4.3.2", "mirador": "^3.3.0", "mirador-dl-plugin": "^0.13.0", "mirador-share-plugin": "^0.11.0", "morgan": "^1.10.0", - "ng-mocks": "^13.1.1", + "ng-mocks": "^14.10.0", "ng2-file-upload": "1.4.0", "ng2-nouislider": "^1.8.3", - "ngx-infinite-scroll": "^10.0.1", - "ngx-pagination": "5.0.0", + "ngx-infinite-scroll": "^15.0.0", + "ngx-pagination": "6.0.3", "ngx-sortablejs": "^11.1.0", - "ngx-ui-switch": "^13.0.2", + "ngx-ui-switch": "^14.0.3", "nouislider": "^14.6.3", - "pem": "1.14.4", - "prop-types": "^15.7.2", - "react-copy-to-clipboard": "^5.0.1", + "pem": "1.14.7", + "prop-types": "^15.8.1", + "react-copy-to-clipboard": "^5.1.0", "reflect-metadata": "^0.1.13", - "rxjs": "^7.5.5", - "sanitize-html": "^2.7.2", - "sortablejs": "1.13.0", + "rxjs": "^7.8.0", + "sanitize-html": "^2.10.0", + "sortablejs": "1.15.0", "uuid": "^8.3.2", "webfontloader": "1.6.28", "zone.js": "~0.11.5" }, "devDependencies": { - "@angular-builders/custom-webpack": "~13.1.0", - "@angular-devkit/build-angular": "~13.3.10", - "@angular-eslint/builder": "13.1.0", - "@angular-eslint/eslint-plugin": "13.1.0", - "@angular-eslint/eslint-plugin-template": "13.1.0", - "@angular-eslint/schematics": "13.1.0", - "@angular-eslint/template-parser": "13.1.0", - "@angular/cli": "~13.3.10", - "@angular/compiler-cli": "~13.3.12", - "@angular/language-service": "~13.3.12", + "@angular-builders/custom-webpack": "~15.0.0", + "@angular-devkit/build-angular": "^15.2.6", + "@angular-eslint/builder": "15.2.1", + "@angular-eslint/eslint-plugin": "15.2.1", + "@angular-eslint/eslint-plugin-template": "15.2.1", + "@angular-eslint/schematics": "15.2.1", + "@angular-eslint/template-parser": "15.2.1", + "@angular/cli": "^15.2.6", + "@angular/compiler-cli": "^15.2.8", + "@angular/language-service": "^15.2.8", "@cypress/schematic": "^1.5.0", - "@fortawesome/fontawesome-free": "^6.2.1", - "@ngrx/store-devtools": "^13.0.2", - "@ngtools/webpack": "^13.2.6", - "@nguniversal/builders": "^13.1.1", + "@fortawesome/fontawesome-free": "^6.4.0", + "@ngrx/store-devtools": "^15.4.0", + "@ngtools/webpack": "^15.2.6", + "@nguniversal/builders": "^15.2.1", "@types/deep-freeze": "0.1.2", - "@types/ejs": "^3.1.1", - "@types/express": "^4.17.9", + "@types/ejs": "^3.1.2", + "@types/express": "^4.17.17", "@types/jasmine": "~3.6.0", "@types/js-cookie": "2.2.6", - "@types/lodash": "^4.14.165", + "@types/lodash": "^4.14.194", "@types/node": "^14.14.9", - "@types/sanitize-html": "^2.6.2", - "@typescript-eslint/eslint-plugin": "5.11.0", - "@typescript-eslint/parser": "5.11.0", - "axe-core": "^4.4.3", + "@types/sanitize-html": "^2.9.0", + "@typescript-eslint/eslint-plugin": "^5.59.1", + "@typescript-eslint/parser": "^5.59.1", + "axe-core": "^4.7.0", "compression-webpack-plugin": "^9.2.0", "copy-webpack-plugin": "^6.4.1", "cross-env": "^7.0.3", - "cypress": "12.9.0", - "cypress-axe": "^1.1.0", + "cypress": "12.10.0", + "cypress-axe": "^1.4.0", "deep-freeze": "0.0.1", - "eslint": "^8.2.0", - "eslint-plugin-deprecation": "^1.3.2", - "eslint-plugin-import": "^2.25.4", + "eslint": "^8.39.0", + "eslint-plugin-deprecation": "^1.4.1", + "eslint-plugin-import": "^2.27.5", "eslint-plugin-jsdoc": "^39.6.4", "eslint-plugin-jsonc": "^2.6.0", "eslint-plugin-lodash": "^7.4.0", "eslint-plugin-unused-imports": "^2.0.0", - "express-static-gzip": "^2.1.5", + "express-static-gzip": "^2.1.7", "jasmine-core": "^3.8.0", "jasmine-marbles": "0.9.2", - "karma": "^6.3.14", - "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~3.0.2", + "karma": "^6.4.2", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage-istanbul-reporter": "~3.0.3", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "karma-mocha-reporter": "2.2.5", "ngx-mask": "^13.1.7", - "nodemon": "^2.0.20", - "postcss": "^8.1", + "nodemon": "^2.0.22", + "postcss": "^8.4", "postcss-apply": "0.12.0", "postcss-import": "^14.0.0", "postcss-loader": "^4.0.3", @@ -194,14 +194,14 @@ "react-dom": "^16.14.0", "rimraf": "^3.0.2", "rxjs-spy": "^8.0.2", - "sass": "~1.33.0", + "sass": "~1.62.0", "sass-loader": "^12.6.0", - "sass-resources-loader": "^2.1.1", + "sass-resources-loader": "^2.2.5", "ts-node": "^8.10.2", - "typescript": "~4.5.5", - "webpack": "^5.76.0", - "webpack-bundle-analyzer": "^4.4.0", + "typescript": "~4.8.4", + "webpack": "5.76.1", + "webpack-bundle-analyzer": "^4.8.0", "webpack-cli": "^4.2.0", - "webpack-dev-server": "^4.5.0" + "webpack-dev-server": "^4.13.3" } } diff --git a/scripts/webpack.js b/scripts/webpack.js deleted file mode 100644 index 93f17b4619..0000000000 --- a/scripts/webpack.js +++ /dev/null @@ -1,13 +0,0 @@ -const path = require('path'); -const child_process = require('child_process'); - -const heapSize = 4096; -const webpackPath = path.join('node_modules', 'webpack', 'bin', 'webpack.js'); - -const params = [ - '--max_old_space_size=' + heapSize, - webpackPath, - ...process.argv.slice(2) -]; - -child_process.spawn('node', params, { stdio:'inherit' }); diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.html b/src/app/access-control/epeople-registry/epeople-registry.component.html index 3a7806a231..e3a8e2c590 100644 --- a/src/app/access-control/epeople-registry/epeople-registry.component.html +++ b/src/app/access-control/epeople-registry/epeople-registry.component.html @@ -68,18 +68,18 @@ {{epersonDto.eperson.id}} - {{epersonDto.eperson.name}} + {{ dsoNameService.getName(epersonDto.eperson) }} {{epersonDto.eperson.email}}
diff --git a/src/app/access-control/epeople-registry/epeople-registry.component.ts b/src/app/access-control/epeople-registry/epeople-registry.component.ts index 55233d8173..706dcab690 100644 --- a/src/app/access-control/epeople-registry/epeople-registry.component.ts +++ b/src/app/access-control/epeople-registry/epeople-registry.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs'; @@ -21,6 +21,7 @@ import { RequestService } from '../../core/data/request.service'; import { PageInfo } from '../../core/shared/page-info.model'; import { NoContent } from '../../core/shared/NoContent.model'; import { PaginationService } from '../../core/pagination/pagination.service'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-epeople-registry', @@ -89,11 +90,13 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy { private translateService: TranslateService, private notificationsService: NotificationsService, private authorizationService: AuthorizationDataService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private router: Router, private modalService: NgbModal, private paginationService: PaginationService, - public requestService: RequestService) { + public requestService: RequestService, + public dsoNameService: DSONameService, + ) { this.currentSearchQuery = ''; this.currentSearchScope = 'metadata'; this.searchForm = this.formBuilder.group(({ @@ -121,7 +124,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy { this.subs.push(this.ePeople$.pipe( switchMap((epeople: PaginatedList) => { if (epeople.pageInfo.totalElements > 0) { - return combineLatest(...epeople.page.map((eperson) => { + return combineLatest([...epeople.page.map((eperson: EPerson) => { return this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined).pipe( map((authorized) => { const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel(); @@ -130,7 +133,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy { return epersonDtoModel; }) ); - })).pipe(map((dtos: EpersonDtoModel[]) => { + })]).pipe(map((dtos: EpersonDtoModel[]) => { return buildPaginatedList(epeople.pageInfo, dtos); })); } else { @@ -237,7 +240,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy { if (hasValue(ePerson.id)) { this.epersonService.deleteEPerson(ePerson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData) => { if (restResponse.hasSucceeded) { - this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', {name: ePerson.name})); + this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', {name: this.dsoNameService.getName(ePerson)})); } else { this.notificationsService.error('Error occured when trying to delete EPerson with id: ' + ePerson.id + ' with code: ' + restResponse.statusCode + ' and message: ' + restResponse.errorMessage); } diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html index 7386177ea0..228449a8a5 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html @@ -65,9 +65,13 @@ {{group.id}} - {{group.name}} - {{(group.object | async)?.payload?.name}} + + + {{ dsoNameService.getName(group) }} + + + {{ dsoNameService.getName(undefined) }} diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts index bf03e1defb..fb911e709c 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.spec.ts @@ -2,7 +2,7 @@ import { Observable, of as observableOf } from 'rxjs'; import { CommonModule } from '@angular/common'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { BrowserModule, By } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; @@ -116,9 +116,9 @@ describe('EPersonFormComponent', () => { const controlModel = model; const controlState = { value: controlModel.value, disabled: controlModel.disabled }; const controlOptions = this.createAbstractControlOptions(controlModel.validators, controlModel.asyncValidators, controlModel.updateOn); - controls[model.id] = new FormControl(controlState, controlOptions); + controls[model.id] = new UntypedFormControl(controlState, controlOptions); }); - return new FormGroup(controls, options); + return new UntypedFormGroup(controls, options); }, createAbstractControlOptions(validatorsConfig = null, asyncValidatorsConfig = null, updateOn = null) { return { diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts index 5a7ab735ca..c60de00aed 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicCheckboxModel, DynamicFormControlModel, @@ -37,6 +37,7 @@ import { ValidateEmailNotTaken } from './validators/email-taken.validator'; import { Registration } from '../../../core/shared/registration.model'; import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; import { TYPE_REQUEST_FORGOT } from '../../../register-email-form/register-email-form.component'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-eperson-form', @@ -108,7 +109,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy { /** * A FormGroup that combines all inputs */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * An EventEmitter that's fired whenever the form is being submitted @@ -192,6 +193,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy { private paginationService: PaginationService, public requestService: RequestService, private epersonRegistrationService: EpersonRegistrationService, + public dsoNameService: DSONameService, ) { this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => { this.epersonInitial = eperson; @@ -212,14 +214,14 @@ export class EPersonFormComponent implements OnInit, OnDestroy { */ initialisePage() { - observableCombineLatest( + observableCombineLatest([ this.translateService.get(`${this.messagePrefix}.firstName`), this.translateService.get(`${this.messagePrefix}.lastName`), this.translateService.get(`${this.messagePrefix}.email`), this.translateService.get(`${this.messagePrefix}.canLogIn`), this.translateService.get(`${this.messagePrefix}.requireCertificate`), this.translateService.get(`${this.messagePrefix}.emailHint`), - ).subscribe(([firstName, lastName, email, canLogIn, requireCertificate, emailHint]) => { + ]).subscribe(([firstName, lastName, email, canLogIn, requireCertificate, emailHint]) => { this.firstName = new DynamicInputModel({ id: 'firstName', label: firstName, @@ -386,10 +388,10 @@ export class EPersonFormComponent implements OnInit, OnDestroy { getFirstCompletedRemoteData() ).subscribe((rd: RemoteData) => { if (rd.hasSucceeded) { - this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', { name: ePersonToCreate.name })); + this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', { name: this.dsoNameService.getName(ePersonToCreate) })); this.submitForm.emit(ePersonToCreate); } else { - this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure', { name: ePersonToCreate.name })); + this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure', { name: this.dsoNameService.getName(ePersonToCreate) })); this.cancelForm.emit(); } }); @@ -425,10 +427,10 @@ export class EPersonFormComponent implements OnInit, OnDestroy { const response = this.epersonService.updateEPerson(editedEperson); response.pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData) => { if (rd.hasSucceeded) { - this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.edited.success', { name: editedEperson.name })); + this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.edited.success', { name: this.dsoNameService.getName(editedEperson) })); this.submitForm.emit(editedEperson); } else { - this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.edited.failure', { name: editedEperson.name })); + this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.edited.failure', { name: this.dsoNameService.getName(editedEperson) })); this.cancelForm.emit(); } }); @@ -476,7 +478,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy { if (hasValue(eperson.id)) { this.epersonService.deleteEPerson(eperson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData) => { if (restResponse.hasSucceeded) { - this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { name: eperson.name })); + this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { name: this.dsoNameService.getName(eperson) })); this.submitForm.emit(); } else { this.notificationsService.error('Error occured when trying to delete EPerson with id: ' + eperson.id + ' with code: ' + restResponse.statusCode + ' and message: ' + restResponse.errorMessage); @@ -554,7 +556,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy { .subscribe((list: PaginatedList) => { if (list.totalElements > 0) { this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.' + notificationSection + '.failure.emailInUse', { - name: ePerson.name, + name: this.dsoNameService.getName(ePerson), email: ePerson.email })); } diff --git a/src/app/access-control/group-registry/group-form/group-form.component.html b/src/app/access-control/group-registry/group-form/group-form.component.html index d86adc674b..77a81a8daa 100644 --- a/src/app/access-control/group-registry/group-form/group-form.component.html +++ b/src/app/access-control/group-registry/group-form/group-form.component.html @@ -26,7 +26,7 @@ + [content]="(messagePrefix + '.alert.workflowGroup' | translate:{ name: dsoNameService.getName((getLinkedDSO(groupBeingEdited) | async)?.payload), comcol: (getLinkedDSO(groupBeingEdited) | async)?.payload?.type, comcolEditRolesRoute: (getLinkedEditRolesRoute(groupBeingEdited) | async) })"> { let component: GroupFormComponent; @@ -130,9 +132,9 @@ describe('GroupFormComponent', () => { const controlModel = model; const controlState = { value: controlModel.value, disabled: controlModel.disabled }; const controlOptions = this.createAbstractControlOptions(controlModel.validators, controlModel.asyncValidators, controlModel.updateOn); - controls[model.id] = new FormControl(controlState, controlOptions); + controls[model.id] = new UntypedFormControl(controlState, controlOptions); }); - return new FormGroup(controls, options); + return new UntypedFormGroup(controls, options); }, createAbstractControlOptions(validatorsConfig = null, asyncValidatorsConfig = null, updateOn = null) { return { @@ -188,7 +190,7 @@ describe('GroupFormComponent', () => { translateService = getMockTranslateService(); router = new RouterMock(); notificationService = new NotificationsServiceStub(); - TestBed.configureTestingModule({ + return TestBed.configureTestingModule({ imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, TranslateModule.forRoot({ loader: { @@ -198,7 +200,8 @@ describe('GroupFormComponent', () => { }), ], declarations: [GroupFormComponent], - providers: [GroupFormComponent, + providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: GroupDataService, useValue: groupsDataServiceStub }, { provide: DSpaceObjectDataService, useValue: dsoDataServiceStub }, @@ -240,8 +243,8 @@ describe('GroupFormComponent', () => { fixture.detectChanges(); }); - it('should emit a new group using the correct values', waitForAsync(() => { - fixture.whenStable().then(() => { + it('should emit a new group using the correct values', (async () => { + await fixture.whenStable().then(() => { expect(component.submitForm.emit).toHaveBeenCalledWith(expected); }); })); @@ -303,8 +306,8 @@ describe('GroupFormComponent', () => { expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations); }); - it('should emit the existing group using the correct new values', waitForAsync(() => { - fixture.whenStable().then(() => { + it('should emit the existing group using the correct new values', (async () => { + await fixture.whenStable().then(() => { expect(component.submitForm.emit).toHaveBeenCalledWith(expected2); }); })); diff --git a/src/app/access-control/group-registry/group-form/group-form.component.ts b/src/app/access-control/group-registry/group-form/group-form.component.ts index 4302d126ea..3c0547cca5 100644 --- a/src/app/access-control/group-registry/group-form/group-form.component.ts +++ b/src/app/access-control/group-registry/group-form/group-form.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, HostListener, OnDestroy, OnInit, Output, ChangeDetectorRef } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { @@ -46,6 +46,7 @@ import { followLink } from '../../../shared/utils/follow-link-config.model'; import { NoContent } from '../../../core/shared/NoContent.model'; import { Operation } from 'fast-json-patch'; import { ValidateGroupExists } from './validators/group-exists.validator'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; import { environment } from '../../../../environments/environment'; @Component({ @@ -95,7 +96,7 @@ export class GroupFormComponent implements OnInit, OnDestroy { /** * A FormGroup that combines all inputs */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * An EventEmitter that's fired whenever the form is being submitted @@ -134,7 +135,8 @@ export class GroupFormComponent implements OnInit, OnDestroy { groupNameValueChangeSubscribe: Subscription; - constructor(public groupDataService: GroupDataService, + constructor( + public groupDataService: GroupDataService, private ePersonDataService: EPersonDataService, private dSpaceObjectDataService: DSpaceObjectDataService, private formBuilderService: FormBuilderService, @@ -145,7 +147,9 @@ export class GroupFormComponent implements OnInit, OnDestroy { private authorizationService: AuthorizationDataService, private modalService: NgbModal, public requestService: RequestService, - protected changeDetectorRef: ChangeDetectorRef) { + protected changeDetectorRef: ChangeDetectorRef, + public dsoNameService: DSONameService, + ) { } ngOnInit() { @@ -331,7 +335,7 @@ export class GroupFormComponent implements OnInit, OnDestroy { .subscribe((list: PaginatedList) => { if (list.totalElements > 0) { this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.' + notificationSection + '.failure.groupNameInUse', { - name: group.name + name: this.dsoNameService.getName(group), })); } })); @@ -364,10 +368,10 @@ export class GroupFormComponent implements OnInit, OnDestroy { getFirstCompletedRemoteData() ).subscribe((rd: RemoteData) => { if (rd.hasSucceeded) { - this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.edited.success', { name: rd.payload.name })); + this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.edited.success', { name: this.dsoNameService.getName(rd.payload) })); this.submitForm.emit(rd.payload); } else { - this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.edited.failure', { name: group.name })); + this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.edited.failure', { name: this.dsoNameService.getName(group) })); this.cancelForm.emit(); } }); @@ -427,11 +431,11 @@ export class GroupFormComponent implements OnInit, OnDestroy { this.groupDataService.delete(group.id).pipe(getFirstCompletedRemoteData()) .subscribe((rd: RemoteData) => { if (rd.hasSucceeded) { - this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.deleted.success', { name: group.name })); + this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.deleted.success', { name: this.dsoNameService.getName(group) })); this.onCancel(); } else { this.notificationsService.error( - this.translateService.get(this.messagePrefix + '.notification.deleted.failure.title', { name: group.name }), + this.translateService.get(this.messagePrefix + '.notification.deleted.failure.title', { name: this.dsoNameService.getName(group) }), this.translateService.get(this.messagePrefix + '.notification.deleted.failure.content', { cause: rd.errorMessage })); } }); diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.html b/src/app/access-control/group-registry/group-form/members-list/members-list.component.html index 282ee89674..cc9bf34d64 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.html +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.html @@ -57,8 +57,12 @@ {{ePerson.eperson.id}} - {{ePerson.eperson.name}} + + + {{ dsoNameService.getName(ePerson.eperson) }} + + {{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}
{{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }} @@ -69,7 +73,7 @@ (click)="deleteMemberFromGroup(ePerson)" [disabled]="actionConfig.remove.disabled" [ngClass]="['btn btn-sm', actionConfig.remove.css]" - title="{{messagePrefix + '.table.edit.buttons.remove' | translate: {name: ePerson.eperson.name} }}"> + title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}"> @@ -77,7 +81,7 @@ (click)="addMemberToGroup(ePerson)" [disabled]="actionConfig.add.disabled" [ngClass]="['btn btn-sm', actionConfig.add.css]" - title="{{messagePrefix + '.table.edit.buttons.add' | translate: {name: ePerson.eperson.name} }}"> + title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}"> @@ -117,8 +121,12 @@ {{ePerson.eperson.id}} - {{ePerson.eperson.name}} + + + {{ dsoNameService.getName(ePerson.eperson) }} + + {{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}
{{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }} @@ -129,14 +137,14 @@ (click)="deleteMemberFromGroup(ePerson)" [disabled]="actionConfig.remove.disabled" [ngClass]="['btn btn-sm', actionConfig.remove.css]" - title="{{messagePrefix + '.table.edit.buttons.remove' | translate: {name: ePerson.eperson.name} }}"> + title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}"> diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts index 4b65535fce..7c8db399bc 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.spec.ts @@ -28,6 +28,8 @@ import { NotificationsServiceStub } from '../../../../shared/testing/notificatio import { RouterMock } from '../../../../shared/mocks/router.mock'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; describe('MembersListComponent', () => { let component: MembersListComponent; @@ -118,7 +120,7 @@ describe('MembersListComponent', () => { translateService = getMockTranslateService(); paginationService = new PaginationServiceStub(); - TestBed.configureTestingModule({ + return TestBed.configureTestingModule({ imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, TranslateModule.forRoot({ loader: { @@ -135,6 +137,7 @@ describe('MembersListComponent', () => { { provide: FormBuilderService, useValue: builderService }, { provide: Router, useValue: new RouterMock() }, { provide: PaginationService, useValue: paginationService }, + { provide: DSONameService, useValue: new DSONameServiceMock() }, ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts index d0fc046093..b3e686c012 100644 --- a/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts +++ b/src/app/access-control/group-registry/group-form/members-list/members-list.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { @@ -27,6 +27,7 @@ import { NotificationsService } from '../../../../shared/notifications/notificat import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; /** * Keys to keep track of specific subscriptions @@ -141,9 +142,10 @@ export class MembersListComponent implements OnInit, OnDestroy { public ePersonDataService: EPersonDataService, protected translateService: TranslateService, protected notificationsService: NotificationsService, - protected formBuilder: FormBuilder, + protected formBuilder: UntypedFormBuilder, protected paginationService: PaginationService, - private router: Router + protected router: Router, + public dsoNameService: DSONameService, ) { this.currentSearchQuery = ''; this.currentSearchScope = 'metadata'; @@ -253,7 +255,7 @@ export class MembersListComponent implements OnInit, OnDestroy { this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { if (activeGroup != null) { const response = this.groupDataService.deleteMemberFromGroup(activeGroup, ePerson.eperson); - this.showNotifications('deleteMember', response, ePerson.eperson.name, activeGroup); + this.showNotifications('deleteMember', response, this.dsoNameService.getName(ePerson.eperson), activeGroup); } else { this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); } @@ -269,7 +271,7 @@ export class MembersListComponent implements OnInit, OnDestroy { this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { if (activeGroup != null) { const response = this.groupDataService.addMemberToGroup(activeGroup, ePerson.eperson); - this.showNotifications('addMember', response, ePerson.eperson.name, activeGroup); + this.showNotifications('addMember', response, this.dsoNameService.getName(ePerson.eperson), activeGroup); } else { this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); } diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html index d1574b0dba..d009f0283e 100644 --- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html +++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html @@ -53,15 +53,19 @@ {{group.id}} - {{group.name}} - {{(group.object | async)?.payload?.name}} + + + {{ dsoNameService.getName(group) }} + + + {{ dsoNameService.getName((group.object | async)?.payload) }}
@@ -70,7 +74,7 @@
@@ -108,14 +112,18 @@ {{group.id}} - {{group.name}} - {{(group.object | async)?.payload?.name}} + + + {{ dsoNameService.getName(group) }} + + + {{ dsoNameService.getName((group.object | async)?.payload)}}
diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts index ff0fd121af..ac5750dcac 100644 --- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts +++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.spec.ts @@ -29,6 +29,8 @@ import { NotificationsServiceStub } from '../../../../shared/testing/notificatio import { map } from 'rxjs/operators'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; describe('SubgroupsListComponent', () => { let component: SubgroupsListComponent; @@ -108,6 +110,7 @@ describe('SubgroupsListComponent', () => { ], declarations: [SubgroupsListComponent], providers: [SubgroupsListComponent, + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: GroupDataService, useValue: groupsDataServiceStub }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: FormBuilderService, useValue: builderService }, diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts index 5f1700e07d..0cff730c62 100644 --- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts +++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs'; @@ -18,6 +18,7 @@ import { PaginationComponentOptions } from '../../../../shared/pagination/pagina import { NoContent } from '../../../../core/shared/NoContent.model'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { followLink } from '../../../../shared/utils/follow-link-config.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; /** * Keys to keep track of specific subscriptions @@ -86,9 +87,11 @@ export class SubgroupsListComponent implements OnInit, OnDestroy { constructor(public groupDataService: GroupDataService, private translateService: TranslateService, private notificationsService: NotificationsService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private paginationService: PaginationService, - private router: Router) { + private router: Router, + public dsoNameService: DSONameService, + ) { this.currentSearchQuery = ''; } @@ -177,7 +180,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy { this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { if (activeGroup != null) { const response = this.groupDataService.deleteSubGroupFromGroup(activeGroup, subgroup); - this.showNotifications('deleteSubgroup', response, subgroup.name, activeGroup); + this.showNotifications('deleteSubgroup', response, this.dsoNameService.getName(subgroup), activeGroup); } else { this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); } @@ -193,7 +196,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy { if (activeGroup != null) { if (activeGroup.uuid !== subgroup.uuid) { const response = this.groupDataService.addSubGroupToGroup(activeGroup, subgroup); - this.showNotifications('addSubgroup', response, subgroup.name, activeGroup); + this.showNotifications('addSubgroup', response, this.dsoNameService.getName(subgroup), activeGroup); } else { this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.subgroupToAddIsActiveGroup')); } diff --git a/src/app/access-control/group-registry/groups-registry.component.html b/src/app/access-control/group-registry/groups-registry.component.html index bf14034d4f..828aadc95a 100644 --- a/src/app/access-control/group-registry/groups-registry.component.html +++ b/src/app/access-control/group-registry/groups-registry.component.html @@ -56,8 +56,8 @@ {{groupDto.group.id}} - {{groupDto.group.name}} - {{(groupDto.group.object | async)?.payload?.name}} + {{ dsoNameService.getName(groupDto.group) }} + {{ dsoNameService.getName((groupDto.group.object | async)?.payload) }} {{groupDto.epersons?.totalElements + groupDto.subgroups?.totalElements}}
@@ -65,7 +65,7 @@ @@ -80,7 +80,7 @@
diff --git a/src/app/access-control/group-registry/groups-registry.component.spec.ts b/src/app/access-control/group-registry/groups-registry.component.spec.ts index 239939e70d..635ba727c2 100644 --- a/src/app/access-control/group-registry/groups-registry.component.spec.ts +++ b/src/app/access-control/group-registry/groups-registry.component.spec.ts @@ -32,8 +32,10 @@ import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { NoContent } from '../../core/shared/NoContent.model'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock, UNDEFINED_NAME } from '../../shared/mocks/dso-name.service.mock'; -describe('GroupRegistryComponent', () => { +describe('GroupsRegistryComponent', () => { let component: GroupsRegistryComponent; let fixture: ComponentFixture; let ePersonDataServiceStub: any; @@ -160,7 +162,7 @@ describe('GroupRegistryComponent', () => { authorizationService = jasmine.createSpyObj('authorizationService', ['isAuthorized']); setIsAuthorized(true, true); paginationService = new PaginationServiceStub(); - TestBed.configureTestingModule({ + return TestBed.configureTestingModule({ imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, TranslateModule.forRoot({ loader: { @@ -171,6 +173,7 @@ describe('GroupRegistryComponent', () => { ], declarations: [GroupsRegistryComponent], providers: [GroupsRegistryComponent, + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: GroupDataService, useValue: groupsDataServiceStub }, { provide: DSpaceObjectDataService, useValue: dsoDataServiceStub }, @@ -208,7 +211,7 @@ describe('GroupRegistryComponent', () => { it('should display community/collection name if present', () => { const collectionNamesFound = fixture.debugElement.queryAll(By.css('#groups tr td:nth-child(3)')); expect(collectionNamesFound.length).toEqual(2); - expect(collectionNamesFound[0].nativeElement.textContent).toEqual(''); + expect(collectionNamesFound[0].nativeElement.textContent).toEqual(UNDEFINED_NAME); expect(collectionNamesFound[1].nativeElement.textContent).toEqual('testgroupid2objectName'); }); diff --git a/src/app/access-control/group-registry/groups-registry.component.ts b/src/app/access-control/group-registry/groups-registry.component.ts index 70c9b22852..ccfd155e39 100644 --- a/src/app/access-control/group-registry/groups-registry.component.ts +++ b/src/app/access-control/group-registry/groups-registry.component.ts @@ -1,5 +1,5 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { @@ -37,6 +37,7 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c import { NoContent } from '../../core/shared/NoContent.model'; import { PaginationService } from '../../core/pagination/pagination.service'; import { followLink } from '../../shared/utils/follow-link-config.model'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-groups-registry', @@ -99,12 +100,14 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy { private dSpaceObjectDataService: DSpaceObjectDataService, private translateService: TranslateService, private notificationsService: NotificationsService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, protected routeService: RouteService, private router: Router, private authorizationService: AuthorizationDataService, private paginationService: PaginationService, - public requestService: RequestService) { + public requestService: RequestService, + public dsoNameService: DSONameService, + ) { this.currentSearchQuery = ''; this.searchForm = this.formBuilder.group(({ query: this.currentSearchQuery, @@ -201,10 +204,10 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy { .subscribe((rd: RemoteData) => { if (rd.hasSucceeded) { this.deletedGroupsIds = [...this.deletedGroupsIds, group.group.id]; - this.notificationsService.success(this.translateService.get(this.messagePrefix + 'notification.deleted.success', { name: group.group.name })); + this.notificationsService.success(this.translateService.get(this.messagePrefix + 'notification.deleted.success', { name: this.dsoNameService.getName(group.group) })); } else { this.notificationsService.error( - this.translateService.get(this.messagePrefix + 'notification.deleted.failure.title', { name: group.group.name }), + this.translateService.get(this.messagePrefix + 'notification.deleted.failure.title', { name: this.dsoNameService.getName(group.group) }), this.translateService.get(this.messagePrefix + 'notification.deleted.failure.content', { cause: rd.errorMessage })); } }); diff --git a/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts b/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts index 5c6885ae72..6d3138987a 100644 --- a/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts +++ b/src/app/admin/admin-registries/metadata-registry/metadata-schema-form/metadata-schema-form.component.ts @@ -5,7 +5,7 @@ import { DynamicFormLayout, DynamicInputModel } from '@ng-dynamic-forms/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { RegistryService } from '../../../../core/registry/registry.service'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; import { take } from 'rxjs/operators'; @@ -66,7 +66,7 @@ export class MetadataSchemaFormComponent implements OnInit, OnDestroy { /** * A FormGroup that combines all inputs */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * An EventEmitter that's fired whenever the form is being submitted diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts index 1c000c3c76..0beb306d6c 100644 --- a/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts +++ b/src/app/admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.ts @@ -5,7 +5,7 @@ import { DynamicFormLayout, DynamicInputModel } from '@ng-dynamic-forms/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { RegistryService } from '../../../../core/registry/registry.service'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; import { take } from 'rxjs/operators'; @@ -82,7 +82,7 @@ export class MetadataFieldFormComponent implements OnInit, OnDestroy { /** * A FormGroup that combines all inputs */ - formGroup: FormGroup; + formGroup: UntypedFormGroup; /** * An EventEmitter that's fired whenever the form is being submitted diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts index e478aa3ef3..ee3de42131 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts @@ -19,7 +19,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote- import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../../../../shared/theme-support/theme.service'; import { AccessStatusDataService } from '../../../../../core/data/access-status-data.service'; -import { AccessStatusObject } from '../../../../../shared/object-list/access-status-badge/access-status.model'; +import { AccessStatusObject } from '../../../../../shared/object-collection/shared/badges/access-status-badge/access-status.model'; import { AuthService } from '../../../../../core/auth/auth.service'; import { AuthServiceStub } from '../../../../../shared/testing/auth-service.stub'; import { FileService } from '../../../../../core/shared/file.service'; diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts index 1ab8fee8c2..dab6694f36 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.ts @@ -13,6 +13,7 @@ import { BitstreamDataService } from '../../../../../core/data/bitstream-data.se import { GenericConstructor } from '../../../../../core/shared/generic-constructor'; import { ListableObjectDirective } from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive'; import { ThemeService } from '../../../../../shared/theme-support/theme.service'; +import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; @listableObjectComponent(ItemSearchResult, ViewMode.GridElement, Context.AdminSearch) @Component({ @@ -28,12 +29,14 @@ export class ItemAdminSearchResultGridElementComponent extends SearchResultGridE @ViewChild('badges', { static: true }) badges: ElementRef; @ViewChild('buttons', { static: true }) buttons: ElementRef; - constructor(protected truncatableService: TruncatableService, - protected bitstreamDataService: BitstreamDataService, - private themeService: ThemeService, - private componentFactoryResolver: ComponentFactoryResolver + constructor( + public dsoNameService: DSONameService, + protected truncatableService: TruncatableService, + protected bitstreamDataService: BitstreamDataService, + private themeService: ThemeService, + private componentFactoryResolver: ComponentFactoryResolver, ) { - super(truncatableService, bitstreamDataService); + super(dsoNameService, truncatableService, bitstreamDataService); } /** diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html index 259512552c..991508335f 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html @@ -2,6 +2,5 @@ [viewMode]="viewModes.ListElement" [index]="index" [linkType]="linkType" - [listID]="listID" - [hideBadges]="true"> + [listID]="listID"> diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component.ts index bbb1df3c49..2eec0cfd0c 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-group-selector/supervision-order-group-selector.component.ts @@ -82,7 +82,7 @@ export class SupervisionOrderGroupSelectorComponent { getFirstCompletedRemoteData(), ).subscribe((rd: RemoteData) => { if (rd.state === 'Success') { - this.notificationsService.success(this.translateService.get('supervision-group-selector.notification.create.success.title', { name: this.selectedGroup.name })); + this.notificationsService.success(this.translateService.get('supervision-group-selector.notification.create.success.title', { name: this.dsoNameService.getName(this.selectedGroup) })); this.create.emit(rd.payload); this.close(); } else { diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component.html b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component.html index fa6a2eafff..84d8c052cd 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component.html +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component.html @@ -7,7 +7,7 @@ - {{supervisionOrder.group.name}} + {{ dsoNameService.getName(supervisionOrder.group) }} diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component.ts index 11b6400ffd..93c6441e92 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/supervision-order-status/supervision-order-status.component.ts @@ -8,6 +8,7 @@ import { Group } from '../../../../../../core/eperson/models/group.model'; import { getFirstCompletedRemoteData } from '../../../../../../core/shared/operators'; import { isNotEmpty } from '../../../../../../shared/empty.util'; import { RemoteData } from '../../../../../../core/data/remote-data'; +import { DSONameService } from '../../../../../../core/breadcrumbs/dso-name.service'; export interface SupervisionOrderListEntry { supervisionOrder: SupervisionOrder; @@ -33,6 +34,11 @@ export class SupervisionOrderStatusComponent implements OnChanges { @Output() delete: EventEmitter = new EventEmitter(); + constructor( + public dsoNameService: DSONameService, + ) { + } + ngOnChanges(changes: SimpleChanges): void { if (changes && changes.supervisionOrderList) { this.getSupervisionOrderEntries(changes.supervisionOrderList.currentValue) diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts index 628fc3f89c..a8f0581ec0 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts @@ -11,7 +11,7 @@ import { URLCombiner } from '../../../../../core/url-combiner/url-combiner'; import { WorkspaceItemAdminWorkflowActionsComponent } from './workspace-item-admin-workflow-actions.component'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; import { - getWorkflowItemDeleteRoute, + getWorkspaceItemDeleteRoute, } from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { Item } from '../../../../../core/shared/item.model'; import { RemoteData } from '../../../../../core/data/remote-data'; @@ -83,7 +83,7 @@ describe('WorkspaceItemAdminWorkflowActionsComponent', () => { it('should render a delete button with the correct link', () => { const button = fixture.debugElement.query(By.css('a.delete-link')); const link = button.nativeElement.href; - expect(link).toContain(new URLCombiner(getWorkflowItemDeleteRoute(wsi.id)).toString()); + expect(link).toContain(new URLCombiner(getWorkspaceItemDeleteRoute(wsi.id)).toString()); }); it('should render a policies button with the correct link', () => { diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts index adbd421628..36678460da 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts @@ -11,7 +11,7 @@ import { SupervisionOrderGroupSelectorComponent } from './supervision-order-group-selector/supervision-order-group-selector.component'; import { - getWorkflowItemDeleteRoute + getWorkspaceItemDeleteRoute } from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { ITEM_EDIT_AUTHORIZATIONS_PATH } from '../../../../../item-page/edit-item-page/edit-item-page.routing-paths'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; @@ -105,10 +105,10 @@ export class WorkspaceItemAdminWorkflowActionsComponent implements OnInit { } /** - * Returns the path to the delete page of this workflow item + * Returns the path to the delete page of this workspace item */ getDeleteRoute(): string { - return getWorkflowItemDeleteRoute(this.wsi.id); + return getWorkspaceItemDeleteRoute(this.wsi.id); } /** diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts index 68f10916d5..fd9d21e227 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workflow-item/workflow-item-search-result-admin-workflow-grid-element.component.ts @@ -23,6 +23,7 @@ import { import { take } from 'rxjs/operators'; import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model'; import { ThemeService } from '../../../../../shared/theme-support/theme.service'; +import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; @listableObjectComponent(WorkflowItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch) @Component({ @@ -55,13 +56,14 @@ export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends S public item$: Observable; constructor( + public dsoNameService: DSONameService, private componentFactoryResolver: ComponentFactoryResolver, private linkService: LinkService, protected truncatableService: TruncatableService, private themeService: ThemeService, protected bitstreamDataService: BitstreamDataService ) { - super(truncatableService, bitstreamDataService); + super(dsoNameService, truncatableService, bitstreamDataService); } /** diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts index f18c18ca1c..d6f39e79fe 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-grid-element/workspace-item/workspace-item-search-result-admin-workflow-grid-element.component.ts @@ -1,4 +1,4 @@ -import { Component, ComponentFactoryResolver, ElementRef, ViewChild } from '@angular/core'; +import { Component, ComponentFactoryResolver, ElementRef, ViewChild, OnInit } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { map, mergeMap, take, tap } from 'rxjs/operators'; @@ -36,6 +36,7 @@ import { DSpaceObject } from '../../../../../core/shared/dspace-object.model'; import { SupervisionOrder } from '../../../../../core/supervision-order/models/supervision-order.model'; import { PaginatedList } from '../../../../../core/data/paginated-list.model'; import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service'; +import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; @listableObjectComponent(WorkspaceItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch) @Component({ @@ -46,7 +47,7 @@ import { SupervisionOrderDataService } from '../../../../../core/supervision-ord /** * The component for displaying a grid element for an workflow item on the admin workflow search page */ -export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent { +export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent implements OnInit { /** * The item linked to the workspace item @@ -79,6 +80,7 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends @ViewChild('buttons', { static: true }) buttons: ElementRef; constructor( + public dsoNameService: DSONameService, private componentFactoryResolver: ComponentFactoryResolver, private linkService: LinkService, protected truncatableService: TruncatableService, @@ -86,7 +88,7 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends protected bitstreamDataService: BitstreamDataService, protected supervisionOrderDataService: SupervisionOrderDataService, ) { - super(truncatableService, bitstreamDataService); + super(dsoNameService, truncatableService, bitstreamDataService); } /** diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.ts index b1db3f99ce..d0e773d696 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workflow-item/workflow-item-search-result-admin-workflow-list-element.component.ts @@ -39,7 +39,7 @@ export class WorkflowItemSearchResultAdminWorkflowListElementComponent extends S constructor(private linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService, + public dsoNameService: DSONameService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { super(truncatableService, dsoNameService, appConfig); diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workspace-item/workspace-item-search-result-admin-workflow-list-element.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workspace-item/workspace-item-search-result-admin-workflow-list-element.component.ts index 597ed8bbe7..3d6d1c8e44 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workspace-item/workspace-item-search-result-admin-workflow-list-element.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/admin-workflow-search-result-list-element/workspace-item/workspace-item-search-result-admin-workflow-list-element.component.ts @@ -59,7 +59,7 @@ export class WorkspaceItemSearchResultAdminWorkflowListElementComponent extends public supervisionOrder$: BehaviorSubject = new BehaviorSubject([]); constructor(private linkService: LinkService, - protected dsoNameService: DSONameService, + public dsoNameService: DSONameService, protected supervisionOrderDataService: SupervisionOrderDataService, protected truncatableService: TruncatableService, @Inject(APP_CONFIG) protected appConfig: AppConfig diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index b69e47c82c..deb68f1ea9 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -209,7 +209,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone { path: REQUEST_COPY_MODULE_PATH, loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule), - canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] + canActivate: [EndUserAgreementCurrentUserGuard] }, { path: FORBIDDEN_PATH, diff --git a/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.html b/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.html index eddc5e345a..af3afe98f8 100644 --- a/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.html +++ b/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.html @@ -1,5 +1,5 @@
-

{{'bitstream.download.page' | translate:{bitstream: (bitstream$ | async)?.name} }}

+

{{'bitstream.download.page' | translate:{ bitstream: dsoNameService.getName((bitstream$ | async)) } }}

diff --git a/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.ts b/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.ts index 5d29eb7f73..6425996fd2 100644 --- a/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.ts +++ b/src/app/collection-page/edit-item-template-page/edit-item-template-page.component.ts @@ -9,6 +9,7 @@ import { getCollectionEditRoute } from '../collection-page-routing-paths'; import { Item } from '../../core/shared/item.model'; import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; import { AlertType } from '../../shared/alert/aletr-type'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-edit-item-template-page', @@ -35,8 +36,11 @@ export class EditItemTemplatePageComponent implements OnInit { */ AlertTypeEnum = AlertType; - constructor(protected route: ActivatedRoute, - public itemTemplateService: ItemTemplateDataService) { + constructor( + protected route: ActivatedRoute, + public itemTemplateService: ItemTemplateDataService, + public dsoNameService: DSONameService, + ) { } ngOnInit(): void { diff --git a/src/app/collection-page/edit-item-template-page/item-template-page.resolver.spec.ts b/src/app/collection-page/edit-item-template-page/item-template-page.resolver.spec.ts index 2712a194c0..95f0d888e4 100644 --- a/src/app/collection-page/edit-item-template-page/item-template-page.resolver.spec.ts +++ b/src/app/collection-page/edit-item-template-page/item-template-page.resolver.spec.ts @@ -2,18 +2,22 @@ import { first } from 'rxjs/operators'; import { ItemTemplatePageResolver } from './item-template-page.resolver'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../shared/mocks/dso-name.service.mock'; describe('ItemTemplatePageResolver', () => { describe('resolve', () => { let resolver: ItemTemplatePageResolver; let itemTemplateService: any; + let dsoNameService: DSONameServiceMock; const uuid = '1234-65487-12354-1235'; beforeEach(() => { itemTemplateService = { findByCollectionID: (id: string) => createSuccessfulRemoteDataObject$({ id }) }; - resolver = new ItemTemplatePageResolver(itemTemplateService); + dsoNameService = new DSONameServiceMock(); + resolver = new ItemTemplatePageResolver(dsoNameService as DSONameService, itemTemplateService); }); it('should resolve an item template with the correct id', (done) => { diff --git a/src/app/collection-page/edit-item-template-page/item-template-page.resolver.ts b/src/app/collection-page/edit-item-template-page/item-template-page.resolver.ts index 719a04196f..586617c44c 100644 --- a/src/app/collection-page/edit-item-template-page/item-template-page.resolver.ts +++ b/src/app/collection-page/edit-item-template-page/item-template-page.resolver.ts @@ -6,13 +6,17 @@ import { ItemTemplateDataService } from '../../core/data/item-template-data.serv import { Observable } from 'rxjs'; import { followLink } from '../../shared/utils/follow-link-config.model'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; /** * This class represents a resolver that requests a specific collection's item template before the route is activated */ @Injectable() export class ItemTemplatePageResolver implements Resolve> { - constructor(private itemTemplateService: ItemTemplateDataService) { + constructor( + public dsoNameService: DSONameService, + private itemTemplateService: ItemTemplateDataService, + ) { } /** diff --git a/src/app/community-list-page/community-list/community-list.component.html b/src/app/community-list-page/community-list/community-list.component.html index ea772bb891..94c14236c1 100644 --- a/src/app/community-list-page/community-list/community-list.component.html +++ b/src/app/community-list-page/community-list/community-list.component.html @@ -8,10 +8,10 @@
@@ -25,8 +25,8 @@ class="example-tree-node expandable-node"> @@ -66,12 +66,11 @@ class="example-tree-node childless-node"> diff --git a/src/app/community-list-page/community-list/community-list.component.spec.ts b/src/app/community-list-page/community-list/community-list.component.spec.ts index 575edf14e8..ce6b27dbeb 100644 --- a/src/app/community-list-page/community-list/community-list.component.spec.ts +++ b/src/app/community-list-page/community-list/community-list.component.spec.ts @@ -16,6 +16,7 @@ import { of as observableOf } from 'rxjs'; import { By } from '@angular/platform-browser'; import { isEmpty, isNotEmpty } from '../../shared/empty.util'; import { FlatNode } from '../flat-node.model'; +import { RouterLinkWithHref } from '@angular/router'; describe('CommunityListComponent', () => { let component: CommunityListComponent; @@ -193,7 +194,8 @@ describe('CommunityListComponent', () => { }, }), CdkTreeModule, - RouterTestingModule], + RouterTestingModule, + RouterLinkWithHref], declarations: [CommunityListComponent], providers: [CommunityListComponent, { provide: CommunityListService, useValue: communityListServiceStub },], @@ -230,9 +232,14 @@ describe('CommunityListComponent', () => { expect(showMoreEl).toBeTruthy(); }); + it('should not render the show more button as an empty link', () => { + const debugElements = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); + expect(debugElements).toBeTruthy(); + }); + describe('when show more of top communities is clicked', () => { beforeEach(fakeAsync(() => { - const showMoreLink = fixture.debugElement.query(By.css('.show-more-node a')); + const showMoreLink = fixture.debugElement.query(By.css('.show-more-node .btn-outline-primary')); showMoreLink.triggerEventHandler('click', { preventDefault: () => {/**/ } @@ -240,6 +247,7 @@ describe('CommunityListComponent', () => { tick(); fixture.detectChanges(); })); + it('tree contains maximum of currentPage (2) * (2) elementsPerPage of first top communities, or less if there are less communities (3)', () => { const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a')); const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a')); diff --git a/src/app/community-list-page/community-list/community-list.component.ts b/src/app/community-list-page/community-list/community-list.component.ts index 556387da25..5b2f930813 100644 --- a/src/app/community-list-page/community-list/community-list.component.ts +++ b/src/app/community-list-page/community-list/community-list.component.ts @@ -7,6 +7,7 @@ import { FlatTreeControl } from '@angular/cdk/tree'; import { isEmpty } from '../../shared/empty.util'; import { FlatNode } from '../flat-node.model'; import { FindListOptions } from '../../core/data/find-list-options.model'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; /** * A tree-structured list of nodes representing the communities, their subCommunities and collections. @@ -32,7 +33,10 @@ export class CommunityListComponent implements OnInit, OnDestroy { paginationConfig: FindListOptions; - constructor(private communityListService: CommunityListService) { + constructor( + protected communityListService: CommunityListService, + public dsoNameService: DSONameService, + ) { this.paginationConfig = new FindListOptions(); this.paginationConfig.elementsPerPage = 2; this.paginationConfig.currentPage = 1; diff --git a/src/app/community-page/community-page.component.html b/src/app/community-page/community-page.component.html index 8e06fd2db3..6d5262d933 100644 --- a/src/app/community-page/community-page.component.html +++ b/src/app/community-page/community-page.component.html @@ -5,7 +5,7 @@
- + diff --git a/src/app/community-page/community-page.component.ts b/src/app/community-page/community-page.component.ts index b1a0cfc946..a5bbff3cee 100644 --- a/src/app/community-page/community-page.component.ts +++ b/src/app/community-page/community-page.component.ts @@ -19,6 +19,7 @@ import { AuthorizationDataService } from '../core/data/feature-authorization/aut import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { getCommunityPageRoute } from './community-page-routing-paths'; import { redirectOn4xx } from '../core/shared/authorized.operators'; +import { DSONameService } from '../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-community-page', @@ -57,7 +58,8 @@ export class CommunityPageComponent implements OnInit { private route: ActivatedRoute, private router: Router, private authService: AuthService, - private authorizationDataService: AuthorizationDataService + private authorizationDataService: AuthorizationDataService, + public dsoNameService: DSONameService, ) { } diff --git a/src/app/community-page/create-community-page/create-community-page.component.html b/src/app/community-page/create-community-page/create-community-page.component.html index 71a580b0aa..57039040c2 100644 --- a/src/app/community-page/create-community-page/create-community-page.component.html +++ b/src/app/community-page/create-community-page/create-community-page.component.html @@ -3,7 +3,7 @@
-

{{ 'community.create.sub-head' | translate:{ parent: parent.name } }}

+

{{ 'community.create.sub-head' | translate:{ parent: dsoNameService.getName(parent) } }}

diff --git a/src/app/community-page/create-community-page/create-community-page.component.ts b/src/app/community-page/create-community-page/create-community-page.component.ts index b332fad100..eea0908388 100644 --- a/src/app/community-page/create-community-page/create-community-page.component.ts +++ b/src/app/community-page/create-community-page/create-community-page.component.ts @@ -7,6 +7,7 @@ import { CreateComColPageComponent } from '../../shared/comcol/comcol-forms/crea import { NotificationsService } from '../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; import { RequestService } from '../../core/data/request.service'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; /** * Component that represents the page where a user can create a new Community @@ -22,12 +23,13 @@ export class CreateCommunityPageComponent extends CreateComColPageComponent
-

{{ 'community.delete.text' | translate:{ dso: dso.name } }}

+

{{ 'community.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}

diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts index 10b3016a52..67a6f98ac0 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts @@ -68,7 +68,7 @@ describe('DsoEditMetadataValueComponent', () => { }); it('should not show a badge', () => { - expect(fixture.debugElement.query(By.css('ds-type-badge'))).toBeNull(); + expect(fixture.debugElement.query(By.css('ds-themed-type-badge'))).toBeNull(); }); describe('when no changes have been made', () => { @@ -134,7 +134,7 @@ describe('DsoEditMetadataValueComponent', () => { }); it('should show a badge', () => { - expect(fixture.debugElement.query(By.css('ds-type-badge'))).toBeTruthy(); + expect(fixture.debugElement.query(By.css('ds-themed-type-badge'))).toBeTruthy(); }); assertButton(EDIT_BTN, true, true); diff --git a/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts b/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts index 5053a4b83d..5f76d87265 100644 --- a/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts +++ b/src/app/dso-shared/dso-edit-metadata/metadata-field-selector/metadata-field-selector.component.ts @@ -17,7 +17,7 @@ import { } from '../../../core/shared/operators'; import { Observable } from 'rxjs/internal/Observable'; import { RegistryService } from '../../../core/registry/registry.service'; -import { FormControl } from '@angular/forms'; +import { UntypedFormControl } from '@angular/forms'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; import { hasValue } from '../../../shared/empty.util'; import { Subscription } from 'rxjs/internal/Subscription'; @@ -70,7 +70,7 @@ export class MetadataFieldSelectorComponent implements OnInit, OnDestroy, AfterV /** * FormControl for the input */ - public input: FormControl = new FormControl(); + public input: UntypedFormControl = new UntypedFormControl(); /** * The current query to update mdFieldOptions$ for diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/journal-issue/journal-issue-grid-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-grid-elements/journal-issue/journal-issue-grid-element.component.spec.ts index df9e8ea7fd..6c55616760 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/journal-issue/journal-issue-grid-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-grid-elements/journal-issue/journal-issue-grid-element.component.spec.ts @@ -10,6 +10,8 @@ import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem = Object.assign(new Item(), { bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), @@ -48,6 +50,7 @@ describe('JournalIssueGridElementComponent', () => { imports: [NoopAnimationsModule], declarations: [JournalIssueGridElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/journal-volume/journal-volume-grid-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-grid-elements/journal-volume/journal-volume-grid-element.component.spec.ts index ac082e7f1b..14f63d5781 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/journal-volume/journal-volume-grid-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-grid-elements/journal-volume/journal-volume-grid-element.component.spec.ts @@ -10,6 +10,8 @@ import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem = Object.assign(new Item(), { bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), @@ -48,6 +50,7 @@ describe('JournalVolumeGridElementComponent', () => { imports: [NoopAnimationsModule], declarations: [JournalVolumeGridElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/journal/journal-grid-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-grid-elements/journal/journal-grid-element.component.spec.ts index 93287313aa..e4a2375441 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/journal/journal-grid-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-grid-elements/journal/journal-grid-element.component.spec.ts @@ -10,6 +10,8 @@ import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem = Object.assign(new Item(), { bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), @@ -54,6 +56,7 @@ describe('JournalGridElementComponent', () => { imports: [NoopAnimationsModule], declarations: [JournalGridElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html index c6c29c2f1a..c6a2f5bcc3 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html index 5849105f96..b7e1330c86 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html index 9a3ea95c07..e4c9ada3ca 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.spec.ts index 8c33ffdc1e..59af3b9e7b 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component.spec.ts @@ -6,6 +6,8 @@ import { of as observableOf } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem: Item = Object.assign(new Item(), { bundles: observableOf({}), @@ -43,6 +45,7 @@ describe('JournalIssueListElementComponent', () => { TestBed.configureTestingModule({ declarations: [JournalIssueListElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.spec.ts index f4bf0d250b..663c1e477e 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component.spec.ts @@ -6,6 +6,8 @@ import { of as observableOf } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem: Item = Object.assign(new Item(), { bundles: observableOf({}), @@ -43,6 +45,7 @@ describe('JournalVolumeListElementComponent', () => { TestBed.configureTestingModule({ declarations: [JournalVolumeListElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.spec.ts b/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.spec.ts index b82876e364..e5dd55772b 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.spec.ts +++ b/src/app/entity-groups/journal-entities/item-list-elements/journal/journal-list-element.component.spec.ts @@ -6,6 +6,8 @@ import { of as observableOf } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem: Item = Object.assign(new Item(), { bundles: observableOf({}), @@ -34,9 +36,10 @@ describe('JournalListElementComponent', () => { }; beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ + return TestBed.configureTestingModule({ declarations: [JournalListElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html index 36b7e98c51..9d02a5d837 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html @@ -12,7 +12,7 @@
- +
- +
- +
- +
- + { imports: [NoopAnimationsModule], declarations: [OrgUnitGridElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/research-entities/item-grid-elements/person/person-grid-element.component.spec.ts b/src/app/entity-groups/research-entities/item-grid-elements/person/person-grid-element.component.spec.ts index b4d563b0d5..ca0784e997 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/person/person-grid-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-grid-elements/person/person-grid-element.component.spec.ts @@ -10,6 +10,8 @@ import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem = Object.assign(new Item(), { bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), @@ -48,6 +50,7 @@ describe('PersonGridElementComponent', () => { imports: [NoopAnimationsModule], declarations: [PersonGridElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/research-entities/item-grid-elements/project/project-grid-element.component.spec.ts b/src/app/entity-groups/research-entities/item-grid-elements/project/project-grid-element.component.spec.ts index 5f4808bd2a..3f92bfe410 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/project/project-grid-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-grid-elements/project/project-grid-element.component.spec.ts @@ -10,6 +10,8 @@ import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem = Object.assign(new Item(), { bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), @@ -42,6 +44,7 @@ describe('ProjectGridElementComponent', () => { imports: [NoopAnimationsModule], declarations: [ProjectGridElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.html index 2dbccd4346..bd40090197 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/org-unit/org-unit-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html index 853a717965..fe26fd7063 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html index a54d136de2..cf47f947cc 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html @@ -8,18 +8,18 @@ rel="noopener noreferrer" [routerLink]="[itemPageRoute]" class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
- - + +
- - + +
- +

diff --git a/src/app/entity-groups/research-entities/item-list-elements/org-unit/org-unit-list-element.component.spec.ts b/src/app/entity-groups/research-entities/item-list-elements/org-unit/org-unit-list-element.component.spec.ts index 1cca1d3314..275accc956 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/org-unit/org-unit-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/org-unit/org-unit-list-element.component.spec.ts @@ -6,6 +6,8 @@ import { of as observableOf } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem: Item = Object.assign(new Item(), { bundles: observableOf({}), @@ -37,6 +39,7 @@ describe('OrgUnitListElementComponent', () => { TestBed.configureTestingModule({ declarations: [OrgUnitListElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.spec.ts b/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.spec.ts index 8e86a129ce..dc874b8ec8 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/person/person-list-element.component.spec.ts @@ -6,6 +6,8 @@ import { of as observableOf } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem: Item = Object.assign(new Item(), { bundles: observableOf({}), @@ -37,6 +39,7 @@ describe('PersonListElementComponent', () => { TestBed.configureTestingModule({ declarations: [PersonListElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.spec.ts b/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.spec.ts index 0c08d7eaaa..02241e3060 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/item-list-elements/project/project-list-element.component.spec.ts @@ -6,6 +6,8 @@ import { Item } from '../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { ProjectListElementComponent } from './project-list-element.component'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../shared/mocks/dso-name.service.mock'; const mockItem: Item = Object.assign(new Item(), { bundles: observableOf({}), @@ -37,6 +39,7 @@ describe('ProjectListElementComponent', () => { TestBed.configureTestingModule({ declarations: [ProjectListElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html index 0bba83a209..9495577c01 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html @@ -18,7 +18,7 @@
- +
- +
- +
- - +
- - +
- - + diff --git a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.html b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.html index 1771f3d2bc..ec4dbd4323 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.html +++ b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.html @@ -1,12 +1,12 @@ - + - + + [innerHTML]="mdRepresentation.getValue()" + [ngbTooltip]="mdRepresentation.allMetadata(['dc.description']).length > 0 ? descTemplate : null"> diff --git a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts index eff6fd0b31..429f2986b9 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts @@ -34,7 +34,7 @@ describe('OrgUnitItemMetadataListElementComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(OrgUnitItemMetadataListElementComponent); comp = fixture.componentInstance; - comp.metadataRepresentation = mockItemMetadataRepresentation; + comp.mdRepresentation = mockItemMetadataRepresentation; fixture.detectChanges(); }); diff --git a/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.html b/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.html index 97632117f4..6f56056781 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.html +++ b/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.html @@ -1,15 +1,15 @@ - - + - + + [innerHTML]="mdRepresentation.getValue()" + [ngbTooltip]="mdRepresentation.allMetadata(['person.jobTitle']).length > 0 ? descTemplate : null"> diff --git a/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.spec.ts b/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.spec.ts index 895cf52223..b9ebf19b67 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/metadata-representations/person/person-item-metadata-list-element.component.spec.ts @@ -36,7 +36,7 @@ describe('PersonItemMetadataListElementComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(PersonItemMetadataListElementComponent); comp = fixture.componentInstance; - comp.metadataRepresentation = mockItemMetadataRepresentation; + comp.mdRepresentation = mockItemMetadataRepresentation; fixture.detectChanges(); }); diff --git a/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.html b/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.html index 6123dc3dc8..e264958738 100644 --- a/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.html +++ b/src/app/entity-groups/research-entities/submission/item-list-elements/org-unit/org-unit-search-result-list-submission-element.component.html @@ -1,6 +1,6 @@
- +
{ {provide: ActivatedRoute, useValue: route}, {provide: Store, useValue: store}, {provide: EPersonDataService, useValue: ePersonDataService}, - {provide: FormBuilder, useValue: new FormBuilder()}, + {provide: UntypedFormBuilder, useValue: new UntypedFormBuilder()}, {provide: NotificationsService, useValue: notificationsService}, ], schemas: [CUSTOM_ELEMENTS_SCHEMA] diff --git a/src/app/header-nav-wrapper/header-navbar-wrapper.component.scss b/src/app/header-nav-wrapper/header-navbar-wrapper.component.scss index b297979fd0..b02b3c378c 100644 --- a/src/app/header-nav-wrapper/header-navbar-wrapper.component.scss +++ b/src/app/header-nav-wrapper/header-navbar-wrapper.component.scss @@ -1,7 +1,4 @@ -@media screen and (max-width: map-get($grid-breakpoints, md)) { - :host.open { - background-color: var(--bs-white); - top: 0; - position: sticky; - } +:host { + position: relative; + z-index: var(--ds-nav-z-index); } diff --git a/src/app/header-nav-wrapper/themed-header-navbar-wrapper.component.scss b/src/app/header-nav-wrapper/themed-header-navbar-wrapper.component.scss deleted file mode 100644 index db392096aa..0000000000 --- a/src/app/header-nav-wrapper/themed-header-navbar-wrapper.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -:host { - z-index: var(--ds-nav-z-index); -} diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss index 546f6a06fa..cca3ed2abb 100644 --- a/src/app/header/header.component.scss +++ b/src/app/header/header.component.scss @@ -11,13 +11,12 @@ line-height: 1.5; } -.navbar ::ng-deep { - a { - color: var(--ds-header-icon-color); +.navbar-toggler { + border: none; + color: var(--ds-header-icon-color); - &:hover, &:focus { - color: var(--ds-header-icon-color-hover); - } + &:hover, &:focus { + color: var(--ds-header-icon-color-hover); } } diff --git a/src/app/home-page/home-page.component.html b/src/app/home-page/home-page.component.html index 87613b56d0..caa86ac290 100644 --- a/src/app/home-page/home-page.component.html +++ b/src/app/home-page/home-page.component.html @@ -4,6 +4,6 @@ - +
diff --git a/src/app/home-page/home-page.module.ts b/src/app/home-page/home-page.module.ts index 3418437d3c..1681abd805 100644 --- a/src/app/home-page/home-page.module.ts +++ b/src/app/home-page/home-page.module.ts @@ -12,11 +12,13 @@ import { ThemedHomePageComponent } from './themed-home-page.component'; import { RecentItemListComponent } from './recent-item-list/recent-item-list.component'; import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; +import { ThemedTopLevelCommunityListComponent } from './top-level-community-list/themed-top-level-community-list.component'; const DECLARATIONS = [ HomePageComponent, ThemedHomePageComponent, TopLevelCommunityListComponent, + ThemedTopLevelCommunityListComponent, ThemedHomeNewsComponent, HomeNewsComponent, RecentItemListComponent diff --git a/src/app/home-page/top-level-community-list/themed-top-level-community-list.component.ts b/src/app/home-page/top-level-community-list/themed-top-level-community-list.component.ts new file mode 100644 index 0000000000..6eb74cc0a9 --- /dev/null +++ b/src/app/home-page/top-level-community-list/themed-top-level-community-list.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { TopLevelCommunityListComponent } from './top-level-community-list.component'; +import { ThemedComponent } from '../../shared/theme-support/themed.component'; + +@Component({ + selector: 'ds-themed-top-level-community-list', + styleUrls: [], + templateUrl: '../../shared/theme-support/themed.component.html', +}) +export class ThemedTopLevelCommunityListComponent extends ThemedComponent { + protected inAndOutputNames: (keyof TopLevelCommunityListComponent & keyof this)[]; + + protected getComponentName(): string { + return 'TopLevelCommunityListComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../themes/${themeName}/app/home-page/top-level-community-list/top-level-community-list.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./top-level-community-list.component`); + } + +} diff --git a/src/app/import-external-page/import-external-page.component.html b/src/app/import-external-page/import-external-page.component.html index 5edccd55cb..5b0c7e3ed6 100644 --- a/src/app/import-external-page/import-external-page.component.html +++ b/src/app/import-external-page/import-external-page.component.html @@ -1 +1 @@ - + diff --git a/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts b/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts index d6bedc46cf..c3d38a2876 100644 --- a/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts +++ b/src/app/info/feedback/feedback-form/feedback-form.component.spec.ts @@ -7,7 +7,7 @@ import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { RouteService } from '../../../core/services/route.service'; import { routeServiceStub } from '../../../shared/testing/route-service.stub'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; import { AuthService } from '../../../core/auth/auth.service'; @@ -41,7 +41,7 @@ describe('FeedbackFormComponent', () => { declarations: [FeedbackFormComponent], providers: [ { provide: RouteService, useValue: routeServiceStub }, - { provide: FormBuilder, useValue: new FormBuilder() }, + { provide: UntypedFormBuilder, useValue: new UntypedFormBuilder() }, { provide: NotificationsService, useValue: notificationService }, { provide: FeedbackDataService, useValue: feedbackDataServiceStub }, { provide: AuthService, useValue: authService }, diff --git a/src/app/info/feedback/feedback-form/feedback-form.component.ts b/src/app/info/feedback/feedback-form/feedback-form.component.ts index 35cdd69cfc..684f974701 100644 --- a/src/app/info/feedback/feedback-form/feedback-form.component.ts +++ b/src/app/info/feedback/feedback-form/feedback-form.component.ts @@ -3,7 +3,7 @@ import { NoContent } from '../../../core/shared/NoContent.model'; import { FeedbackDataService } from '../../../core/feedback/feedback-data.service'; import { Component, Inject, OnInit } from '@angular/core'; import { RouteService } from '../../../core/services/route.service'; -import { FormBuilder, Validators } from '@angular/forms'; +import { UntypedFormBuilder, Validators } from '@angular/forms'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; import { AuthService } from '../../../core/auth/auth.service'; @@ -37,7 +37,7 @@ export class FeedbackFormComponent implements OnInit { constructor( @Inject(NativeWindowService) protected _window: NativeWindowRef, public routeService: RouteService, - private fb: FormBuilder, + private fb: UntypedFormBuilder, protected notificationsService: NotificationsService, protected translate: TranslateService, private feedbackDataService: FeedbackDataService, diff --git a/src/app/item-page/bitstreams/request-a-copy/bitstream-request-a-copy-page.component.ts b/src/app/item-page/bitstreams/request-a-copy/bitstream-request-a-copy-page.component.ts index 59819a4a66..77e1049d87 100644 --- a/src/app/item-page/bitstreams/request-a-copy/bitstream-request-a-copy-page.component.ts +++ b/src/app/item-page/bitstreams/request-a-copy/bitstream-request-a-copy-page.component.ts @@ -11,7 +11,7 @@ import { combineLatest as observableCombineLatest, Observable, of as observableO import { getBitstreamDownloadRoute, getForbiddenRoute } from '../../../app-routing-paths'; import { TranslateService } from '@ngx-translate/core'; import { EPerson } from '../../../core/eperson/models/eperson.model'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { ItemRequestDataService } from '../../../core/data/item-request-data.service'; import { ItemRequest } from '../../../core/shared/item-request.model'; import { Item } from '../../../core/shared/item.model'; @@ -34,7 +34,7 @@ export class BitstreamRequestACopyPageComponent implements OnInit, OnDestroy { canDownload$: Observable; private subs: Subscription[] = []; - requestCopyForm: FormGroup; + requestCopyForm: UntypedFormGroup; item: Item; itemName: string; @@ -49,7 +49,7 @@ export class BitstreamRequestACopyPageComponent implements OnInit, OnDestroy { protected router: Router, private authorizationService: AuthorizationDataService, private auth: AuthService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private itemRequestDataService: ItemRequestDataService, private notificationsService: NotificationsService, private dsoNameService: DSONameService, @@ -59,15 +59,15 @@ export class BitstreamRequestACopyPageComponent implements OnInit, OnDestroy { ngOnInit(): void { this.requestCopyForm = this.formBuilder.group({ - name: new FormControl('', { + name: new UntypedFormControl('', { validators: [Validators.required], }), - email: new FormControl('', { + email: new UntypedFormControl('', { validators: [Validators.required, Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$')] }), - allfiles: new FormControl(''), - message: new FormControl(''), + allfiles: new UntypedFormControl(''), + message: new UntypedFormControl(''), }); diff --git a/src/app/item-page/bitstreams/upload/upload-bitstream.component.html b/src/app/item-page/bitstreams/upload/upload-bitstream.component.html index 7a193451f7..1732da1235 100644 --- a/src/app/item-page/bitstreams/upload/upload-bitstream.component.html +++ b/src/app/item-page/bitstreams/upload/upload-bitstream.component.html @@ -1,12 +1,12 @@
-
+

{{'item.bitstreams.upload.title' | translate}}

{{'item.bitstreams.upload.item' | translate}} - {{item.name}} + {{ dsoNameService.getName(item) }}
@@ -24,7 +24,7 @@ (click)="f.open()" ngDefaultControl> -
diff --git a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts index c06bcad7f2..635cf455b5 100644 --- a/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts +++ b/src/app/item-page/edit-item-page/item-authorizations/item-authorizations.component.ts @@ -101,7 +101,7 @@ export class ItemAuthorizationsComponent implements OnInit, OnDestroy { constructor( private linkService: LinkService, private route: ActivatedRoute, - private nameService: DSONameService + public nameService: DSONameService ) { } diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html index b732e6a950..62170235a3 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.html @@ -3,7 +3,7 @@
- {{'item.edit.bitstreams.bundle.name' | translate:{ name: bundle.name } }} + {{'item.edit.bitstreams.bundle.name' | translate:{ name: dsoNameService.getName(bundle) } }}
diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts index eb05d5e74f..70f4b63217 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.ts @@ -4,6 +4,7 @@ import { Item } from '../../../../core/shared/item.model'; import { ResponsiveColumnSizes } from '../../../../shared/responsive-table-sizes/responsive-column-sizes'; import { ResponsiveTableSizes } from '../../../../shared/responsive-table-sizes/responsive-table-sizes'; import { getItemPageRoute } from '../../../item-page-routing-paths'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-item-edit-bitstream-bundle', @@ -55,7 +56,10 @@ export class ItemEditBitstreamBundleComponent implements OnInit { */ itemPageRoute: string; - constructor(private viewContainerRef: ViewContainerRef) { + constructor( + protected viewContainerRef: ViewContainerRef, + public dsoNameService: DSONameService, + ) { } ngOnInit(): void { diff --git a/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html b/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html index 507e35e630..c522b8a9c5 100644 --- a/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html +++ b/src/app/item-page/edit-item-page/item-collection-mapper/item-collection-mapper.component.html @@ -1,4 +1,4 @@ -
+

{{'item.edit.item-mapper.head' | translate}}

diff --git a/src/app/item-page/edit-item-page/item-curate/item-curate.component.html b/src/app/item-page/edit-item-page/item-curate/item-curate.component.html new file mode 100644 index 0000000000..7c7ed41bd9 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-curate/item-curate.component.html @@ -0,0 +1,7 @@ +
+

{{'item.edit.curate.title' |translate:{item: (itemName$ |async)} }}

+ +
diff --git a/src/app/item-page/edit-item-page/item-curate/item-curate.component.spec.ts b/src/app/item-page/edit-item-page/item-curate/item-curate.component.spec.ts new file mode 100644 index 0000000000..c104b4400b --- /dev/null +++ b/src/app/item-page/edit-item-page/item-curate/item-curate.component.spec.ts @@ -0,0 +1,75 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core'; +import { ItemCurateComponent } from './item-curate.component'; +import { of as observableOf } from 'rxjs'; +import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; +import { ActivatedRoute } from '@angular/router'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { Item } from '../../../core/shared/item.model'; + +describe('ItemCurateComponent', () => { + let comp: ItemCurateComponent; + let fixture: ComponentFixture; + let debugEl: DebugElement; + + let routeStub; + let dsoNameService; + + const item = Object.assign(new Item(), { + handle: '123456789/1', + metadata: {'dc.title': ['Item Name']} + }); + + beforeEach(waitForAsync(() => { + routeStub = { + parent: { + data: observableOf({ + dso: createSuccessfulRemoteDataObject(item) + }) + } + }; + + dsoNameService = jasmine.createSpyObj('dsoNameService', { + getName: 'Item Name' + }); + + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [ItemCurateComponent], + providers: [ + {provide: ActivatedRoute, useValue: routeStub}, + {provide: DSONameService, useValue: dsoNameService} + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemCurateComponent); + comp = fixture.componentInstance; + debugEl = fixture.debugElement; + + fixture.detectChanges(); + }); + describe('init', () => { + it('should initialise the comp', () => { + expect(comp).toBeDefined(); + expect(debugEl.nativeElement.innerHTML).toContain('ds-curation-form'); + }); + + it('should contain the item information provided in the route', (done) => { + comp.dsoRD$.subscribe((value) => { + expect(value.payload.handle).toEqual('123456789/1'); + done(); + }); + }); + + it('should contain the item name', (done) => { + comp.itemName$.subscribe((value) => { + expect(value).toEqual('Item Name'); + done(); + }); + }); + }); +}); diff --git a/src/app/item-page/edit-item-page/item-curate/item-curate.component.ts b/src/app/item-page/edit-item-page/item-curate/item-curate.component.ts new file mode 100644 index 0000000000..fa1e0287fa --- /dev/null +++ b/src/app/item-page/edit-item-page/item-curate/item-curate.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit } from '@angular/core'; +import { filter, map, take } from 'rxjs/operators'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Observable } from 'rxjs'; +import { ActivatedRoute } from '@angular/router'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { hasValue } from '../../../shared/empty.util'; +import { Item } from '../../../core/shared/item.model'; + +/** + * Component for managing a collection's curation tasks + */ +@Component({ + selector: 'ds-item-curate', + templateUrl: './item-curate.component.html', +}) +export class ItemCurateComponent implements OnInit { + dsoRD$: Observable>; + itemName$: Observable; + + constructor( + private route: ActivatedRoute, + private dsoNameService: DSONameService, + ) {} + + ngOnInit(): void { + this.dsoRD$ = this.route.parent.data.pipe( + take(1), + map((data) => data.dso), + ); + + this.itemName$ = this.dsoRD$.pipe( + filter((rd: RemoteData) => hasValue(rd)), + map((rd: RemoteData) => { + return this.dsoNameService.getName(rd.payload); + }) + ); + } +} diff --git a/src/app/item-page/edit-item-page/item-move/item-move.component.html b/src/app/item-page/edit-item-page/item-move/item-move.component.html index 8cb8d1c1c4..589aac655f 100644 --- a/src/app/item-page/edit-item-page/item-move/item-move.component.html +++ b/src/app/item-page/edit-item-page/item-move/item-move.component.html @@ -20,9 +20,10 @@

+

{{'item.edit.move.inheritpolicies.description' | translate}} diff --git a/src/app/item-page/edit-item-page/item-move/item-move.component.ts b/src/app/item-page/edit-item-page/item-move/item-move.component.ts index 4f53fa7df1..0f9df588a1 100644 --- a/src/app/item-page/edit-item-page/item-move/item-move.component.ts +++ b/src/app/item-page/edit-item-page/item-move/item-move.component.ts @@ -16,6 +16,7 @@ import { SearchService } from '../../../core/shared/search/search.service'; import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths'; import { followLink } from '../../../shared/utils/follow-link-config.model'; import { RequestService } from '../../../core/data/request.service'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-item-move', @@ -57,6 +58,7 @@ export class ItemMoveComponent implements OnInit { private searchService: SearchService, private translateService: TranslateService, private requestService: RequestService, + protected dsoNameService: DSONameService, ) {} ngOnInit(): void { @@ -88,7 +90,7 @@ export class ItemMoveComponent implements OnInit { */ selectDso(data: any): void { this.selectedCollection = data; - this.selectedCollectionName = data.name; + this.selectedCollectionName = this.dsoNameService.getName(data); this.canSubmit = true; } diff --git a/src/app/item-page/edit-item-page/item-status/item-status.component.ts b/src/app/item-page/edit-item-page/item-status/item-status.component.ts index c2db518415..ce84786304 100644 --- a/src/app/item-page/edit-item-page/item-status/item-status.component.ts +++ b/src/app/item-page/edit-item-page/item-status/item-status.component.ts @@ -9,9 +9,9 @@ import { RemoteData } from '../../../core/data/remote-data'; import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; -import { hasValue } from '../../../shared/empty.util'; +import { hasValue, isNotEmpty } from '../../../shared/empty.util'; import { - getAllSucceededRemoteDataPayload, getFirstSucceededRemoteData, getRemoteDataPayload, + getAllSucceededRemoteDataPayload, getFirstCompletedRemoteData, getFirstSucceededRemoteData, getRemoteDataPayload, } from '../../../core/shared/operators'; import { IdentifierDataService } from '../../../core/data/identifier-data.service'; import { Identifier } from '../../../shared/object-list/identifier-data/identifier.model'; @@ -105,12 +105,13 @@ export class ItemStatusComponent implements OnInit { // Observable for configuration determining whether the Register DOI feature is enabled let registerConfigEnabled$: Observable = this.configurationService.findByPropertyName('identifiers.item-status.register-doi').pipe( - getFirstSucceededRemoteData(), - getRemoteDataPayload(), - map((enabled: ConfigurationProperty) => { - if (enabled !== undefined && enabled.values) { - return true; + getFirstCompletedRemoteData(), + map((rd: RemoteData) => { + // If the config property is exposed via rest and has a value set, return it + if (rd.hasSucceeded && hasValue(rd.payload) && isNotEmpty(rd.payload.values)) { + return rd.payload.values[0] === 'true'; } + // Otherwise, return false return false; }) ); diff --git a/src/app/item-page/edit-item-page/item-status/themed-item-status.component.ts b/src/app/item-page/edit-item-page/item-status/themed-item-status.component.ts new file mode 100644 index 0000000000..eac5a53702 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-status/themed-item-status.component.ts @@ -0,0 +1,23 @@ +import { Component } from '@angular/core'; +import { ThemedComponent } from '../../../shared/theme-support/themed.component'; +import { ItemStatusComponent } from './item-status.component'; + +@Component({ + selector: 'ds-themed-item-status', + styleUrls: [], + templateUrl: '../../../shared/theme-support/themed.component.html', +}) +export class ThemedItemStatusComponent extends ThemedComponent { + protected getComponentName(): string { + return 'ItemStatusComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../../themes/${themeName}/app/item-page/edit-item-page/item-status/item-status.component`); + } + + protected importUnthemedComponent(): Promise { + return import('./item-status.component'); + } + +} diff --git a/src/app/item-page/field-components/collections/collections.component.html b/src/app/item-page/field-components/collections/collections.component.html index ac68ae1c31..27ebb41d61 100644 --- a/src/app/item-page/field-components/collections/collections.component.html +++ b/src/app/item-page/field-components/collections/collections.component.html @@ -1,7 +1,7 @@

diff --git a/src/app/item-page/field-components/collections/collections.component.spec.ts b/src/app/item-page/field-components/collections/collections.component.spec.ts index c293109ba6..8ea5219308 100644 --- a/src/app/item-page/field-components/collections/collections.component.spec.ts +++ b/src/app/item-page/field-components/collections/collections.component.spec.ts @@ -12,6 +12,8 @@ import { CollectionsComponent } from './collections.component'; import { buildPaginatedList, PaginatedList } from '../../../core/data/paginated-list.model'; import { PageInfo } from '../../../core/shared/page-info.model'; import { FindListOptions } from '../../../core/data/find-list-options.model'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../shared/mocks/dso-name.service.mock'; const createMockCollection = (id: string) => Object.assign(new Collection(), { id: id, @@ -46,6 +48,7 @@ describe('CollectionsComponent', () => { imports: [TranslateModule.forRoot()], declarations: [ CollectionsComponent ], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: RemoteDataBuildService, useValue: getMockRemoteDataBuildService()}, { provide: CollectionDataService, useValue: collectionDataService }, ], diff --git a/src/app/item-page/field-components/collections/collections.component.ts b/src/app/item-page/field-components/collections/collections.component.ts index 6c7053b25d..78d7c985a3 100644 --- a/src/app/item-page/field-components/collections/collections.component.ts +++ b/src/app/item-page/field-components/collections/collections.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, ChangeDetectorRef } from '@angular/core'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import {map, scan, startWith, switchMap, tap, withLatestFrom} from 'rxjs/operators'; import { CollectionDataService } from '../../../core/data/collection-data.service'; @@ -14,6 +14,7 @@ import { getPaginatedListPayload, } from '../../../core/shared/operators'; import { FindListOptions } from '../../../core/data/find-list-options.model'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; /** * This component renders the parent collections section of the item @@ -65,8 +66,11 @@ export class CollectionsComponent implements OnInit { */ collections$: Observable; - constructor(private cds: CollectionDataService) { - + constructor( + private cds: CollectionDataService, + public dsoNameService: DSONameService, + protected cdr: ChangeDetectorRef, + ) { } ngOnInit(): void { @@ -114,6 +118,7 @@ export class CollectionsComponent implements OnInit { return [owningCollection, ...mappedCollections].filter(collection => hasValue(collection)); }), ); + this.cdr.detectChanges(); } handleLoadMore() { diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.html b/src/app/item-page/full/field-components/file-section/full-file-section.component.html index f474c666f2..8b8603011d 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.html +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.html @@ -13,12 +13,12 @@
- +
{{"item.page.filesection.name" | translate}}
-
{{file.name}}
+
{{ dsoNameService.getName(file) }}
{{"item.page.filesection.size" | translate}}
{{(file.sizeBytes) | dsFileSize }}
@@ -27,9 +27,10 @@
{{"item.page.filesection.format" | translate}}
{{(file.format | async)?.payload?.description}}
- +
{{"item.page.filesection.description" | translate}}
{{file.firstMetadataValue("dc.description")}}
+
@@ -55,12 +56,12 @@
- +
{{"item.page.filesection.name" | translate}}
-
{{file.name}}
+
{{ dsoNameService.getName(file) }}
{{"item.page.filesection.size" | translate}}
{{(file.sizeBytes) | dsFileSize }}
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 3be0d58c81..d34edeb025 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, Inject, Input, OnInit } from '@angular/core'; +import { Component, Inject, Input, OnDestroy, 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 { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; /** @@ -26,7 +27,7 @@ import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; styleUrls: ['./full-file-section.component.scss'], templateUrl: './full-file-section.component.html' }) -export class FullFileSectionComponent extends FileSectionComponent implements OnInit { +export class FullFileSectionComponent extends FileSectionComponent implements OnDestroy, OnInit { @Input() item: Item; @@ -52,9 +53,10 @@ export class FullFileSectionComponent extends FileSectionComponent implements On protected notificationsService: NotificationsService, protected translateService: TranslateService, protected paginationService: PaginationService, + public dsoNameService: DSONameService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { - super(bitstreamDataService, notificationsService, translateService, appConfig); + super(bitstreamDataService, notificationsService, translateService, dsoNameService, appConfig); } ngOnInit(): void { diff --git a/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.html b/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.html index 73f27e4d81..3217680815 100644 --- a/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.html +++ b/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.html @@ -47,7 +47,7 @@ class="list-element" (click)="selectedMedia(indexOfelement)" > - {{ item.bitstream.name }} + {{ dsoNameService.getName(item.bitstream) }}
diff --git a/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.ts b/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.ts index 8b8b4d1531..52cd3cac34 100644 --- a/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.ts +++ b/src/app/item-page/media-viewer/media-viewer-video/media-viewer-video.component.ts @@ -1,5 +1,6 @@ import { Component, Input } from '@angular/core'; import { MediaViewerItem } from '../../../core/shared/media-viewer-item.model'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; import { languageHelper } from './language-helper'; import { CaptionInfo } from './caption-info'; import { Bitstream } from 'src/app/core/shared/bitstream.model'; @@ -26,6 +27,11 @@ export class MediaViewerVideoComponent { audio: './assets/images/replacement_audio.svg', }; + constructor( + public dsoNameService: DSONameService, + ) { + } + /** * This method check if there is caption file for the media * The caption file name is the media name plus "-" following two letter diff --git a/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.spec.ts b/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.spec.ts index cd466ae4a4..f2fa9d2440 100644 --- a/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.spec.ts +++ b/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.spec.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { By } from '@angular/platform-browser'; @@ -26,7 +26,7 @@ describe('OrcidSyncSettingsComponent test suite', () => { let scheduler: TestScheduler; let researcherProfileService: jasmine.SpyObj; let notificationsService; - let formGroup: FormGroup; + let formGroup: UntypedFormGroup; const mockResearcherProfile: ResearcherProfile = Object.assign(new ResearcherProfile(), { id: 'test-id', @@ -186,12 +186,12 @@ describe('OrcidSyncSettingsComponent test suite', () => { beforeEach(() => { scheduler = getTestScheduler(); notificationsService = (comp as any).notificationsService; - formGroup = new FormGroup({ - syncMode: new FormControl('MANUAL'), - syncFundings: new FormControl('ALL'), - syncPublications: new FormControl('ALL'), - syncProfile_BIOGRAPHICAL: new FormControl(true), - syncProfile_IDENTIFIERS: new FormControl(true), + formGroup = new UntypedFormGroup({ + syncMode: new UntypedFormControl('MANUAL'), + syncFundings: new UntypedFormControl('ALL'), + syncPublications: new UntypedFormControl('ALL'), + syncProfile_BIOGRAPHICAL: new UntypedFormControl(true), + syncProfile_IDENTIFIERS: new UntypedFormControl(true), }); spyOn(comp.settingsUpdated, 'emit'); }); diff --git a/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.ts b/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.ts index 1ed8e95616..1aec416d62 100644 --- a/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.ts +++ b/src/app/item-page/orcid-page/orcid-sync-settings/orcid-sync-settings.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { Operation } from 'fast-json-patch'; @@ -127,7 +127,7 @@ export class OrcidSyncSettingsComponent implements OnInit { * * @param form The form group */ - onSubmit(form: FormGroup): void { + onSubmit(form: UntypedFormGroup): void { const operations: Operation[] = []; this.fillOperationsFor(operations, '/orcid/mode', form.value.syncMode); this.fillOperationsFor(operations, '/orcid/publications', form.value.syncPublications); diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.html b/src/app/item-page/simple/field-components/file-section/file-section.component.html index 8e4f4dfcb0..cd708510e8 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.html +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.html @@ -2,16 +2,16 @@
- {{file?.name}} - ({{(file?.sizeBytes) | dsFileSize }}) + {{ dsoNameService.getName(file) }} + ({{(file?.sizeBytes) | dsFileSize }})
- {{'item.page.bitstreams.view-more' | translate}} +
- {{'item.page.bitstreams.collapse' | translate}} +
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 08e792fc8b..3c41731c5f 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 @@ -11,6 +11,7 @@ import { NotificationsService } from '../../../../shared/notifications/notificat import { TranslateService } from '@ngx-translate/core'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; /** * This component renders the file section of the item @@ -42,6 +43,7 @@ export class FileSectionComponent implements OnInit { protected bitstreamDataService: BitstreamDataService, protected notificationsService: NotificationsService, protected translateService: TranslateService, + public dsoNameService: DSONameService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { this.pageSize = this.appConfig.item.bitstream.pageSize; diff --git a/src/app/item-page/simple/item-types/publication/publication.component.html b/src/app/item-page/simple/item-types/publication/publication.component.html index dcf243c63b..3749f63964 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.html +++ b/src/app/item-page/simple/item-types/publication/publication.component.html @@ -17,7 +17,7 @@
- +
diff --git a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html index ff98057334..904b7e039c 100644 --- a/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html +++ b/src/app/item-page/simple/item-types/untyped-item/untyped-item.component.html @@ -18,7 +18,7 @@
- +
diff --git a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html index 65660eaa34..efbe9206d1 100644 --- a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html +++ b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html @@ -7,12 +7,12 @@
- {{'item.page.related-items.view-more' | - translate:{ amount: (total - (objects.length * incrementBy) < incrementBy) ? total - (objects.length * incrementBy) : incrementBy } }} +
- {{'item.page.related-items.view-less' | - translate:{ amount: representations?.length } }} +
diff --git a/src/app/item-page/simple/related-items/related-items.component.html b/src/app/item-page/simple/related-items/related-items.component.html index 0d1e14941d..bee1f345fd 100644 --- a/src/app/item-page/simple/related-items/related-items.component.html +++ b/src/app/item-page/simple/related-items/related-items.component.html @@ -7,12 +7,12 @@
- {{'item.page.related-items.view-more' | - translate:{ amount: (itemsRD?.payload?.totalElements - (incrementBy * objects.length) < incrementBy) ? itemsRD?.payload?.totalElements - (incrementBy * objects.length) : incrementBy } }} +
- {{'item.page.related-items.view-less' | - translate:{ amount: itemsRD?.payload?.page?.length } }} +
diff --git a/src/app/item-page/versions/item-versions.component.spec.ts b/src/app/item-page/versions/item-versions.component.spec.ts index 999176d996..e46f1585d7 100644 --- a/src/app/item-page/versions/item-versions.component.spec.ts +++ b/src/app/item-page/versions/item-versions.component.spec.ts @@ -18,7 +18,7 @@ import { PaginationServiceStub } from '../../shared/testing/pagination-service.s import { AuthService } from '../../core/auth/auth.service'; import { VersionDataService } from '../../core/data/version-data.service'; import { ItemDataService } from '../../core/data/item-data.service'; -import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; @@ -140,7 +140,7 @@ describe('ItemVersionsComponent', () => { imports: [TranslateModule.forRoot(), CommonModule, FormsModule, ReactiveFormsModule, BrowserModule, ItemSharedModule], providers: [ {provide: PaginationService, useValue: new PaginationServiceStub()}, - {provide: FormBuilder, useValue: new FormBuilder()}, + {provide: UntypedFormBuilder, useValue: new UntypedFormBuilder()}, {provide: NotificationsService, useValue: new NotificationsServiceStub()}, {provide: AuthService, useValue: authenticationServiceSpy}, {provide: AuthorizationDataService, useValue: authorizationServiceSpy}, diff --git a/src/app/item-page/versions/item-versions.component.ts b/src/app/item-page/versions/item-versions.component.ts index e0fc623f49..700a35552c 100644 --- a/src/app/item-page/versions/item-versions.component.ts +++ b/src/app/item-page/versions/item-versions.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Item } from '../../core/shared/item.model'; import { Version } from '../../core/shared/version.model'; import { RemoteData } from '../../core/data/remote-data'; @@ -32,7 +32,7 @@ import { getItemPageRoute, getItemVersionRoute } from '../item-page-routing-paths'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ItemVersionsSummaryModalComponent } from './item-versions-summary-modal/item-versions-summary-modal.component'; import { NotificationsService } from '../../shared/notifications/notifications.service'; @@ -58,7 +58,7 @@ import { ConfigurationDataService } from '../../core/data/configuration-data.ser /** * Component listing all available versions of the history the provided item is a part of */ -export class ItemVersionsComponent implements OnInit { +export class ItemVersionsComponent implements OnDestroy, OnInit { /** * The item to display a version history for @@ -171,7 +171,7 @@ export class ItemVersionsComponent implements OnInit { private versionService: VersionDataService, private itemService: ItemDataService, private paginationService: PaginationService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private modalService: NgbModal, private notificationsService: NotificationsService, private translateService: TranslateService, diff --git a/src/app/my-dspace-page/my-dspace-search.module.ts b/src/app/my-dspace-page/my-dspace-search.module.ts index f3775214d5..71d1343a30 100644 --- a/src/app/my-dspace-page/my-dspace-search.module.ts +++ b/src/app/my-dspace-page/my-dspace-search.module.ts @@ -23,7 +23,6 @@ import { ItemDetailPreviewComponent } from '../shared/object-detail/my-dspace-re import { ItemDetailPreviewFieldComponent } from '../shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component'; import { ItemListPreviewComponent } from '../shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component'; import { ThemedItemListPreviewComponent } from '../shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; -import { MyDSpaceItemStatusComponent } from '../shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component'; import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { MyDSpaceActionsModule } from '../shared/mydspace-actions/mydspace-actions.module'; import { ClaimedDeclinedTaskSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component'; @@ -52,7 +51,6 @@ const DECLARATIONS = [ ItemDetailPreviewFieldComponent, ItemListPreviewComponent, ThemedItemListPreviewComponent, - MyDSpaceItemStatusComponent, ]; @NgModule({ diff --git a/src/app/process-page/form/process-parameters/process-parameters.component.html b/src/app/process-page/form/process-parameters/process-parameters.component.html index d7bbce6779..a937524e88 100644 --- a/src/app/process-page/form/process-parameters/process-parameters.component.html +++ b/src/app/process-page/form/process-parameters/process-parameters.component.html @@ -1,11 +1,11 @@ -
- - +
+ +
diff --git a/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts b/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts index 91bc61e02f..b44479c14a 100644 --- a/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts +++ b/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts @@ -14,14 +14,14 @@ import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock describe('ProcessParametersComponent', () => { let component: ProcessParametersComponent; let fixture: ComponentFixture; - let parameterValues; - let script; + let mockParameterValues: ProcessParameter[]; + let mockScript: Script; - function init() { + function initParametersAndScriptMockValues() { const param1 = new ScriptParameter(); const param2 = new ScriptParameter(); - script = Object.assign(new Script(), { parameters: [param1, param2] }); - parameterValues = [ + mockScript = Object.assign(new Script(), { parameters: [param1, param2] }); + mockParameterValues = [ Object.assign(new ProcessParameter(), { name: '-a', value: 'bla' }), Object.assign(new ProcessParameter(), { name: '-b', value: '123' }), Object.assign(new ProcessParameter(), { name: '-c', value: 'value' }), @@ -29,7 +29,6 @@ describe('ProcessParametersComponent', () => { } beforeEach(waitForAsync(() => { - init(); TestBed.configureTestingModule({ imports: [ FormsModule, @@ -48,17 +47,34 @@ describe('ProcessParametersComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ProcessParametersComponent); component = fixture.componentInstance; - component.script = script; - component.parameterValues = parameterValues; - fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); - it('should render a ParameterSelectComponent for each parameter value of the component', () => { - const selectComponents = fixture.debugElement.queryAll(By.directive(ParameterSelectComponent)); - expect(selectComponents.length).toBe(parameterValues.length); + describe('when parameter values and script are initialized', () => { + + beforeEach(() => { + initParametersAndScriptMockValues(); + component.parameterValues = mockParameterValues; + component.script = mockScript; + + fixture.detectChanges(); + }); + + it(`should render a ${ParameterSelectComponent.name} for each parameter value`, () => { + const selectComponents = fixture.debugElement.queryAll(By.directive(ParameterSelectComponent)); + expect(selectComponents.length).toBe(mockParameterValues.length); + }); + + it('should not render a selector box if the parameter array is empty',() => { + fixture.componentInstance.script.parameters = []; + + fixture.detectChanges(); + + const formGroupComponent = fixture.debugElement.query(By.css('[data-testID=parameters-select-container]')); + expect(formGroupComponent).toBeFalsy(); + }); }); }); diff --git a/src/app/process-page/overview/process-overview.component.ts b/src/app/process-page/overview/process-overview.component.ts index 1ca29693cb..7fa3b12dac 100644 --- a/src/app/process-page/overview/process-overview.component.ts +++ b/src/app/process-page/overview/process-overview.component.ts @@ -14,6 +14,7 @@ import { FindListOptions } from '../../core/data/find-list-options.model'; import { ProcessBulkDeleteService } from './process-bulk-delete.service'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { hasValue } from '../../shared/empty.util'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-process-overview', @@ -59,6 +60,7 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy { protected ePersonService: EPersonDataService, protected modalService: NgbModal, public processBulkDeleteService: ProcessBulkDeleteService, + protected dsoNameService: DSONameService, ) { } @@ -83,7 +85,7 @@ export class ProcessOverviewComponent implements OnInit, OnDestroy { getEpersonName(id: string): Observable { return this.ePersonService.findById(id).pipe( getFirstSucceededRemoteDataPayload(), - map((eperson: EPerson) => eperson.name) + map((eperson: EPerson) => this.dsoNameService.getName(eperson)), ); } diff --git a/src/app/process-page/process-page-shared.module.ts b/src/app/process-page/process-page-shared.module.ts new file mode 100644 index 0000000000..e666283e03 --- /dev/null +++ b/src/app/process-page/process-page-shared.module.ts @@ -0,0 +1,48 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { NewProcessComponent } from './new/new-process.component'; +import { ScriptsSelectComponent } from './form/scripts-select/scripts-select.component'; +import { ScriptHelpComponent } from './form/script-help/script-help.component'; +import { ParameterSelectComponent } from './form/process-parameters/parameter-select/parameter-select.component'; +import { ProcessParametersComponent } from './form/process-parameters/process-parameters.component'; +import { StringValueInputComponent } from './form/process-parameters/parameter-value-input/string-value-input/string-value-input.component'; +import { ParameterValueInputComponent } from './form/process-parameters/parameter-value-input/parameter-value-input.component'; +import { FileValueInputComponent } from './form/process-parameters/parameter-value-input/file-value-input/file-value-input.component'; +import { BooleanValueInputComponent } from './form/process-parameters/parameter-value-input/boolean-value-input/boolean-value-input.component'; +import { DateValueInputComponent } from './form/process-parameters/parameter-value-input/date-value-input/date-value-input.component'; +import { ProcessOverviewComponent } from './overview/process-overview.component'; +import { ProcessDetailComponent } from './detail/process-detail.component'; +import { ProcessDetailFieldComponent } from './detail/process-detail-field/process-detail-field.component'; +import { ProcessBreadcrumbsService } from './process-breadcrumbs.service'; +import { ProcessBreadcrumbResolver } from './process-breadcrumb.resolver'; +import { ProcessFormComponent } from './form/process-form.component'; + +@NgModule({ + imports: [ + SharedModule, + ], + declarations: [ + NewProcessComponent, + ScriptsSelectComponent, + ScriptHelpComponent, + ParameterSelectComponent, + ProcessParametersComponent, + StringValueInputComponent, + ParameterValueInputComponent, + FileValueInputComponent, + BooleanValueInputComponent, + DateValueInputComponent, + ProcessOverviewComponent, + ProcessDetailComponent, + ProcessDetailFieldComponent, + ProcessFormComponent + ], + providers: [ + ProcessBreadcrumbResolver, + ProcessBreadcrumbsService + ] +}) + +export class ProcessPageSharedModule { + +} diff --git a/src/app/process-page/process-page.module.ts b/src/app/process-page/process-page.module.ts index ebe03e4537..2587813998 100644 --- a/src/app/process-page/process-page.module.ts +++ b/src/app/process-page/process-page.module.ts @@ -1,47 +1,17 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { ProcessPageRoutingModule } from './process-page-routing.module'; -import { NewProcessComponent } from './new/new-process.component'; -import { ScriptsSelectComponent } from './form/scripts-select/scripts-select.component'; -import { ScriptHelpComponent } from './form/script-help/script-help.component'; -import { ParameterSelectComponent } from './form/process-parameters/parameter-select/parameter-select.component'; -import { ProcessParametersComponent } from './form/process-parameters/process-parameters.component'; -import { StringValueInputComponent } from './form/process-parameters/parameter-value-input/string-value-input/string-value-input.component'; -import { ParameterValueInputComponent } from './form/process-parameters/parameter-value-input/parameter-value-input.component'; -import { FileValueInputComponent } from './form/process-parameters/parameter-value-input/file-value-input/file-value-input.component'; -import { BooleanValueInputComponent } from './form/process-parameters/parameter-value-input/boolean-value-input/boolean-value-input.component'; -import { DateValueInputComponent } from './form/process-parameters/parameter-value-input/date-value-input/date-value-input.component'; -import { ProcessOverviewComponent } from './overview/process-overview.component'; -import { ProcessDetailComponent } from './detail/process-detail.component'; -import { ProcessDetailFieldComponent } from './detail/process-detail-field/process-detail-field.component'; -import { ProcessBreadcrumbsService } from './process-breadcrumbs.service'; -import { ProcessBreadcrumbResolver } from './process-breadcrumb.resolver'; -import { ProcessFormComponent } from './form/process-form.component'; +import { ProcessPageSharedModule } from './process-page-shared.module'; @NgModule({ imports: [ ProcessPageRoutingModule, SharedModule, + ProcessPageSharedModule, ], declarations: [ - NewProcessComponent, - ScriptsSelectComponent, - ScriptHelpComponent, - ParameterSelectComponent, - ProcessParametersComponent, - StringValueInputComponent, - ParameterValueInputComponent, - FileValueInputComponent, - BooleanValueInputComponent, - DateValueInputComponent, - ProcessOverviewComponent, - ProcessDetailComponent, - ProcessDetailFieldComponent, - ProcessFormComponent ], providers: [ - ProcessBreadcrumbResolver, - ProcessBreadcrumbsService ] }) diff --git a/src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.html b/src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.html index eec9f437f1..a5bbb38d02 100644 --- a/src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.html +++ b/src/app/profile-page/profile-claim-item-modal/profile-claim-item-modal.component.html @@ -12,7 +12,7 @@
diff --git a/src/app/search-navbar/search-navbar.component.scss b/src/app/search-navbar/search-navbar.component.scss index d5f3d8d615..cf46c25d91 100644 --- a/src/app/search-navbar/search-navbar.component.scss +++ b/src/app/search-navbar/search-navbar.component.scss @@ -1,13 +1,14 @@ input[type="text"] { margin-top: calc(-0.5 * var(--bs-font-size-base)); background-color: #fff !important; + border-color: var(--ds-header-icon-color); &.collapsed { opacity: 0; } } -a.submit-icon { +.submit-icon { cursor: pointer; position: sticky; top: 0; diff --git a/src/app/search-navbar/search-navbar.component.ts b/src/app/search-navbar/search-navbar.component.ts index ccdaa27861..98e64f6e10 100644 --- a/src/app/search-navbar/search-navbar.component.ts +++ b/src/app/search-navbar/search-navbar.component.ts @@ -1,5 +1,5 @@ import { Component, ElementRef, ViewChild } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Router } from '@angular/router'; import { SearchService } from '../core/shared/search/search.service'; import { expandSearchInput } from '../shared/animations/slide'; @@ -24,7 +24,7 @@ export class SearchNavbarComponent { // Search input field @ViewChild('searchInput') searchField: ElementRef; - constructor(private formBuilder: FormBuilder, private router: Router, private searchService: SearchService) { + constructor(private formBuilder: UntypedFormBuilder, private router: Router, private searchService: SearchService) { this.searchForm = this.formBuilder.group(({ query: '', })); diff --git a/src/app/search-page/search-page.module.ts b/src/app/search-page/search-page.module.ts index 19fd9bd309..cc76d0427d 100644 --- a/src/app/search-page/search-page.module.ts +++ b/src/app/search-page/search-page.module.ts @@ -4,7 +4,6 @@ import { CoreModule } from '../core/core.module'; import { SharedModule } from '../shared/shared.module'; import { SidebarService } from '../shared/sidebar/sidebar.service'; import { ConfigurationSearchPageGuard } from './configuration-search-page.guard'; -import { SearchTrackerComponent } from './search-tracker.component'; import { StatisticsModule } from '../statistics/statistics.module'; import { SearchPageComponent } from './search-page.component'; import { SearchFilterService } from '../core/shared/search/search-filter.service'; @@ -16,7 +15,6 @@ import { SearchModule } from '../shared/search/search.module'; const components = [ SearchPageComponent, - SearchTrackerComponent, ThemedSearchPageComponent ]; diff --git a/src/app/search-page/search-tracker.component.html b/src/app/search-page/search-tracker.component.html deleted file mode 100644 index c0c0ffe181..0000000000 --- a/src/app/search-page/search-tracker.component.html +++ /dev/null @@ -1 +0,0 @@ -  diff --git a/src/app/search-page/search-tracker.component.scss b/src/app/search-page/search-tracker.component.scss deleted file mode 100644 index c76cafbe44..0000000000 --- a/src/app/search-page/search-tracker.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -:host { - display: none -} diff --git a/src/app/search-page/search-tracker.component.ts b/src/app/search-page/search-tracker.component.ts deleted file mode 100644 index f766e6f669..0000000000 --- a/src/app/search-page/search-tracker.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Component, Inject, OnInit } from '@angular/core'; -import { Angulartics2 } from 'angulartics2'; -import { map, switchMap } from 'rxjs/operators'; -import { SearchComponent } from '../shared/search/search.component'; -import { SidebarService } from '../shared/sidebar/sidebar.service'; -import { HostWindowService } from '../shared/host-window.service'; -import { SEARCH_CONFIG_SERVICE } from '../my-dspace-page/my-dspace-page.component'; -import { RouteService } from '../core/services/route.service'; -import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; -import { SearchService } from '../core/shared/search/search.service'; -import { PaginatedSearchOptions } from '../shared/search/models/paginated-search-options.model'; -import { SearchObjects } from '../shared/search/models/search-objects.model'; -import { Router } from '@angular/router'; -import { RemoteData } from '../core/data/remote-data'; -import { DSpaceObject } from '../core/shared/dspace-object.model'; -import { getFirstSucceededRemoteData } from '../core/shared/operators'; - -/** - * This component triggers a page view statistic - */ -@Component({ - selector: 'ds-search-tracker', - styleUrls: ['./search-tracker.component.scss'], - templateUrl: './search-tracker.component.html', - providers: [ - { - provide: SEARCH_CONFIG_SERVICE, - useClass: SearchConfigurationService - } - ] -}) -export class SearchTrackerComponent extends SearchComponent implements OnInit { - - constructor( - protected service: SearchService, - protected sidebarService: SidebarService, - protected windowService: HostWindowService, - @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, - protected routeService: RouteService, - public angulartics2: Angulartics2, - protected router: Router - ) { - super(service, sidebarService, windowService, searchConfigService, routeService, router); - } - - ngOnInit(): void { - // super.ngOnInit(); - this.getSearchOptions().pipe( - switchMap((options: PaginatedSearchOptions) => - this.service.searchEntries(options).pipe( - getFirstSucceededRemoteData(), - map((rd: RemoteData>) => ({ - config: options, - searchQueryResponse: rd.payload - })) - )), - ).subscribe(({ config, searchQueryResponse }) => { - const filters: { filter: string, operator: string, value: string, label: string; }[] = []; - const appliedFilters = searchQueryResponse.appliedFilters || []; - for (let i = 0, filtersLength = appliedFilters.length; i < filtersLength; i++) { - const appliedFilter = appliedFilters[i]; - filters.push(appliedFilter); - } - this.angulartics2.eventTrack.next({ - action: 'search', - properties: { - searchOptions: config, - page: { - size: config.pagination.size, // same as searchQueryResponse.page.elementsPerPage - totalElements: searchQueryResponse.pageInfo.totalElements, - totalPages: searchQueryResponse.pageInfo.totalPages, - number: config.pagination.currentPage, // same as searchQueryResponse.page.currentPage - }, - sort: { - by: config.sort.field, - order: config.sort.direction - }, - filters: filters, - }, - }); - }); - } -} diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html index 94cbd4368a..05f502afa1 100644 --- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html +++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html @@ -19,7 +19,7 @@
diff --git a/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts index 59fc95b67f..00efc3ccaa 100644 --- a/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.spec.ts @@ -12,6 +12,8 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { ComcolModule } from '../../../comcol.module'; import { NotificationsService } from '../../../../notifications/notifications.service'; import { NotificationsServiceStub } from '../../../../testing/notifications-service.stub'; +import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../mocks/dso-name.service.mock'; describe('ComcolRoleComponent', () => { @@ -41,6 +43,7 @@ describe('ComcolRoleComponent', () => { NoopAnimationsModule ], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: GroupDataService, useValue: groupService }, { provide: RequestService, useValue: requestService }, { provide: NotificationsService, useClass: NotificationsServiceStub } diff --git a/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts index 3091dd0cf0..5ae22d754e 100644 --- a/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts +++ b/src/app/shared/comcol/comcol-forms/edit-comcol-page/comcol-role/comcol-role.component.ts @@ -14,6 +14,7 @@ import { hasNoValue, hasValue } from '../../../../empty.util'; import { NoContent } from '../../../../../core/shared/NoContent.model'; import { NotificationsService } from '../../../../notifications/notifications.service'; import { TranslateService } from '@ngx-translate/core'; +import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; /** * Component for managing a community or collection role. @@ -76,6 +77,7 @@ export class ComcolRoleComponent implements OnInit { protected groupService: GroupDataService, protected notificationsService: NotificationsService, protected translateService: TranslateService, + public dsoNameService: DSONameService, ) { } diff --git a/src/app/shared/confirmation-modal/confirmation-modal.component.html b/src/app/shared/confirmation-modal/confirmation-modal.component.html index 82c70b662b..02434b1fa1 100644 --- a/src/app/shared/confirmation-modal/confirmation-modal.component.html +++ b/src/app/shared/confirmation-modal/confirmation-modal.component.html @@ -1,18 +1,18 @@
- diff --git a/src/app/shared/confirmation-modal/confirmation-modal.component.ts b/src/app/shared/confirmation-modal/confirmation-modal.component.ts index 4fa4858600..46eb4cedc5 100644 --- a/src/app/shared/confirmation-modal/confirmation-modal.component.ts +++ b/src/app/shared/confirmation-modal/confirmation-modal.component.ts @@ -1,6 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-confirmation-modal', @@ -25,7 +26,10 @@ export class ConfirmationModalComponent { @Output() response = new EventEmitter(); - constructor(protected activeModal: NgbActiveModal) { + constructor( + protected activeModal: NgbActiveModal, + public dsoNameService: DSONameService, + ) { } /** diff --git a/src/app/shared/context-help-wrapper/context-help-wrapper.component.html b/src/app/shared/context-help-wrapper/context-help-wrapper.component.html index b031d0f42d..083b8163ff 100644 --- a/src/app/shared/context-help-wrapper/context-help-wrapper.component.html +++ b/src/app/shared/context-help-wrapper/context-help-wrapper.component.html @@ -2,7 +2,7 @@
- {{elem.text}} + {{elem.text}} {{ elem }} diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts b/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts index fe64c0a41e..503e4c4412 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts @@ -9,7 +9,7 @@ import { QueryList, ViewChildren } from '@angular/core'; -import { FormControl } from '@angular/forms'; +import { UntypedFormControl } from '@angular/forms'; import { BehaviorSubject, @@ -86,7 +86,7 @@ export class DSOSelectorComponent implements OnInit, OnDestroy { /** * Input form control to query the list */ - public input: FormControl = new FormControl(); + public input: UntypedFormControl = new UntypedFormControl(); /** * Default pagination for this feature diff --git a/src/app/shared/eperson-group-list/eperson-search-box/eperson-search-box.component.spec.ts b/src/app/shared/eperson-group-list/eperson-search-box/eperson-search-box.component.spec.ts index 5a9e74055a..e88f08a131 100644 --- a/src/app/shared/eperson-group-list/eperson-search-box/eperson-search-box.component.spec.ts +++ b/src/app/shared/eperson-group-list/eperson-search-box/eperson-search-box.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing'; import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; -import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { TranslateModule } from '@ngx-translate/core'; @@ -13,7 +13,7 @@ describe('EpersonSearchBoxComponent test suite', () => { let compAsAny: any; let fixture: ComponentFixture; let de; - let formBuilder: FormBuilder; + let formBuilder: UntypedFormBuilder; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -27,7 +27,7 @@ describe('EpersonSearchBoxComponent test suite', () => { TestComponent ], providers: [ - FormBuilder, + UntypedFormBuilder, EpersonSearchBoxComponent ], schemas: [ @@ -64,7 +64,7 @@ describe('EpersonSearchBoxComponent test suite', () => { beforeEach(() => { // initTestScheduler(); fixture = TestBed.createComponent(EpersonSearchBoxComponent); - formBuilder = TestBed.inject(FormBuilder); + formBuilder = TestBed.inject(UntypedFormBuilder); comp = fixture.componentInstance; compAsAny = fixture.componentInstance; }); diff --git a/src/app/shared/eperson-group-list/eperson-search-box/eperson-search-box.component.ts b/src/app/shared/eperson-group-list/eperson-search-box/eperson-search-box.component.ts index 2aa4891c03..4689d29a8c 100644 --- a/src/app/shared/eperson-group-list/eperson-search-box/eperson-search-box.component.ts +++ b/src/app/shared/eperson-group-list/eperson-search-box/eperson-search-box.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Output } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Subscription } from 'rxjs'; @@ -33,7 +33,7 @@ export class EpersonSearchBoxComponent { */ @Output() search: EventEmitter = new EventEmitter(); - constructor(private formBuilder: FormBuilder) { + constructor(private formBuilder: UntypedFormBuilder) { this.searchForm = this.formBuilder.group(({ scope: 'metadata', query: '', diff --git a/src/app/shared/eperson-group-list/group-search-box/group-search-box.component.spec.ts b/src/app/shared/eperson-group-list/group-search-box/group-search-box.component.spec.ts index d28a144245..b4c663902d 100644 --- a/src/app/shared/eperson-group-list/group-search-box/group-search-box.component.spec.ts +++ b/src/app/shared/eperson-group-list/group-search-box/group-search-box.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing'; import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; -import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { TranslateModule } from '@ngx-translate/core'; @@ -13,7 +13,7 @@ describe('GroupSearchBoxComponent test suite', () => { let compAsAny: any; let fixture: ComponentFixture; let de; - let formBuilder: FormBuilder; + let formBuilder: UntypedFormBuilder; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -27,7 +27,7 @@ describe('GroupSearchBoxComponent test suite', () => { TestComponent ], providers: [ - FormBuilder, + UntypedFormBuilder, GroupSearchBoxComponent ], schemas: [ @@ -64,7 +64,7 @@ describe('GroupSearchBoxComponent test suite', () => { beforeEach(() => { // initTestScheduler(); fixture = TestBed.createComponent(GroupSearchBoxComponent); - formBuilder = TestBed.inject(FormBuilder); + formBuilder = TestBed.inject(UntypedFormBuilder); comp = fixture.componentInstance; compAsAny = fixture.componentInstance; }); diff --git a/src/app/shared/eperson-group-list/group-search-box/group-search-box.component.ts b/src/app/shared/eperson-group-list/group-search-box/group-search-box.component.ts index 3e45bb0336..154bee2d07 100644 --- a/src/app/shared/eperson-group-list/group-search-box/group-search-box.component.ts +++ b/src/app/shared/eperson-group-list/group-search-box/group-search-box.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Output } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { UntypedFormBuilder } from '@angular/forms'; import { Subscription } from 'rxjs'; @@ -33,7 +33,7 @@ export class GroupSearchBoxComponent { */ @Output() search: EventEmitter = new EventEmitter(); - constructor(private formBuilder: FormBuilder) { + constructor(private formBuilder: UntypedFormBuilder) { this.searchForm = this.formBuilder.group(({ query: '', })); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts index 08966f0f78..b77ee9950c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing'; import { CUSTOM_ELEMENTS_SCHEMA, DebugElement, NgZone, SimpleChange } from '@angular/core'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing'; import { By } from '@angular/platform-browser'; @@ -187,7 +187,7 @@ describe('DsDynamicFormControlContainerComponent test suite', () => { new DynamicQualdropModel({ id: 'combobox', readOnly: false, required: false }) ]; const testModel = formModel[8]; - let formGroup: FormGroup; + let formGroup: UntypedFormGroup; let fixture: ComponentFixture; let component: DsDynamicFormControlContainerComponent; let debugElement: DebugElement; @@ -270,8 +270,8 @@ describe('DsDynamicFormControlContainerComponent test suite', () => { it('should initialize correctly', () => { expect(component.context).toBeNull(); - expect(component.control instanceof FormControl).toBe(true); - expect(component.group instanceof FormGroup).toBe(true); + expect(component.control instanceof UntypedFormControl).toBe(true); + expect(component.group instanceof UntypedFormGroup).toBe(true); expect(component.model instanceof DynamicFormControlModel).toBe(true); expect(component.hasErrorMessaging).toBe(false); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts index 5eee8f932d..ff5a119b6f 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component.ts @@ -17,7 +17,7 @@ import { ViewChild, ViewContainerRef } from '@angular/core'; -import { FormArray, FormGroup } from '@angular/forms'; +import { UntypedFormArray, UntypedFormGroup } from '@angular/forms'; import { DYNAMIC_FORM_CONTROL_TYPE_ARRAY, @@ -199,12 +199,12 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo @Input('templates') inputTemplateList: QueryList; @Input() hasMetadataModel: any; @Input() formId: string; - @Input() formGroup: FormGroup; + @Input() formGroup: UntypedFormGroup; @Input() formModel: DynamicFormControlModel[]; @Input() asBootstrapFormGroup = false; @Input() bindId = true; @Input() context: any | null = null; - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() hostClass: string[]; @Input() hasErrorMessaging = false; @Input() layout = null as DynamicFormLayout; @@ -475,7 +475,7 @@ export class DsDynamicFormControlContainerComponent extends DynamicFormControlCo onRemove(): void { const arrayContext: DynamicFormArrayModel = (this.context as DynamicFormArrayGroupModel).context; const path = this.formBuilderService.getPath(arrayContext); - const formArrayControl = this.group.root.get(path) as FormArray; + const formArrayControl = this.group.root.get(path) as UntypedFormArray; this.formBuilderService.removeFormArrayGroup(this.context.index, formArrayControl, arrayContext); if (this.model.parent.context.groups.length === 0) { this.formBuilderService.addFormArrayGroup(formArrayControl, arrayContext); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form.component.ts index 537a1f0f52..45511d6941 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form.component.ts @@ -8,7 +8,7 @@ import { QueryList, ViewChildren } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicFormComponent, DynamicFormControlContainerComponent, @@ -27,7 +27,7 @@ import { DsDynamicFormControlContainerComponent } from './ds-dynamic-form-contro export class DsDynamicFormComponent extends DynamicFormComponent { @Input() formId: string; - @Input() formGroup: FormGroup; + @Input() formGroup: UntypedFormGroup; @Input() formModel: DynamicFormControlModel[]; @Input() formLayout: DynamicFormLayout; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts index 7f0c7e2e35..12b2409bf2 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.spec.ts @@ -14,7 +14,7 @@ import { } from '../../../mocks/form-models.mock'; import {DsDynamicTypeBindRelationService} from './ds-dynamic-type-bind-relation.service'; import {FormFieldMetadataValueObject} from '../models/form-field-metadata-value.model'; -import {FormControl, ReactiveFormsModule} from '@angular/forms'; +import {UntypedFormControl, ReactiveFormsModule} from '@angular/forms'; import {FormBuilderService} from '../form-builder.service'; import {getMockFormBuilderService} from '../../../mocks/form-builder-service.mock'; import {Injector} from '@angular/core'; @@ -87,7 +87,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => { it('Should receive one subscription to dc.type type binding"', () => { const testModel = mockInputWithTypeBindModel; testModel.typeBindRelations = getTypeBindRelations(['boundType']); - const dcTypeControl = new FormControl(); + const dcTypeControl = new UntypedFormControl(); dcTypeControl.setValue('boundType'); let subscriptions = service.subscribeRelations(testModel, dcTypeControl); expect(subscriptions).toHaveSize(1); @@ -96,7 +96,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => { it('Expect hasMatch to be true (ie. this should be hidden)', () => { const testModel = mockInputWithTypeBindModel; testModel.typeBindRelations = getTypeBindRelations(['boundType']); - const dcTypeControl = new FormControl(); + const dcTypeControl = new UntypedFormControl(); dcTypeControl.setValue('boundType'); testModel.typeBindRelations[0].when[0].value = 'anotherType'; const relation = dynamicFormRelationService.findRelationByMatcher((testModel as any).typeBindRelations, HIDDEN_MATCHER); @@ -111,7 +111,7 @@ describe('DSDynamicTypeBindRelationService test suite', () => { it('Expect hasMatch to be false (ie. this should NOT be hidden)', () => { const testModel = mockInputWithTypeBindModel; testModel.typeBindRelations = getTypeBindRelations(['boundType']); - const dcTypeControl = new FormControl(); + const dcTypeControl = new UntypedFormControl(); dcTypeControl.setValue('boundType'); testModel.typeBindRelations[0].when[0].value = 'boundType'; const relation = dynamicFormRelationService.findRelationByMatcher((testModel as any).typeBindRelations, HIDDEN_MATCHER); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts index 5dd4a6627d..d5e735ed1a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-type-bind-relation.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable, Injector, Optional } from '@angular/core'; -import { FormControl } from '@angular/forms'; +import { UntypedFormControl } from '@angular/forms'; import { Subscription } from 'rxjs'; import { startWith } from 'rxjs/operators'; @@ -172,7 +172,7 @@ export class DsDynamicTypeBindRelationService { * @param model * @param control */ - subscribeRelations(model: DynamicFormControlModel, control: FormControl): Subscription[] { + subscribeRelations(model: DynamicFormControlModel, control: UntypedFormControl): Subscription[] { const relatedModels = this.getRelatedFormModel(model); const subscriptions: Subscription[] = []; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts index 3160bccb41..aa50133e8a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component.ts @@ -1,6 +1,6 @@ /* eslint-disable max-classes-per-file */ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; -import { FormControl } from '@angular/forms'; +import { UntypedFormControl } from '@angular/forms'; import { DynamicFormArrayGroupModel } from '@ng-dynamic-forms/core'; import { Store } from '@ngrx/store'; import { BehaviorSubject, Subscription } from 'rxjs'; @@ -64,7 +64,7 @@ export class ReorderableFormFieldMetadataValue extends Reorderable { constructor( public metadataValue: FormFieldMetadataValueObject, public model: DynamicConcatModel, - public control: FormControl, + public control: UntypedFormControl, public group: DynamicFormArrayGroupModel, oldIndex?: number, newIndex?: number diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts index 01bba74cc8..9d48bdac21 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/array-group/dynamic-form-array.component.ts @@ -1,6 +1,6 @@ import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { Component, EventEmitter, Input, Output, QueryList } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicFormArrayComponent, DynamicFormControlCustomEvent, @@ -26,7 +26,7 @@ export class DsDynamicFormArrayComponent extends DynamicFormArrayComponent { @Input() bindId = true; @Input() formModel: DynamicFormControlModel[]; @Input() formLayout: DynamicFormLayout; - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() layout: DynamicFormControlLayout; @Input() model: DynamicRowArrayModel;// DynamicRow? @Input() templates: QueryList | undefined; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.spec.ts index 9f85ccc013..ceb498fe56 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.spec.ts @@ -1,5 +1,5 @@ import { DynamicFormsCoreModule, DynamicFormService } from '@ng-dynamic-forms/core'; -import { FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormGroup, ReactiveFormsModule } from '@angular/forms'; import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing'; import { DebugElement } from '@angular/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -11,7 +11,7 @@ describe('CustomSwitchComponent', () => { const testModel = new DynamicCustomSwitchModel({ id: 'switch' }); const formModel = [testModel]; - let formGroup: FormGroup; + let formGroup: UntypedFormGroup; let fixture: ComponentFixture; let component: CustomSwitchComponent; let debugElement: DebugElement; @@ -47,7 +47,7 @@ describe('CustomSwitchComponent', () => { it('should initialize correctly', () => { expect(component.bindId).toBe(true); - expect(component.group instanceof FormGroup).toBe(true); + expect(component.group instanceof UntypedFormGroup).toBe(true); expect(component.model instanceof DynamicCustomSwitchModel).toBe(true); expect(component.blur).toBeDefined(); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.ts index 5b3f1e89e4..47780e66f6 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicNGBootstrapCheckboxComponent } from '@ng-dynamic-forms/ui-ng-bootstrap'; import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; @@ -24,7 +24,7 @@ export class CustomSwitchComponent extends DynamicNGBootstrapCheckboxComponent { /** * The formgroup containing this component */ - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; /** * The model used for displaying the switch diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.spec.ts index 0756e48a8d..c0d1c83bf9 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, inject, TestBed, waitForAsync } from '@angular/core/testing'; import { DebugElement } from '@angular/core'; -import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, ReactiveFormsModule } from '@angular/forms'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { By } from '@angular/platform-browser'; import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap'; @@ -11,7 +11,7 @@ describe('DsDatePickerInlineComponent test suite', () => { const testModel = new DynamicDatePickerModel({ id: 'datepicker' }); const formModel = [testModel]; - let formGroup: FormGroup; + let formGroup: UntypedFormGroup; let fixture: ComponentFixture; let component: DsDatePickerInlineComponent; let debugElement: DebugElement; @@ -53,8 +53,8 @@ describe('DsDatePickerInlineComponent test suite', () => { it('should initialize correctly', () => { expect(component.bindId).toBe(true); - expect(component.control instanceof FormControl).toBe(true); - expect(component.group instanceof FormGroup).toBe(true); + expect(component.control instanceof UntypedFormControl).toBe(true); + expect(component.group instanceof UntypedFormGroup).toBe(true); expect(component.model instanceof DynamicDatePickerModel).toBe(true); expect(component.blur).toBeDefined(); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.ts index f23b6c1e7b..2eb6e9291c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker-inline/dynamic-date-picker-inline.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { NgbDatepicker, NgbDatepickerConfig } from '@ng-bootstrap/ng-bootstrap'; import { DynamicDatePickerModel, @@ -16,7 +16,7 @@ import { export class DsDatePickerInlineComponent extends DynamicFormControlComponent { @Input() bindId = true; - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() layout: DynamicFormControlLayout; @Input() model: DynamicDatePickerModel; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts index 4989dab93a..ffc36008c6 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.spec.ts @@ -1,7 +1,7 @@ // Load the implementations that should be tested import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, inject, TestBed, waitForAsync, } from '@angular/core/testing'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; @@ -15,8 +15,8 @@ import { } from '../../../../../testing/dynamic-form-mock-services'; -export const DATE_TEST_GROUP = new FormGroup({ - date: new FormControl() +export const DATE_TEST_GROUP = new UntypedFormGroup({ + date: new UntypedFormControl() }); export const DATE_TEST_MODEL_CONFIG = { diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts index 78f9935829..8d5ce5b48e 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicDsDatePickerModel } from './date-picker.model'; import { hasValue } from '../../../../../empty.util'; import { @@ -18,7 +18,7 @@ export const DS_DATE_PICKER_SEPARATOR = '-'; export class DsDatePickerComponent extends DynamicFormControlComponent implements OnInit { @Input() bindId = true; - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() model: DynamicDsDatePickerModel; @Input() legend: string; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.spec.ts index 8cfa5c818a..a25ad4d231 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { FormControl, FormGroup, FormsModule } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { TranslateModule } from '@ngx-translate/core'; @@ -31,8 +31,8 @@ describe('DsDynamicDisabledComponent', () => { name: 'disabledInput', hasSelectableMetadata: false }); - group = new FormGroup({ - disabledInput: new FormControl(), + group = new UntypedFormGroup({ + disabledInput: new UntypedFormControl(), }); } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.ts index 974858b1cc..222ad51049 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/disabled/dynamic-disabled.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicFormControlComponent, DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; @@ -18,7 +18,7 @@ import { DynamicDisabledModel } from './dynamic-disabled.model'; export class DsDynamicDisabledComponent extends DynamicFormControlComponent { @Input() formId: string; - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() model: DynamicDisabledModel; modelValuesString = ''; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-vocabulary.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-vocabulary.component.ts index 4c24443633..f19b660295 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-vocabulary.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/dynamic-vocabulary.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicFormControlComponent, @@ -25,7 +25,7 @@ import { PageInfo } from '../../../../../core/shared/page-info.model'; }) export abstract class DsDynamicVocabularyComponent extends DynamicFormControlComponent { - @Input() abstract group: FormGroup; + @Input() abstract group: UntypedFormGroup; @Input() abstract model: DsDynamicInputModel; @Output() abstract blur: EventEmitter; @@ -53,7 +53,7 @@ export abstract class DsDynamicVocabularyComponent extends DynamicFormControlCom */ getInitValueFromModel(): Observable { let initValue$: Observable; - if (isNotEmpty(this.model.value) && (this.model.value instanceof FormFieldMetadataValueObject)) { + if (isNotEmpty(this.model.value) && (this.model.value instanceof FormFieldMetadataValueObject) && !this.model.value.hasAuthorityToGenerate()) { let initEntry$: Observable; if (this.model.value.hasAuthority()) { initEntry$ = this.vocabularyService.getVocabularyEntryByID(this.model.value.authority, this.model.vocabularyOptions); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/form-group/dynamic-form-group.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/form-group/dynamic-form-group.component.ts index 9d8d73eab5..cfd1bc293c 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/form-group/dynamic-form-group.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/form-group/dynamic-form-group.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, QueryList } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicFormControlComponent, DynamicFormControlCustomEvent, @@ -22,7 +22,7 @@ export class DsDynamicFormGroupComponent extends DynamicFormControlComponent { @Input() formModel: DynamicFormControlModel[]; @Input() formLayout: DynamicFormLayout; - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() layout: DynamicFormControlLayout; @Input() model: DynamicFormGroupModel; @Input() templates: QueryList | DynamicTemplateDirective[] | undefined; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.spec.ts index b4a02457dc..51ce584bb1 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.spec.ts @@ -1,6 +1,6 @@ // Load the implementations that should be tested import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ComponentFixture, inject, TestBed, waitForAsync, } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; @@ -136,9 +136,9 @@ describe('DsDynamicListComponent test suite', () => { listFixture = TestBed.createComponent(DsDynamicListComponent); listComp = listFixture.componentInstance; // FormComponent test instance - listComp.group = new FormGroup({ - listCheckbox: new FormGroup({}), - listRadio: new FormGroup({}) + listComp.group = new UntypedFormGroup({ + listCheckbox: new UntypedFormGroup({}), + listRadio: new UntypedFormGroup({}) }); listComp.model = new DynamicListCheckboxGroupModel(LIST_CHECKBOX_TEST_MODEL_CONFIG, LAYOUT_TEST); listFixture.detectChanges(); @@ -184,9 +184,9 @@ describe('DsDynamicListComponent test suite', () => { listFixture = TestBed.createComponent(DsDynamicListComponent); listComp = listFixture.componentInstance; // FormComponent test instance - listComp.group = new FormGroup({ - listCheckbox: new FormGroup({}), - listRadio: new FormGroup({}) + listComp.group = new UntypedFormGroup({ + listCheckbox: new UntypedFormGroup({}), + listRadio: new UntypedFormGroup({}) }); listComp.model = new DynamicListCheckboxGroupModel(LIST_CHECKBOX_TEST_MODEL_CONFIG, LAYOUT_TEST); modelValue = [Object.assign(new VocabularyEntry(), { authority: 1, display: 'one', value: 1 })]; @@ -224,9 +224,9 @@ describe('DsDynamicListComponent test suite', () => { listFixture = TestBed.createComponent(DsDynamicListComponent); listComp = listFixture.componentInstance; // FormComponent test instance - listComp.group = new FormGroup({ - listCheckbox: new FormGroup({}), - listRadio: new FormGroup({}) + listComp.group = new UntypedFormGroup({ + listCheckbox: new UntypedFormGroup({}), + listRadio: new UntypedFormGroup({}) }); listComp.model = new DynamicListRadioGroupModel(LIST_RADIO_TEST_MODEL_CONFIG, LAYOUT_TEST); listFixture.detectChanges(); @@ -260,9 +260,9 @@ describe('DsDynamicListComponent test suite', () => { listFixture = TestBed.createComponent(DsDynamicListComponent); listComp = listFixture.componentInstance; // FormComponent test instance - listComp.group = new FormGroup({ - listCheckbox: new FormGroup({}), - listRadio: new FormGroup({}) + listComp.group = new UntypedFormGroup({ + listCheckbox: new UntypedFormGroup({}), + listRadio: new UntypedFormGroup({}) }); listComp.model = new DynamicListRadioGroupModel(LIST_RADIO_TEST_MODEL_CONFIG, LAYOUT_TEST); modelValue = Object.assign(new VocabularyEntry(), { authority: 1, display: 'one', value: 1 }); @@ -291,9 +291,9 @@ describe('DsDynamicListComponent test suite', () => { }) class TestComponent { - group: FormGroup = new FormGroup({ - listCheckbox: new FormGroup({}), - listRadio: new FormGroup({}) + group: UntypedFormGroup = new UntypedFormGroup({ + listCheckbox: new UntypedFormGroup({}), + listRadio: new UntypedFormGroup({}) }); model = new DynamicListCheckboxGroupModel(LIST_CHECKBOX_TEST_MODEL_CONFIG, LAYOUT_TEST); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.ts index 88158f9414..aa62e5b40d 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { FormGroup, ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms'; +import { UntypedFormGroup, ValidatorFn, ValidationErrors, AbstractControl } from '@angular/forms'; import { DynamicCheckboxModel, DynamicFormControlComponent, @@ -35,7 +35,7 @@ export interface ListItem { }) export class DsDynamicListComponent extends DynamicFormControlComponent implements OnInit { - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() model: any; @Output() blur: EventEmitter = new EventEmitter(); @@ -108,7 +108,7 @@ export class DsDynamicListComponent extends DynamicFormControlComponent implemen */ protected setOptionsFromVocabulary() { if (this.model.vocabularyOptions.name && this.model.vocabularyOptions.name.length > 0) { - const listGroup = this.group.controls[this.model.id] as FormGroup; + const listGroup = this.group.controls[this.model.id] as UntypedFormGroup; if (this.model.repeatable && this.model.required) { listGroup.addValidators(this.hasAtLeastOneVocabularyEntry()); } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts index 27029ff2be..2fea4fc985 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.spec.ts @@ -1,6 +1,6 @@ // Load the implementations that should be tested import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync, } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; @@ -71,9 +71,9 @@ let LOOKUP_NAME_TEST_MODEL_CONFIG = { hasSelectableMetadata: false }; -let LOOKUP_TEST_GROUP = new FormGroup({ - lookup: new FormControl(), - lookupName: new FormControl() +let LOOKUP_TEST_GROUP = new UntypedFormGroup({ + lookup: new UntypedFormControl(), + lookupName: new UntypedFormControl() }); describe('Dynamic Lookup component', () => { @@ -122,9 +122,9 @@ describe('Dynamic Lookup component', () => { hasSelectableMetadata: false }; - LOOKUP_TEST_GROUP = new FormGroup({ - lookup: new FormControl(), - lookupName: new FormControl() + LOOKUP_TEST_GROUP = new UntypedFormGroup({ + lookup: new UntypedFormControl(), + lookupName: new UntypedFormControl() }); } @@ -564,7 +564,7 @@ describe('Dynamic Lookup component', () => { }) class TestComponent { - group: FormGroup = LOOKUP_TEST_GROUP; + group: UntypedFormGroup = LOOKUP_TEST_GROUP; inputLookupModelConfig = LOOKUP_TEST_MODEL_CONFIG; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.ts index 3993704895..63545f45d2 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { of as observableOf, Subscription } from 'rxjs'; import { catchError, distinctUntilChanged } from 'rxjs/operators'; @@ -30,7 +30,7 @@ import { DsDynamicVocabularyComponent } from '../dynamic-vocabulary.component'; }) export class DsDynamicLookupComponent extends DsDynamicVocabularyComponent implements OnDestroy, OnInit { - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() model: any; @Output() blur: EventEmitter = new EventEmitter(); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.spec.ts index cf417145a7..69520aba63 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable max-classes-per-file */ // Load the implementations that should be tested import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ComponentFixture, fakeAsync, inject, TestBed, tick, } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { CdkTreeModule } from '@angular/cdk/tree'; @@ -47,8 +47,8 @@ export class MockNgbModalRef { } function init() { - ONEBOX_TEST_GROUP = new FormGroup({ - onebox: new FormControl(), + ONEBOX_TEST_GROUP = new UntypedFormGroup({ + onebox: new UntypedFormControl(), }); ONEBOX_TEST_MODEL_CONFIG = { @@ -457,7 +457,7 @@ describe('DsDynamicOneboxComponent test suite', () => { }) class TestComponent { - group: FormGroup = ONEBOX_TEST_GROUP; + group: UntypedFormGroup = ONEBOX_TEST_GROUP; model = new DynamicOneboxModel(ONEBOX_TEST_MODEL_CONFIG); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts index 008328bf73..251ff36a68 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; import { @@ -44,7 +44,7 @@ import { VocabularyEntryDetail } from '../../../../../../core/submission/vocabul }) export class DsDynamicOneboxComponent extends DsDynamicVocabularyComponent implements OnInit { - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() model: DynamicOneboxModel; @Output() blur: EventEmitter = new EventEmitter(); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts index 733758fd27..feca7f95c6 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.component.spec.ts @@ -1,7 +1,7 @@ // Load the implementations that should be tested import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, inject, TestBed, waitForAsync, } from '@angular/core/testing'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Store, StoreModule } from '@ngrx/store'; @@ -83,8 +83,8 @@ function init() { hasSelectableMetadata: false } as DynamicRelationGroupModelConfig; - FORM_GROUP_TEST_GROUP = new FormGroup({ - dc_contributor_author: new FormControl(), + FORM_GROUP_TEST_GROUP = new UntypedFormGroup({ + dc_contributor_author: new UntypedFormControl(), }); } @@ -96,9 +96,9 @@ describe('DsDynamicRelationGroupComponent test suite', () => { let groupFixture: ComponentFixture; let modelValue: any; let html; - let control1: FormControl; + let control1: UntypedFormControl; let model1: DsDynamicInputModel; - let control2: FormControl; + let control2: UntypedFormControl; let model2: DsDynamicInputModel; // waitForAsync beforeEach @@ -170,9 +170,9 @@ describe('DsDynamicRelationGroupComponent test suite', () => { groupComp.group = FORM_GROUP_TEST_GROUP; groupComp.model = new DynamicRelationGroupModel(FORM_GROUP_TEST_MODEL_CONFIG); groupFixture.detectChanges(); - control1 = service.getFormControlById('dc_contributor_author', (groupComp as any).formRef.formGroup, groupComp.formModel) as FormControl; + control1 = service.getFormControlById('dc_contributor_author', (groupComp as any).formRef.formGroup, groupComp.formModel) as UntypedFormControl; model1 = service.findById('dc_contributor_author', groupComp.formModel) as DsDynamicInputModel; - control2 = service.getFormControlById('local_contributor_affiliation', (groupComp as any).formRef.formGroup, groupComp.formModel) as FormControl; + control2 = service.getFormControlById('local_contributor_affiliation', (groupComp as any).formRef.formGroup, groupComp.formModel) as UntypedFormControl; model2 = service.findById('local_contributor_affiliation', groupComp.formModel) as DsDynamicInputModel; // spyOn(store, 'dispatch'); @@ -272,7 +272,7 @@ describe('DsDynamicRelationGroupComponent test suite', () => { groupComp.onChipSelected(0); groupFixture.detectChanges(); - control1 = service.getFormControlById('dc_contributor_author', (groupComp as any).formRef.formGroup, groupComp.formModel) as FormControl; + control1 = service.getFormControlById('dc_contributor_author', (groupComp as any).formRef.formGroup, groupComp.formModel) as UntypedFormControl; model1 = service.findById('dc_contributor_author', groupComp.formModel) as DsDynamicInputModel; control1.setValue('test author modify'); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts index fd111e44c2..7fdfb61b74 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/relation-group/dynamic-relation-group.components.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { combineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; import { filter, map, mergeMap, scan } from 'rxjs/operators'; @@ -43,7 +43,7 @@ import { VocabularyEntryDetail } from '../../../../../../core/submission/vocabul export class DsDynamicRelationGroupComponent extends DynamicFormControlComponent implements OnDestroy, OnInit { @Input() formId: string; - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() model: DynamicRelationGroupModel; @Output() blur: EventEmitter = new EventEmitter(); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.spec.ts index e8e971e488..0fe617e231 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.spec.ts @@ -1,6 +1,6 @@ // Load the implementations that should be tested import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ComponentFixture, fakeAsync, inject, TestBed, tick, waitForAsync, } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; @@ -21,8 +21,8 @@ import { mockDynamicFormValidationService } from '../../../../../testing/dynamic-form-mock-services'; -export const SD_TEST_GROUP = new FormGroup({ - dropdown: new FormControl(), +export const SD_TEST_GROUP = new UntypedFormGroup({ + dropdown: new UntypedFormControl(), }); export const SD_TEST_MODEL_CONFIG = { @@ -219,7 +219,7 @@ describe('Dynamic Dynamic Scrollable Dropdown component', () => { }) class TestComponent { - group: FormGroup = SD_TEST_GROUP; + group: UntypedFormGroup = SD_TEST_GROUP; model = new DynamicScrollableDropdownModel(SD_TEST_MODEL_CONFIG); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts index 5e35df5ef7..43b785b21f 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { Observable, of as observableOf } from 'rxjs'; import { catchError, distinctUntilChanged, map, tap } from 'rxjs/operators'; @@ -29,7 +29,7 @@ import { FormFieldMetadataValueObject } from '../../../models/form-field-metadat }) export class DsDynamicScrollableDropdownComponent extends DsDynamicVocabularyComponent implements OnInit { @Input() bindId = true; - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() model: DynamicScrollableDropdownModel; @Output() blur: EventEmitter = new EventEmitter(); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts index 162d9c3cec..a6a4c45170 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.spec.ts @@ -1,6 +1,6 @@ // Load the implementations that should be tested import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ComponentFixture, fakeAsync, flush, inject, TestBed, waitForAsync, } from '@angular/core/testing'; import { of as observableOf } from 'rxjs'; @@ -39,8 +39,8 @@ let TAG_TEST_GROUP; let TAG_TEST_MODEL_CONFIG; function init() { - TAG_TEST_GROUP = new FormGroup({ - tag: new FormControl(), + TAG_TEST_GROUP = new UntypedFormGroup({ + tag: new UntypedFormControl(), }); TAG_TEST_MODEL_CONFIG = { @@ -291,7 +291,7 @@ describe('DsDynamicTagComponent test suite', () => { }) class TestComponent { - group: FormGroup = TAG_TEST_GROUP; + group: UntypedFormGroup = TAG_TEST_GROUP; model = new DynamicTagModel(TAG_TEST_MODEL_CONFIG); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts index 1c015be747..4abb68a53b 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; -import { FormGroup } from '@angular/forms'; +import { UntypedFormGroup } from '@angular/forms'; import { DynamicFormLayoutService, DynamicFormValidationService } from '@ng-dynamic-forms/core'; import { Observable, of as observableOf } from 'rxjs'; @@ -32,7 +32,7 @@ import { DsDynamicVocabularyComponent } from '../dynamic-vocabulary.component'; export class DsDynamicTagComponent extends DsDynamicVocabularyComponent implements OnInit { @Input() bindId = true; - @Input() group: FormGroup; + @Input() group: UntypedFormGroup; @Input() model: DynamicTagModel; @Output() blur: EventEmitter = new EventEmitter(); diff --git a/src/app/shared/form/builder/form-builder.service.spec.ts b/src/app/shared/form/builder/form-builder.service.spec.ts index 7d1a9a3986..fd3588e73e 100644 --- a/src/app/shared/form/builder/form-builder.service.spec.ts +++ b/src/app/shared/form/builder/form-builder.service.spec.ts @@ -1,8 +1,8 @@ import { inject, TestBed } from '@angular/core/testing'; import { - FormArray, - FormControl, - FormGroup, + UntypedFormArray, + UntypedFormControl, + UntypedFormGroup, NG_ASYNC_VALIDATORS, NG_VALIDATORS, ReactiveFormsModule @@ -683,21 +683,21 @@ describe('FormBuilderService test suite', () => { const formGroup = service.createFormGroup(testModel); - expect(formGroup instanceof FormGroup).toBe(true); + expect(formGroup instanceof UntypedFormGroup).toBe(true); - expect(formGroup.get('testCheckbox') instanceof FormControl).toBe(true); - expect(formGroup.get('testCheckboxGroup') instanceof FormGroup).toBe(true); - expect(formGroup.get('testDatepicker') instanceof FormControl).toBe(true); - expect(formGroup.get('testFormArray') instanceof FormArray).toBe(true); - expect(formGroup.get('testInput') instanceof FormControl).toBe(true); - expect(formGroup.get('testRadioGroup') instanceof FormControl).toBe(true); - expect(formGroup.get('testSelect') instanceof FormControl).toBe(true); - expect(formGroup.get('testTextArea') instanceof FormControl).toBe(true); - expect(formGroup.get('testFileUpload') instanceof FormControl).toBe(true); - expect(formGroup.get('testEditor') instanceof FormControl).toBe(true); - expect(formGroup.get('testTimePicker') instanceof FormControl).toBe(true); - expect(formGroup.get('testRating') instanceof FormControl).toBe(true); - expect(formGroup.get('testColorPicker') instanceof FormControl).toBe(true); + expect(formGroup.get('testCheckbox') instanceof UntypedFormControl).toBe(true); + expect(formGroup.get('testCheckboxGroup') instanceof UntypedFormGroup).toBe(true); + expect(formGroup.get('testDatepicker') instanceof UntypedFormControl).toBe(true); + expect(formGroup.get('testFormArray') instanceof UntypedFormArray).toBe(true); + expect(formGroup.get('testInput') instanceof UntypedFormControl).toBe(true); + expect(formGroup.get('testRadioGroup') instanceof UntypedFormControl).toBe(true); + expect(formGroup.get('testSelect') instanceof UntypedFormControl).toBe(true); + expect(formGroup.get('testTextArea') instanceof UntypedFormControl).toBe(true); + expect(formGroup.get('testFileUpload') instanceof UntypedFormControl).toBe(true); + expect(formGroup.get('testEditor') instanceof UntypedFormControl).toBe(true); + expect(formGroup.get('testTimePicker') instanceof UntypedFormControl).toBe(true); + expect(formGroup.get('testRating') instanceof UntypedFormControl).toBe(true); + expect(formGroup.get('testColorPicker') instanceof UntypedFormControl).toBe(true); }); it('should throw when unknown DynamicFormControlModel id is specified in JSON', () => { @@ -720,7 +720,7 @@ describe('FormBuilderService test suite', () => { it('should add a form control to an existing form group', () => { const formGroup = service.createFormGroup(testModel); - const nestedFormGroup = formGroup.controls.testFormGroup as FormGroup; + const nestedFormGroup = formGroup.controls.testFormGroup as UntypedFormGroup; const nestedFormGroupModel = testModel[7] as DynamicFormGroupModel; const newModel1 = new DynamicInputModel({ id: 'newInput1' }); const newModel2 = new DynamicInputModel({ id: 'newInput2' }); @@ -731,14 +731,14 @@ describe('FormBuilderService test suite', () => { expect(formGroup.controls[newModel1.id]).toBeTruthy(); expect(testModel[testModel.length - 1] === newModel1).toBe(true); - expect((formGroup.controls.testFormGroup as FormGroup).controls[newModel2.id]).toBeTruthy(); + expect((formGroup.controls.testFormGroup as UntypedFormGroup).controls[newModel2.id]).toBeTruthy(); expect(nestedFormGroupModel.get(nestedFormGroupModel.group.length - 1) === newModel2).toBe(true); }); it('should insert a form control to an existing form group', () => { const formGroup = service.createFormGroup(testModel); - const nestedFormGroup = formGroup.controls.testFormGroup as FormGroup; + const nestedFormGroup = formGroup.controls.testFormGroup as UntypedFormGroup; const nestedFormGroupModel = testModel[7] as DynamicFormGroupModel; const newModel1 = new DynamicInputModel({ id: 'newInput1' }); const newModel2 = new DynamicInputModel({ id: 'newInput2' }); @@ -750,7 +750,7 @@ describe('FormBuilderService test suite', () => { expect(testModel[4] === newModel1).toBe(true); expect(service.getPath(testModel[4])).toEqual(['newInput1']); - expect((formGroup.controls.testFormGroup as FormGroup).controls[newModel2.id]).toBeTruthy(); + expect((formGroup.controls.testFormGroup as UntypedFormGroup).controls[newModel2.id]).toBeTruthy(); expect(nestedFormGroupModel.get(0) === newModel2).toBe(true); expect(service.getPath(nestedFormGroupModel.get(0))).toEqual(['testFormGroup', 'newInput2']); }); @@ -769,14 +769,14 @@ describe('FormBuilderService test suite', () => { service.moveFormGroupControl(0, 1, nestedFormGroupModel); - expect((formGroup.controls.testFormGroup as FormGroup).controls[model2.id]).toBeTruthy(); + expect((formGroup.controls.testFormGroup as UntypedFormGroup).controls[model2.id]).toBeTruthy(); expect(nestedFormGroupModel.get(1) === model2).toBe(true); }); it('should remove a form control from an existing form group', () => { const formGroup = service.createFormGroup(testModel); - const nestedFormGroup = formGroup.controls.testFormGroup as FormGroup; + const nestedFormGroup = formGroup.controls.testFormGroup as UntypedFormGroup; const nestedFormGroupModel = testModel[7] as DynamicFormGroupModel; const length = testModel.length; const size = nestedFormGroupModel.size(); @@ -810,7 +810,7 @@ describe('FormBuilderService test suite', () => { formArray = service.createFormArray(model); - expect(formArray instanceof FormArray).toBe(true); + expect(formArray instanceof UntypedFormArray).toBe(true); expect(formArray.length).toBe(model.initialCount); }); @@ -841,8 +841,8 @@ describe('FormBuilderService test suite', () => { const index = 3; const step = 1; - (formArray.at(index) as FormGroup).controls.testFormArrayGroupInput.setValue('next test value 1'); - (formArray.at(index + step) as FormGroup).controls.testFormArrayGroupInput.setValue('next test value 2'); + (formArray.at(index) as UntypedFormGroup).controls.testFormArrayGroupInput.setValue('next test value 1'); + (formArray.at(index + step) as UntypedFormGroup).controls.testFormArrayGroupInput.setValue('next test value 2'); (model.get(index).get(0) as DynamicFormValueControlModel).value = 'next test value 1'; (model.get(index + step).get(0) as DynamicFormValueControlModel).value = 'next test value 2'; @@ -851,8 +851,8 @@ describe('FormBuilderService test suite', () => { expect(formArray.length).toBe(model.initialCount); - expect((formArray.at(index) as FormGroup).controls.testFormArrayGroupInput.value).toEqual('next test value 2'); - expect((formArray.at(index + step) as FormGroup).controls.testFormArrayGroupInput.value).toEqual('next test value 1'); + expect((formArray.at(index) as UntypedFormGroup).controls.testFormArrayGroupInput.value).toEqual('next test value 2'); + expect((formArray.at(index + step) as UntypedFormGroup).controls.testFormArrayGroupInput.value).toEqual('next test value 1'); expect((model.get(index).get(0) as DynamicFormValueControlModel).value).toEqual('next test value 2'); expect((model.get(index + step).get(0) as DynamicFormValueControlModel).value).toEqual('next test value 1'); @@ -865,8 +865,8 @@ describe('FormBuilderService test suite', () => { const index = 3; const step = -1; - (formArray.at(index) as FormGroup).controls.testFormArrayGroupInput.setValue('next test value 1'); - (formArray.at(index + step) as FormGroup).controls.testFormArrayGroupInput.setValue('next test value 2'); + (formArray.at(index) as UntypedFormGroup).controls.testFormArrayGroupInput.setValue('next test value 1'); + (formArray.at(index + step) as UntypedFormGroup).controls.testFormArrayGroupInput.setValue('next test value 2'); (model.get(index).get(0) as DynamicFormValueControlModel).value = 'next test value 1'; (model.get(index + step).get(0) as DynamicFormValueControlModel).value = 'next test value 2'; @@ -875,8 +875,8 @@ describe('FormBuilderService test suite', () => { expect(formArray.length).toBe(model.initialCount); - expect((formArray.at(index) as FormGroup).controls.testFormArrayGroupInput.value).toEqual('next test value 2'); - expect((formArray.at(index + step) as FormGroup).controls.testFormArrayGroupInput.value).toEqual('next test value 1'); + expect((formArray.at(index) as UntypedFormGroup).controls.testFormArrayGroupInput.value).toEqual('next test value 2'); + expect((formArray.at(index + step) as UntypedFormGroup).controls.testFormArrayGroupInput.value).toEqual('next test value 1'); expect((model.get(index).get(0) as DynamicFormValueControlModel).value).toEqual('next test value 2'); expect((model.get(index + step).get(0) as DynamicFormValueControlModel).value).toEqual('next test value 1'); diff --git a/src/app/shared/form/builder/form-builder.service.ts b/src/app/shared/form/builder/form-builder.service.ts index b3a78d4669..cf6f38bf7b 100644 --- a/src/app/shared/form/builder/form-builder.service.ts +++ b/src/app/shared/form/builder/form-builder.service.ts @@ -1,5 +1,5 @@ import {Injectable, Optional} from '@angular/core'; -import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; +import { AbstractControl, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { DYNAMIC_FORM_CONTROL_TYPE_ARRAY, @@ -59,7 +59,7 @@ export class FormBuilderService extends DynamicFormService { /** * This map contains the active forms control groups */ - private formGroups: Map; + private formGroups: Map; /** * This is the field to use for type binding @@ -83,7 +83,7 @@ export class FormBuilderService extends DynamicFormService { } - createDynamicFormControlEvent(control: FormControl, group: FormGroup, model: DynamicFormControlModel, type: string): DynamicFormControlEvent { + createDynamicFormControlEvent(control: UntypedFormControl, group: UntypedFormGroup, model: DynamicFormControlModel, type: string): DynamicFormControlEvent { const $event = { value: (model as any).value, autoSave: false @@ -369,12 +369,12 @@ export class FormBuilderService extends DynamicFormService { return model.type === DYNAMIC_FORM_CONTROL_TYPE_INPUT; } - getFormControlById(id: string, formGroup: FormGroup, groupModel: DynamicFormControlModel[], index = 0): AbstractControl { + getFormControlById(id: string, formGroup: UntypedFormGroup, groupModel: DynamicFormControlModel[], index = 0): AbstractControl { const fieldModel = this.findById(id, groupModel, index); return isNotEmpty(fieldModel) ? formGroup.get(this.getPath(fieldModel)) : null; } - getFormControlByModel(formGroup: FormGroup, fieldModel: DynamicFormControlModel): AbstractControl { + getFormControlByModel(formGroup: UntypedFormGroup, fieldModel: DynamicFormControlModel): AbstractControl { return isNotEmpty(fieldModel) ? formGroup.get(this.getPath(fieldModel)) : null; } @@ -412,7 +412,7 @@ export class FormBuilderService extends DynamicFormService { * @param id id of model * @param formGroup FormGroup */ - addFormGroups(id: string, formGroup: FormGroup): void { + addFormGroups(id: string, formGroup: UntypedFormGroup): void { this.formGroups.set(id, formGroup); } diff --git a/src/app/shared/form/builder/models/form-field-metadata-value.model.ts b/src/app/shared/form/builder/models/form-field-metadata-value.model.ts index b58844a537..9b18084e40 100644 --- a/src/app/shared/form/builder/models/form-field-metadata-value.model.ts +++ b/src/app/shared/form/builder/models/form-field-metadata-value.model.ts @@ -11,6 +11,10 @@ export interface OtherInformation { * A class representing a specific input-form field's value */ export class FormFieldMetadataValueObject implements MetadataValueInterface { + + static readonly AUTHORITY_SPLIT: string = '::'; + static readonly AUTHORITY_GENERATE: string = 'will be generated' + FormFieldMetadataValueObject.AUTHORITY_SPLIT; + metadata?: string; value: any; display: string; @@ -58,6 +62,13 @@ export class FormFieldMetadataValueObject implements MetadataValueInterface { return isNotEmpty(this.authority); } + /** + * Returns true if this object has an authority value that needs to be generated + */ + hasAuthorityToGenerate(): boolean { + return isNotEmpty(this.authority) && this.authority.startsWith(FormFieldMetadataValueObject.AUTHORITY_GENERATE); + } + /** * Returns true if this this object has a value */ diff --git a/src/app/shared/form/form.component.ts b/src/app/shared/form/form.component.ts index 086b5e1fd8..79cf8ad2c7 100644 --- a/src/app/shared/form/form.component.ts +++ b/src/app/shared/form/form.component.ts @@ -1,6 +1,6 @@ import { distinctUntilChanged, filter, map } from 'rxjs/operators'; import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; -import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms'; +import { AbstractControl, UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { Observable, Subscription } from 'rxjs'; import { @@ -68,7 +68,7 @@ export class FormComponent implements OnDestroy, OnInit { */ @Input() formModel: DynamicFormControlModel[]; @Input() parentFormModel: DynamicFormGroupModel | DynamicFormGroupModel[]; - @Input() formGroup: FormGroup; + @Input() formGroup: UntypedFormGroup; @Input() formLayout = null as DynamicFormLayout; /* eslint-disable @angular-eslint/no-output-rename */ @@ -122,9 +122,9 @@ export class FormComponent implements OnDestroy, OnInit { })); }*/ - private getFormGroup(): FormGroup { + private getFormGroup(): UntypedFormGroup { if (!!this.parentFormModel) { - return this.formGroup.parent as FormGroup; + return this.formGroup.parent as UntypedFormGroup; } return this.formGroup; @@ -184,7 +184,7 @@ export class FormComponent implements OnDestroy, OnInit { const { fieldIndex } = error; let field: AbstractControl; if (!!this.parentFormModel) { - field = this.formBuilderService.getFormControlById(fieldId, formGroup.parent as FormGroup, formModel, fieldIndex); + field = this.formBuilderService.getFormControlById(fieldId, formGroup.parent as UntypedFormGroup, formModel, fieldIndex); } else { field = this.formBuilderService.getFormControlById(fieldId, formGroup, formModel, fieldIndex); } @@ -207,7 +207,7 @@ export class FormComponent implements OnDestroy, OnInit { const { fieldIndex } = error; let field: AbstractControl; if (!!this.parentFormModel) { - field = this.formBuilderService.getFormControlById(fieldId, formGroup.parent as FormGroup, formModel, fieldIndex); + field = this.formBuilderService.getFormControlById(fieldId, formGroup.parent as UntypedFormGroup, formModel, fieldIndex); } else { field = this.formBuilderService.getFormControlById(fieldId, formGroup, formModel, fieldIndex); } @@ -254,7 +254,7 @@ export class FormComponent implements OnDestroy, OnInit { onBlur(event: DynamicFormControlEvent): void { this.blur.emit(event); - const control: FormControl = event.control; + const control: UntypedFormControl = event.control; const fieldIndex: number = (event.context && event.context.index) ? event.context.index : 0; if (control.valid) { this.formService.removeError(this.formId, event.model.name, fieldIndex); @@ -280,7 +280,7 @@ export class FormComponent implements OnDestroy, OnInit { this.change.emit(event); } - const control: FormControl = event.control; + const control: UntypedFormControl = event.control; const fieldIndex: number = (event.context && event.context.index) ? event.context.index : 0; if (control.valid) { this.formService.removeError(this.formId, event.model.id, fieldIndex); @@ -314,7 +314,7 @@ export class FormComponent implements OnDestroy, OnInit { } removeItem($event, arrayContext: DynamicFormArrayModel, index: number): void { - const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray; + const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as UntypedFormArray; const event = this.getEvent($event, arrayContext, index, 'remove'); if (this.formBuilderService.isQualdropGroup(event.model as DynamicFormControlModel)) { // In case of qualdrop value remove event must be dispatched before removing the control from array @@ -329,7 +329,7 @@ export class FormComponent implements OnDestroy, OnInit { } insertItem($event, arrayContext: DynamicFormArrayModel, index: number): void { - const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray; + const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as UntypedFormArray; this.formBuilderService.insertFormArrayGroup(index, formArrayControl, arrayContext); this.addArrayItem.emit(this.getEvent($event, arrayContext, index, 'add')); this.formService.changeForm(this.formId, this.formModel); @@ -344,17 +344,17 @@ export class FormComponent implements OnDestroy, OnInit { protected getEvent($event: any, arrayContext: DynamicFormArrayModel, index: number, type: string): DynamicFormControlEvent { const context = arrayContext.groups[index]; const itemGroupModel = context.context; - let group = this.formGroup.get(itemGroupModel.id) as FormGroup; + let group = this.formGroup.get(itemGroupModel.id) as UntypedFormGroup; if (isNull(group)) { for (const key of Object.keys(this.formGroup.controls)) { - group = this.formGroup.controls[key].get(itemGroupModel.id) as FormGroup; + group = this.formGroup.controls[key].get(itemGroupModel.id) as UntypedFormGroup; if (isNotNull(group)) { break; } } } const model = context.group[0] as DynamicFormControlModel; - const control = group.controls[index] as FormControl; + const control = group.controls[index] as UntypedFormControl; return { $event, context, control, group, model, type }; } } diff --git a/src/app/shared/form/form.service.spec.ts b/src/app/shared/form/form.service.spec.ts index e565c8f219..57880ffc14 100644 --- a/src/app/shared/form/form.service.spec.ts +++ b/src/app/shared/form/form.service.spec.ts @@ -1,6 +1,6 @@ import { Store, StoreModule } from '@ngrx/store'; import { inject, TestBed, waitForAsync } from '@angular/core/testing'; -import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms'; +import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { DynamicFormControlModel, DynamicFormGroupModel, DynamicInputModel } from '@ng-dynamic-forms/core'; @@ -22,7 +22,7 @@ describe('FormService test suite', () => { const formId = 'testForm'; let service: FormService; let builderService: FormBuilderService; - let formGroup: FormGroup; + let formGroup: UntypedFormGroup; const formModel: DynamicFormControlModel[] = [ new DynamicInputModel({ id: 'author', value: 'test' }), @@ -104,18 +104,18 @@ describe('FormService test suite', () => { .subscribe((state) => { state.forms = formState; }); - const author: AbstractControl = new FormControl('test'); - const title: AbstractControl = new FormControl(undefined, Validators.required); - const date: AbstractControl = new FormControl(undefined); - const description: AbstractControl = new FormControl(undefined); + const author: AbstractControl = new UntypedFormControl('test'); + const title: AbstractControl = new UntypedFormControl(undefined, Validators.required); + const date: AbstractControl = new UntypedFormControl(undefined); + const description: AbstractControl = new UntypedFormControl(undefined); - const addressLocation: FormGroup = new FormGroup({ - zipCode: new FormControl(undefined), - state: new FormControl(undefined), - city: new FormControl(undefined), + const addressLocation: UntypedFormGroup = new UntypedFormGroup({ + zipCode: new UntypedFormControl(undefined), + state: new UntypedFormControl(undefined), + city: new UntypedFormControl(undefined), }); - formGroup = new FormGroup({ author, title, date, description, addressLocation }); + formGroup = new UntypedFormGroup({ author, title, date, description, addressLocation }); controls = { author, title, date, description , addressLocation }; service = new FormService(builderService, store); }) diff --git a/src/app/shared/form/form.service.ts b/src/app/shared/form/form.service.ts index 2dbf78f565..bf316daaed 100644 --- a/src/app/shared/form/form.service.ts +++ b/src/app/shared/form/form.service.ts @@ -1,6 +1,6 @@ import { distinctUntilChanged, filter, map } from 'rxjs/operators'; import { Injectable } from '@angular/core'; -import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms'; +import { AbstractControl, UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; import { select, Store } from '@ngrx/store'; @@ -96,13 +96,13 @@ export class FormService { /** * Method to validate form's fields */ - public validateAllFormFields(formGroup: FormGroup | FormArray) { + public validateAllFormFields(formGroup: UntypedFormGroup | UntypedFormArray) { Object.keys(formGroup.controls).forEach((field) => { const control = formGroup.get(field); - if (control instanceof FormControl) { + if (control instanceof UntypedFormControl) { control.markAsTouched({ onlySelf: true }); control.markAsDirty({ onlySelf: true }); - } else if (control instanceof FormGroup || control instanceof FormArray) { + } else if (control instanceof UntypedFormGroup || control instanceof UntypedFormArray) { this.validateAllFormFields(control); } }); @@ -112,14 +112,14 @@ export class FormService { * Check if form group has an invalid form control * @param formGroup The form group to check */ - public hasValidationErrors(formGroup: FormGroup | FormArray): boolean { + public hasValidationErrors(formGroup: UntypedFormGroup | UntypedFormArray): boolean { let hasErrors = false; const fields: string[] = Object.keys(formGroup.controls); for (const field of fields) { const control = formGroup.get(field); - if (control instanceof FormControl) { + if (control instanceof UntypedFormControl) { hasErrors = !control.valid && control.touched; - } else if (control instanceof FormGroup || control instanceof FormArray) { + } else if (control instanceof UntypedFormGroup || control instanceof UntypedFormArray) { hasErrors = this.hasValidationErrors(control); } if (hasErrors) { @@ -162,7 +162,7 @@ export class FormService { } // if the field in question is a concat group, pass down the error to its fields - if (field instanceof FormGroup && model instanceof DynamicFormGroupModel && this.formBuilderService.isConcatGroup(model)) { + if (field instanceof UntypedFormGroup && model instanceof DynamicFormGroupModel && this.formBuilderService.isConcatGroup(model)) { model.group.forEach((subModel) => { const subField = field.controls[subModel.id]; @@ -183,7 +183,7 @@ export class FormService { } // if the field in question is a concat group, clear the error from its fields - if (field instanceof FormGroup && model instanceof DynamicFormGroupModel && this.formBuilderService.isConcatGroup(model)) { + if (field instanceof UntypedFormGroup && model instanceof DynamicFormGroupModel && this.formBuilderService.isConcatGroup(model)) { model.group.forEach((subModel) => { const subField = field.controls[subModel.id]; @@ -194,7 +194,7 @@ export class FormService { field.markAsUntouched(); } - public resetForm(formGroup: FormGroup, groupModel: DynamicFormControlModel[], formId: string) { + public resetForm(formGroup: UntypedFormGroup, groupModel: DynamicFormControlModel[], formId: string) { this.formBuilderService.clearAllModelsValue(groupModel); formGroup.reset(); this.store.dispatch(new FormChangeAction(formId, formGroup.value)); diff --git a/src/app/shared/form/number-picker/number-picker.component.ts b/src/app/shared/form/number-picker/number-picker.component.ts index 0df1e050cd..82240c41d1 100644 --- a/src/app/shared/form/number-picker/number-picker.component.ts +++ b/src/app/shared/form/number-picker/number-picker.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, SimpleChanges, } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ControlValueAccessor, UntypedFormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; import { isEmpty } from '../../empty.util'; @Component({ @@ -31,7 +31,7 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor { startValue: number; - constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) { + constructor(private fb: UntypedFormBuilder, private cd: ChangeDetectorRef) { } ngOnInit() { diff --git a/src/app/shared/input-suggestions/dso-input-suggestions/dso-input-suggestions.component.ts b/src/app/shared/input-suggestions/dso-input-suggestions/dso-input-suggestions.component.ts index e7ca432476..26c7d91b41 100644 --- a/src/app/shared/input-suggestions/dso-input-suggestions/dso-input-suggestions.component.ts +++ b/src/app/shared/input-suggestions/dso-input-suggestions/dso-input-suggestions.component.ts @@ -5,6 +5,7 @@ import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { ViewMode } from '../../../core/shared/view-mode.model'; import { CollectionElementLinkType } from '../../object-collection/collection-element-link.type'; import { hasValue } from '../../empty.util'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-dso-input-suggestions', @@ -42,16 +43,22 @@ export class DsoInputSuggestionsComponent extends InputSuggestionsComponent { currentObject: DSpaceObject; + constructor( + protected dsoNameService: DSONameService, + ) { + super(); + } + onSubmit(data: DSpaceObject) { if (hasValue(data)) { - this.value = data.name; + this.value = this.dsoNameService.getName(data); this.currentObject = data; this.submitSuggestion.emit(data); } } onClickSuggestion(data: DSpaceObject) { - this.value = data.name; + this.value = this.dsoNameService.getName(data); this.currentObject = data; this.clickSuggestion.emit(data); this.close(); diff --git a/src/app/shared/input-suggestions/validation-suggestions/validation-suggestions.component.ts b/src/app/shared/input-suggestions/validation-suggestions/validation-suggestions.component.ts index d845df0555..050b3296c5 100644 --- a/src/app/shared/input-suggestions/validation-suggestions/validation-suggestions.component.ts +++ b/src/app/shared/input-suggestions/validation-suggestions/validation-suggestions.component.ts @@ -1,5 +1,5 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core'; -import { FormControl, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service'; import { MetadatumViewModel } from '../../../core/shared/metadata.models'; import { MetadataFieldValidator } from '../../utils/metadatafield-validator.directive'; @@ -26,7 +26,7 @@ import { InputSuggestion } from '../input-suggestions.model'; */ export class ValidationSuggestionsComponent extends InputSuggestionsComponent implements OnInit { - form: FormGroup; + form: UntypedFormGroup; /** * The current url of this page @@ -52,8 +52,8 @@ export class ValidationSuggestionsComponent extends InputSuggestionsComponent im } ngOnInit() { - this.form = new FormGroup({ - metadataNameField: new FormControl(this._value, { + this.form = new UntypedFormGroup({ + metadataNameField: new UntypedFormControl(this._value, { asyncValidators: [this.metadataFieldValidator.validate.bind(this.metadataFieldValidator)], validators: [Validators.required] }) diff --git a/src/app/shared/log-in/methods/password/log-in-password.component.spec.ts b/src/app/shared/log-in/methods/password/log-in-password.component.spec.ts index 5238482770..5d9370f204 100644 --- a/src/app/shared/log-in/methods/password/log-in-password.component.spec.ts +++ b/src/app/shared/log-in/methods/password/log-in-password.component.spec.ts @@ -1,6 +1,6 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { provideMockStore } from '@ngrx/store/testing'; @@ -95,7 +95,7 @@ describe('LogInPasswordComponent', () => { it('should create a FormGroup comprised of FormControls', () => { fixture.detectChanges(); - expect(component.form instanceof FormGroup).toBe(true); + expect(component.form instanceof UntypedFormGroup).toBe(true); }); it('should authenticate', () => { diff --git a/src/app/shared/log-in/methods/password/log-in-password.component.ts b/src/app/shared/log-in/methods/password/log-in-password.component.ts index e4a92ded29..22b3b13130 100644 --- a/src/app/shared/log-in/methods/password/log-in-password.component.ts +++ b/src/app/shared/log-in/methods/password/log-in-password.component.ts @@ -1,6 +1,6 @@ import { map } from 'rxjs/operators'; import { Component, Inject, OnInit } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { select, Store } from '@ngrx/store'; import { Observable } from 'rxjs'; @@ -63,7 +63,7 @@ export class LogInPasswordComponent implements OnInit { * The authentication form. * @type {FormGroup} */ - public form: FormGroup; + public form: UntypedFormGroup; /** * @constructor @@ -79,7 +79,7 @@ export class LogInPasswordComponent implements OnInit { @Inject('isStandalonePage') public isStandalonePage: boolean, private authService: AuthService, private hardRedirectService: HardRedirectService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private store: Store ) { this.authMethod = injectedAuthMethodModel; diff --git a/src/app/shared/menu/menu.service.spec.ts b/src/app/shared/menu/menu.service.spec.ts index f4b0fe3db8..0d8d669a0a 100644 --- a/src/app/shared/menu/menu.service.spec.ts +++ b/src/app/shared/menu/menu.service.spec.ts @@ -567,4 +567,41 @@ describe('MenuService', () => { }); }); + describe(`resolveSubstitutions`, () => { + let linkPrefix; + let link; + let uuid; + + beforeEach(() => { + linkPrefix = 'statistics_collection_'; + link = `${linkPrefix}:id`; + uuid = 'f7cc3ca4-3c2c-464d-8af8-add9f84f711c'; + }); + + it(`shouldn't do anything when there are no params`, () => { + let result = (service as any).resolveSubstitutions(link, undefined); + expect(result).toEqual(link); + result = (service as any).resolveSubstitutions(link, null); + expect(result).toEqual(link); + result = (service as any).resolveSubstitutions(link, {}); + expect(result).toEqual(link); + }); + + it(`should replace link params that are also route params`, () => { + const result = (service as any).resolveSubstitutions(link,{ 'id': uuid }); + expect(result).toEqual(linkPrefix + uuid); + }); + + it(`should not replace link params that aren't route params`, () => { + const result = (service as any).resolveSubstitutions(link,{ 'something': 'else' }); + expect(result).toEqual(link); + }); + + it(`should gracefully deal with routes that contain the name of the route param`, () => { + const selfReferentialParam = `:id:something`; + const result = (service as any).resolveSubstitutions(link,{ 'id': selfReferentialParam }); + expect(result).toEqual(linkPrefix + selfReferentialParam); + }); + }); + }); diff --git a/src/app/shared/menu/menu.service.ts b/src/app/shared/menu/menu.service.ts index 2253e9ce09..fac3b3fba7 100644 --- a/src/app/shared/menu/menu.service.ts +++ b/src/app/shared/menu/menu.service.ts @@ -17,7 +17,7 @@ import { ToggleActiveMenuSectionAction, ToggleMenuAction, } from './menu.actions'; -import { hasNoValue, hasValue, hasValueOperator, isNotEmpty } from '../empty.util'; +import { hasNoValue, hasValue, hasValueOperator, isNotEmpty, isEmpty } from '../empty.util'; import { MenuState } from './menu-state.model'; import { MenuSections } from './menu-sections.model'; import { MenuSection } from './menu-section.model'; @@ -409,20 +409,14 @@ export class MenuService { } protected resolveSubstitutions(object, params) { - let resolved; - if (typeof object === 'string') { + if (isEmpty(params)) { resolved = object; - let match: RegExpMatchArray; - do { - match = resolved.match(/:(\w+)/); - if (match) { - const substitute = params[match[1]]; - if (hasValue(substitute)) { - resolved = resolved.replace(match[0], `${substitute}`); - } - } - } while (match); + } else if (typeof object === 'string') { + resolved = object; + Object.entries(params).forEach(([key, value]: [string, string]) => + resolved = resolved.replaceAll(`:${key}`, value) + ); } else if (Array.isArray(object)) { resolved = []; object.forEach((entry, index) => { diff --git a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts index 7077949809..42ee093278 100644 --- a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts +++ b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts @@ -1,4 +1,4 @@ -import { Component, ComponentFactoryResolver, Inject, Input, OnInit, ViewChild } from '@angular/core'; +import { Component, ComponentFactoryResolver, Inject, Input, OnInit, ViewChild, OnChanges, SimpleChanges, ComponentRef, ViewContainerRef, ComponentFactory } from '@angular/core'; import { MetadataRepresentation, MetadataRepresentationType @@ -8,19 +8,17 @@ import { Context } from '../../core/shared/context.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { MetadataRepresentationListElementComponent } from '../object-list/metadata-representation-list-element/metadata-representation-list-element.component'; import { MetadataRepresentationDirective } from './metadata-representation.directive'; -import { hasValue } from '../empty.util'; +import { hasValue, isNotEmpty, hasNoValue } from '../empty.util'; import { ThemeService } from '../theme-support/theme.service'; @Component({ selector: 'ds-metadata-representation-loader', - // styleUrls: ['./metadata-representation-loader.component.scss'], templateUrl: './metadata-representation-loader.component.html' }) /** * Component for determining what component to use depending on the item's entity type (dspace.entity.type), its metadata representation and, optionally, its context */ -export class MetadataRepresentationLoaderComponent implements OnInit { - private componentRefInstance: MetadataRepresentationListElementComponent; +export class MetadataRepresentationLoaderComponent implements OnInit, OnChanges { /** * The item or metadata to determine the component for @@ -31,8 +29,8 @@ export class MetadataRepresentationLoaderComponent implements OnInit { } @Input() set mdRepresentation(nextValue: MetadataRepresentation) { this._mdRepresentation = nextValue; - if (hasValue(this.componentRefInstance)) { - this.componentRefInstance.metadataRepresentation = nextValue; + if (hasValue(this.compRef?.instance)) { + this.compRef.instance.mdRepresentation = nextValue; } } @@ -46,6 +44,16 @@ export class MetadataRepresentationLoaderComponent implements OnInit { */ @ViewChild(MetadataRepresentationDirective, {static: true}) mdRepDirective: MetadataRepresentationDirective; + /** + * The reference to the dynamic component + */ + protected compRef: ComponentRef; + + protected inAndOutputNames: (keyof this)[] = [ + 'context', + 'mdRepresentation', + ]; + constructor( private componentFactoryResolver: ComponentFactoryResolver, private themeService: ThemeService, @@ -57,14 +65,41 @@ export class MetadataRepresentationLoaderComponent implements OnInit { * Set up the dynamic child component */ ngOnInit(): void { - const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent()); + this.instantiateComponent(); + } - const viewContainerRef = this.mdRepDirective.viewContainerRef; + /** + * Whenever the inputs change, update the inputs of the dynamic component + */ + ngOnChanges(changes: SimpleChanges): void { + if (hasNoValue(this.compRef)) { + // sometimes the component has not been initialized yet, so it first needs to be initialized + // before being called again + this.instantiateComponent(changes); + } else { + // if an input or output has changed + if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) { + this.connectInputsAndOutputs(); + if (this.compRef?.instance && 'ngOnChanges' in this.compRef.instance) { + (this.compRef.instance as any).ngOnChanges(changes); + } + } + } + } + + private instantiateComponent(changes?: SimpleChanges): void { + const componentFactory: ComponentFactory = this.componentFactoryResolver.resolveComponentFactory(this.getComponent()); + + const viewContainerRef: ViewContainerRef = this.mdRepDirective.viewContainerRef; viewContainerRef.clear(); - const componentRef = viewContainerRef.createComponent(componentFactory); - this.componentRefInstance = componentRef.instance as MetadataRepresentationListElementComponent; - this.componentRefInstance.metadataRepresentation = this.mdRepresentation; + this.compRef = viewContainerRef.createComponent(componentFactory); + + if (hasValue(changes)) { + this.ngOnChanges(changes); + } else { + this.connectInputsAndOutputs(); + } } /** @@ -74,4 +109,16 @@ export class MetadataRepresentationLoaderComponent implements OnInit { private getComponent(): GenericConstructor { return this.getMetadataRepresentationComponent(this.mdRepresentation.itemType, this.mdRepresentation.representationType, this.context, this.themeService.getThemeName()); } + + /** + * Connect the in and outputs of this component to the dynamic component, + * to ensure they're in sync + */ + protected connectInputsAndOutputs(): void { + if (isNotEmpty(this.inAndOutputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { + this.inAndOutputNames.filter((name: any) => this[name] !== undefined).forEach((name: any) => { + this.compRef.instance[name] = this[name]; + }); + } + } } diff --git a/src/app/shared/mocks/dso-name.service.mock.ts b/src/app/shared/mocks/dso-name.service.mock.ts index cf3cf5466b..b8fbaebee4 100644 --- a/src/app/shared/mocks/dso-name.service.mock.ts +++ b/src/app/shared/mocks/dso-name.service.mock.ts @@ -3,8 +3,8 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model'; export const UNDEFINED_NAME = 'Undefined'; export class DSONameServiceMock { - public getName(dso: DSpaceObject) { - return UNDEFINED_NAME; + public getName(dso: DSpaceObject | undefined) { + return dso?.name || UNDEFINED_NAME; } public getHitHighlights(object: any, dso: DSpaceObject) { diff --git a/src/app/shared/mocks/form-builder-service.mock.ts b/src/app/shared/mocks/form-builder-service.mock.ts index f9d9dffdcb..eaaeb60829 100644 --- a/src/app/shared/mocks/form-builder-service.mock.ts +++ b/src/app/shared/mocks/form-builder-service.mock.ts @@ -1,14 +1,14 @@ import { FormBuilderService } from '../form/builder/form-builder.service'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import {DsDynamicInputModel} from '../form/builder/ds-dynamic-form-ui/models/ds-dynamic-input.model'; export function getMockFormBuilderService(): FormBuilderService { return jasmine.createSpyObj('FormBuilderService', { modelFromConfiguration: [], - createFormGroup: new FormGroup({}), + createFormGroup: new UntypedFormGroup({}), getValueFromModel: {}, - getFormControlById: new FormControl(), + getFormControlById: new UntypedFormControl(), hasMappedGroupValue: false, findById: {}, getPath: ['test', 'path'], diff --git a/src/app/shared/mocks/request.service.mock.ts b/src/app/shared/mocks/request.service.mock.ts index bce5b2d466..90db66dcd7 100644 --- a/src/app/shared/mocks/request.service.mock.ts +++ b/src/app/shared/mocks/request.service.mock.ts @@ -14,6 +14,7 @@ export function getMockRequestService(requestEntry$: Observable = removeByHrefSubstring: observableOf(true), setStaleByHrefSubstring: observableOf(true), setStaleByUUID: observableOf(true), - hasByHref$: observableOf(false) + hasByHref$: observableOf(false), + shouldDispatchRequest: true }); } diff --git a/src/app/shared/mocks/submission.mock.ts b/src/app/shared/mocks/submission.mock.ts index b90cababcb..268ae33ab3 100644 --- a/src/app/shared/mocks/submission.mock.ts +++ b/src/app/shared/mocks/submission.mock.ts @@ -1668,11 +1668,7 @@ export const mockFileFormData = { ], endDate: [ { - value: { - year: 2019, - month: 1, - day: 16 - }, + value: new Date('2019-01-16'), language: null, authority: null, display: { @@ -1694,7 +1690,7 @@ export const mockFileFormData = { value: 'embargo', language: null, authority: null, - display: 'lease', + display: 'embargo', confidence: -1, place: 0, otherInformation: null @@ -1702,11 +1698,7 @@ export const mockFileFormData = { ], startDate: [ { - value: { - year: 2019, - month: 1, - day: 16 - }, + value: new Date('2019-01-16'), language: null, authority: null, display: { diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts index b3ea5e1258..2d369ae014 100644 --- a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts +++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormGroup, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'; @@ -25,7 +25,7 @@ import { ClaimedDeclinedTaskSearchResult } from '../../../object-collection/shar let component: ClaimedTaskActionsRejectComponent; let fixture: ComponentFixture; -let formBuilder: FormBuilder; +let formBuilder: UntypedFormBuilder; let modalService: NgbModal; const searchService = getMockSearchService(); @@ -63,7 +63,7 @@ describe('ClaimedTaskActionsRejectComponent', () => { { provide: SearchService, useValue: searchService }, { provide: RequestService, useValue: requestService }, { provide: PoolTaskDataService, useValue: mockPoolTaskDataService }, - FormBuilder, + UntypedFormBuilder, NgbModal, ], schemas: [NO_ERRORS_SCHEMA] @@ -72,7 +72,7 @@ describe('ClaimedTaskActionsRejectComponent', () => { }).compileComponents(); fixture = TestBed.createComponent(ClaimedTaskActionsRejectComponent); component = fixture.componentInstance; - formBuilder = TestBed.inject(FormBuilder); + formBuilder = TestBed.inject(UntypedFormBuilder); modalService = TestBed.inject(NgbModal); component.object = object; component.modalRef = modalService.open('ok'); @@ -82,7 +82,7 @@ describe('ClaimedTaskActionsRejectComponent', () => { it('should init reject form properly', () => { expect(component.rejectForm).toBeDefined(); - expect(component.rejectForm instanceof FormGroup).toBeTruthy(); + expect(component.rejectForm instanceof UntypedFormGroup).toBeTruthy(); expect(component.rejectForm.controls.reason).toBeDefined(); }); diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts index 31514613ce..7d68af1b8f 100644 --- a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts @@ -1,5 +1,5 @@ import { Component, Injector, OnInit } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; @@ -34,7 +34,7 @@ export class ClaimedTaskActionsRejectComponent extends ClaimedTaskActionsAbstrac /** * The reject form group */ - public rejectForm: FormGroup; + public rejectForm: UntypedFormGroup; /** * Reference to NgbModal @@ -47,7 +47,7 @@ export class ClaimedTaskActionsRejectComponent extends ClaimedTaskActionsAbstrac protected translate: TranslateService, protected searchService: SearchService, protected requestService: RequestService, - private formBuilder: FormBuilder, + private formBuilder: UntypedFormBuilder, private modalService: NgbModal) { super(injector, router, notificationsService, translate, searchService, requestService); } diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts index 6a14aeb5bc..c0dc1cad02 100644 --- a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts @@ -3,20 +3,21 @@ import { ComponentFactoryResolver, EventEmitter, Input, - OnDestroy, OnInit, Output, - ViewChild + ViewChild, + OnChanges, + SimpleChanges, + ComponentRef, } from '@angular/core'; import { getComponentByWorkflowTaskOption } from './claimed-task-actions-decorator'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; import { ClaimedTaskActionsDirective } from './claimed-task-actions.directive'; -import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; -import { hasValue } from '../../../empty.util'; -import { Subscription } from 'rxjs'; +import { hasValue, isNotEmpty, hasNoValue } from '../../../empty.util'; import { MyDSpaceActionsResult } from '../../mydspace-actions'; import { Item } from '../../../../core/shared/item.model'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; +import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; @Component({ selector: 'ds-claimed-task-actions-loader', @@ -26,7 +27,7 @@ import { WorkflowItem } from '../../../../core/submission/models/workflowitem.mo * Component for loading a ClaimedTaskAction component depending on the "option" input * Passes on the ClaimedTask to the component and subscribes to the processCompleted output */ -export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy { +export class ClaimedTaskActionsLoaderComponent implements OnInit, OnChanges { /** * The item object that belonging to the ClaimedTask object */ @@ -59,10 +60,18 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy { @ViewChild(ClaimedTaskActionsDirective, {static: true}) claimedTaskActionsDirective: ClaimedTaskActionsDirective; /** - * Array to track all subscriptions and unsubscribe them onDestroy - * @type {Array} + * The reference to the dynamic component */ - protected subs: Subscription[] = []; + protected compRef: ComponentRef; + + /** + * The list of input and output names for the dynamic component + */ + protected inAndOutputNames: (keyof ClaimedTaskActionsAbstractComponent & keyof this)[] = [ + 'object', + 'option', + 'processCompleted', + ]; constructor(private componentFactoryResolver: ComponentFactoryResolver) { } @@ -71,7 +80,29 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy { * Fetch, create and initialize the relevant component */ ngOnInit(): void { + this.instantiateComponent(); + } + /** + * Whenever the inputs change, update the inputs of the dynamic component + */ + ngOnChanges(changes: SimpleChanges): void { + if (hasNoValue(this.compRef)) { + // sometimes the component has not been initialized yet, so it first needs to be initialized + // before being called again + this.instantiateComponent(changes); + } else { + // if an input or output has changed + if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) { + this.connectInputsAndOutputs(); + if (this.compRef?.instance && 'ngOnChanges' in this.compRef.instance) { + (this.compRef.instance as any).ngOnChanges(changes); + } + } + } + } + + private instantiateComponent(changes?: SimpleChanges): void { const comp = this.getComponentByWorkflowTaskOption(this.option); if (hasValue(comp)) { const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp); @@ -79,13 +110,12 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy { const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef; viewContainerRef.clear(); - const componentRef = viewContainerRef.createComponent(componentFactory); - const componentInstance = (componentRef.instance as ClaimedTaskActionsAbstractComponent); - componentInstance.item = this.item; - componentInstance.object = this.object; - componentInstance.workflowitem = this.workflowitem; - if (hasValue(componentInstance.processCompleted)) { - this.subs.push(componentInstance.processCompleted.subscribe((result) => this.processCompleted.emit(result))); + this.compRef = viewContainerRef.createComponent(componentFactory); + + if (hasValue(changes)) { + this.ngOnChanges(changes); + } else { + this.connectInputsAndOutputs(); } } } @@ -95,11 +125,14 @@ export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy { } /** - * Unsubscribe from open subscriptions + * Connect the in and outputs of this component to the dynamic component, + * to ensure they're in sync */ - ngOnDestroy(): void { - this.subs - .filter((subscription) => hasValue(subscription)) - .forEach((subscription) => subscription.unsubscribe()); + protected connectInputsAndOutputs(): void { + if (isNotEmpty(this.inAndOutputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { + this.inAndOutputNames.filter((name: any) => this[name] !== undefined).forEach((name: any) => { + this.compRef.instance[name] = this[name]; + }); + } } } diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html index f789f4df47..6e958c7a8b 100644 --- a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html +++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html @@ -7,7 +7,8 @@ {{"submission.workspace.generic.view" | translate}} - @@ -15,6 +16,7 @@
- \ No newline at end of file + diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts index 14d3c07650..c76cce3982 100644 --- a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts +++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts @@ -1,3 +1,4 @@ +import { EPerson } from '../../../core/eperson/models/eperson.model'; import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { Router } from '@angular/router'; @@ -24,12 +25,16 @@ import { RequestService } from '../../../core/data/request.service'; import { getMockRequestService } from '../../mocks/request.service.mock'; import { getMockSearchService } from '../../mocks/search-service.mock'; import { SearchService } from '../../../core/shared/search/search.service'; +import { AuthService } from '../../../core/auth/auth.service'; +import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; let component: WorkspaceitemActionsComponent; let fixture: ComponentFixture; let mockObject: WorkspaceItem; let notificationsServiceStub: NotificationsServiceStub; +let authorizationService; +let authService; const mockDataService = jasmine.createSpyObj('WorkspaceitemDataService', { delete: jasmine.createSpy('delete') @@ -71,10 +76,91 @@ const item = Object.assign(new Item(), { const rd = createSuccessfulRemoteDataObject(item); mockObject = Object.assign(new WorkspaceItem(), { item: observableOf(rd), id: '1234', uuid: '1234' }); -describe('WorkspaceitemActionsComponent', () => { - beforeEach(waitForAsync(() => { +const ePersonMock: EPerson = Object.assign(new EPerson(), { + handle: null, + netid: null, + lastActive: '2023-04-27T12:15:57.054+00:00', + canLogIn: true, + email: 'dspacedemo+submit@gmail.com', + requireCertificate: false, + selfRegistered: false, + _name: 'dspacedemo+submit@gmail.com', + id: '914955b1-cf2e-4884-8af7-a166aa24cf73', + uuid: '914955b1-cf2e-4884-8af7-a166aa24cf73', + type: 'eperson', + metadata: { + 'dspace.agreements.cookies': [ + { + uuid: '0a53a0f2-e168-4ed9-b4af-cba9a2d267ca', + language: null, + value: + '{"authentication":true,"preferences":true,"acknowledgement":true,"google-analytics":true}', + place: 0, + authority: null, + confidence: -1, + }, + ], + 'dspace.agreements.end-user': [ + { + uuid: '0879e571-6e4a-4efe-af9b-704c755166be', + language: null, + value: 'true', + place: 0, + authority: null, + confidence: -1, + }, + ], + 'eperson.firstname': [ + { + uuid: '18052a3e-f19b-49ca-b9f9-ee4cf9c71b86', + language: null, + value: 'Demo', + place: 0, + authority: null, + confidence: -1, + }, + ], + 'eperson.language': [ + { + uuid: '98c2abdb-6a6f-4b41-b455-896bcf333ca3', + language: null, + value: 'en', + place: 0, + authority: null, + confidence: -1, + }, + ], + 'eperson.lastname': [ + { + uuid: 'df722e70-9497-468d-a92a-4038e7ef2586', + language: null, + value: 'Submitter', + place: 0, + authority: null, + confidence: -1, + }, + ], + }, + _links: { + groups: { + href: 'http://localhost:8080/server/api/eperson/epersons/914955b1-cf2e-4884-8af7-a166aa24cf73/groups', + }, + self: { + href: 'http://localhost:8080/server/api/eperson/epersons/914955b1-cf2e-4884-8af7-a166aa24cf73', + }, + }, +}); - TestBed.configureTestingModule({ +authService = jasmine.createSpyObj('authService', { + getAuthenticatedUserFromStore: jasmine.createSpy('getAuthenticatedUserFromStore') +}); + +describe('WorkspaceitemActionsComponent', () => { + beforeEach(waitForAsync(async () => { + authorizationService = jasmine.createSpyObj('authorizationService', { + isAuthorized: observableOf(true) + }); + await TestBed.configureTestingModule({ imports: [ NgbModule, TranslateModule.forRoot({ @@ -92,6 +178,8 @@ describe('WorkspaceitemActionsComponent', () => { { provide: WorkspaceitemDataService, useValue: mockDataService }, { provide: SearchService, useValue: searchService }, { provide: RequestService, useValue: requestServce }, + { provide: AuthService, useValue: authService }, + { provide: AuthorizationDataService, useValue: authorizationService}, NgbModal ], schemas: [NO_ERRORS_SCHEMA] @@ -105,6 +193,7 @@ describe('WorkspaceitemActionsComponent', () => { component = fixture.componentInstance; component.object = mockObject; notificationsServiceStub = TestBed.inject(NotificationsService as any); + (authService.getAuthenticatedUserFromStore as jasmine.Spy).and.returnValue(observableOf(ePersonMock)); fixture.detectChanges(); }); @@ -150,7 +239,6 @@ describe('WorkspaceitemActionsComponent', () => { confirmBtn.click(); fixture.detectChanges(); - fixture.whenStable().then(() => { done(); }); diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts index a6d30728ac..05afacf0da 100644 --- a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts +++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts @@ -1,7 +1,11 @@ -import { Component, Injector, Input } from '@angular/core'; +import { AuthorizationDataService } from 'src/app/core/data/feature-authorization/authorization-data.service'; +import { AuthService } from '../../../core/auth/auth.service'; +import { Item } from '../../../core/shared/item.model'; +import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; +import { Component, Injector, Input, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, Observable, switchMap } from 'rxjs'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; @@ -11,7 +15,7 @@ import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem import { NotificationsService } from '../../notifications/notifications.service'; import { RequestService } from '../../../core/data/request.service'; import { SearchService } from '../../../core/shared/search/search.service'; -import { getFirstCompletedRemoteData } from '../../../core/shared/operators'; +import { getFirstCompletedRemoteData, getRemoteDataPayload } from '../../../core/shared/operators'; import { RemoteData } from '../../../core/data/remote-data'; import { NoContent } from '../../../core/shared/NoContent.model'; import { getWorkspaceItemViewRoute } from '../../../workspaceitems-edit-page/workspaceitems-edit-page-routing-paths'; @@ -24,7 +28,7 @@ import { getWorkspaceItemViewRoute } from '../../../workspaceitems-edit-page/wor styleUrls: ['./workspaceitem-actions.component.scss'], templateUrl: './workspaceitem-actions.component.html', }) -export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent { +export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent implements OnInit { /** * The workspaceitem object @@ -37,6 +41,14 @@ export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent(false); + /** + * A boolean representing if the user can edit the item + * and therefore can delete it as well + * (since the user can discard the item also from the edit page) + * @type {Observable} + */ + canEditItem$: Observable; + /** * Initialize instance variables * @@ -54,8 +66,12 @@ export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent { + return this.object?.item.pipe( + getFirstCompletedRemoteData(), + getRemoteDataPayload(), + switchMap((item: Item) => { + return this.authorizationService.isAuthorized(FeatureID.CanEditItem, item?._links?.self.href, eperson.uuid); + }) + ) as Observable; + })); + } + /** * Init the target object * @@ -92,5 +124,4 @@ export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent -
+ {{ accessStatus | translate }} -
+ diff --git a/src/app/shared/object-list/access-status-badge/access-status-badge.component.spec.ts b/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.spec.ts similarity index 94% rename from src/app/shared/object-list/access-status-badge/access-status-badge.component.spec.ts rename to src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.spec.ts index 9101df2f4c..f661ed6e00 100644 --- a/src/app/shared/object-list/access-status-badge/access-status-badge.component.spec.ts +++ b/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.spec.ts @@ -1,10 +1,10 @@ -import { Item } from '../../../core/shared/item.model'; +import { Item } from '../../../../../core/shared/item.model'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; -import { TruncatePipe } from '../../utils/truncate.pipe'; +import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { AccessStatusBadgeComponent } from './access-status-badge.component'; -import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils'; +import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; import { By } from '@angular/platform-browser'; import { AccessStatusObject } from './access-status.model'; import { AccessStatusDataService } from 'src/app/core/data/access-status-data.service'; @@ -50,7 +50,8 @@ describe('ItemAccessStatusBadgeComponent', () => { }); item = Object.assign(new Item(), { - uuid: 'item-uuid' + uuid: 'item-uuid', + type: 'item' }); } @@ -69,7 +70,7 @@ describe('ItemAccessStatusBadgeComponent', () => { environment.item.showAccessStatuses = true; fixture = TestBed.createComponent(AccessStatusBadgeComponent); component = fixture.componentInstance; - component.item = item; + component.object = item; fixture.detectChanges(); environment.item.showAccessStatuses = false; } diff --git a/src/app/shared/object-list/access-status-badge/access-status-badge.component.ts b/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.ts similarity index 73% rename from src/app/shared/object-list/access-status-badge/access-status-badge.component.ts rename to src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.ts index fbca3cb971..2be44669b0 100644 --- a/src/app/shared/object-list/access-status-badge/access-status-badge.component.ts +++ b/src/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component.ts @@ -2,10 +2,12 @@ import { Component, Input } from '@angular/core'; import { catchError, map } from 'rxjs/operators'; import { Observable, of as observableOf } from 'rxjs'; import { AccessStatusObject } from './access-status.model'; -import { hasValue } from '../../empty.util'; +import { hasValue } from '../../../../empty.util'; import { environment } from 'src/environments/environment'; -import { Item } from 'src/app/core/shared/item.model'; import { AccessStatusDataService } from 'src/app/core/data/access-status-data.service'; +import { DSpaceObject } from '../../../../../core/shared/dspace-object.model'; +import { Item } from '../../../../../core/shared/item.model'; +import { ITEM } from '../../../../../core/shared/item.resource-type'; @Component({ selector: 'ds-access-status-badge', @@ -16,7 +18,7 @@ import { AccessStatusDataService } from 'src/app/core/data/access-status-data.se */ export class AccessStatusBadgeComponent { - @Input() item: Item; + @Input() object: DSpaceObject; accessStatus$: Observable; /** @@ -33,15 +35,17 @@ export class AccessStatusBadgeComponent { ngOnInit(): void { this.showAccessStatus = environment.item.showAccessStatuses; - if (!this.showAccessStatus || this.item == null) { + if (this.object.type.toString() !== ITEM.value || !this.showAccessStatus || this.object == null) { // Do not show the badge if the feature is inactive or if the item is null. return; } - if (this.item.accessStatus == null) { + + const item = this.object as Item; + if (item.accessStatus == null) { // In case the access status has not been loaded, do it individually. - this.item.accessStatus = this.accessStatusDataService.findAccessStatusFor(this.item); + item.accessStatus = this.accessStatusDataService.findAccessStatusFor(item); } - this.accessStatus$ = this.item.accessStatus.pipe( + this.accessStatus$ = item.accessStatus.pipe( map((accessStatusRD) => { if (accessStatusRD.statusCode !== 401 && hasValue(accessStatusRD.payload)) { return accessStatusRD.payload; diff --git a/src/app/shared/object-list/access-status-badge/access-status.model.ts b/src/app/shared/object-collection/shared/badges/access-status-badge/access-status.model.ts similarity index 100% rename from src/app/shared/object-list/access-status-badge/access-status.model.ts rename to src/app/shared/object-collection/shared/badges/access-status-badge/access-status.model.ts diff --git a/src/app/shared/object-list/access-status-badge/access-status.resource-type.ts b/src/app/shared/object-collection/shared/badges/access-status-badge/access-status.resource-type.ts similarity index 100% rename from src/app/shared/object-list/access-status-badge/access-status.resource-type.ts rename to src/app/shared/object-collection/shared/badges/access-status-badge/access-status.resource-type.ts diff --git a/src/app/shared/object-collection/shared/badges/access-status-badge/themed-access-status-badge.component.ts b/src/app/shared/object-collection/shared/badges/access-status-badge/themed-access-status-badge.component.ts new file mode 100644 index 0000000000..ad8dfd7cb0 --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/access-status-badge/themed-access-status-badge.component.ts @@ -0,0 +1,30 @@ +import { Component, Input } from '@angular/core'; +import { ThemedComponent } from '../../../../theme-support/themed.component'; +import { AccessStatusBadgeComponent } from './access-status-badge.component'; +import { DSpaceObject } from '../../../../../core/shared/dspace-object.model'; + +/** + * Themed wrapper for AccessStatusBadgeComponent + */ +@Component({ + selector: 'ds-themed-access-status-badge', + styleUrls: [], + templateUrl: '../../../../theme-support/themed.component.html', +}) +export class ThemedAccessStatusBadgeComponent extends ThemedComponent { + @Input() object: DSpaceObject; + + protected inAndOutputNames: (keyof AccessStatusBadgeComponent & keyof this)[] = ['object']; + + protected getComponentName(): string { + return 'AccessStatusBadgeComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../../../../themes/${themeName}/app/shared/object-collection/shared/badges/access-status-badge/access-status-badge.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./access-status-badge.component`); + } +} diff --git a/src/app/shared/object-collection/shared/badges/badges.component.html b/src/app/shared/object-collection/shared/badges/badges.component.html new file mode 100644 index 0000000000..7fc08751aa --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/badges.component.html @@ -0,0 +1,10 @@ + + + + +
+ + + + +
diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.scss b/src/app/shared/object-collection/shared/badges/badges.component.scss similarity index 100% rename from src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.scss rename to src/app/shared/object-collection/shared/badges/badges.component.scss diff --git a/src/app/shared/object-collection/shared/badges/badges.component.spec.ts b/src/app/shared/object-collection/shared/badges/badges.component.spec.ts new file mode 100644 index 0000000000..9e0c277a08 --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/badges.component.spec.ts @@ -0,0 +1,30 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BadgesComponent } from './badges.component'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ThemeService } from '../../../theme-support/theme.service'; +import { getMockThemeService } from '../../../mocks/theme-service.mock'; + +describe('BadgesComponent', () => { + let component: BadgesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ BadgesComponent ], + providers: [{provide: ThemeService, useValue: getMockThemeService()}], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BadgesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/object-collection/shared/badges/badges.component.ts b/src/app/shared/object-collection/shared/badges/badges.component.ts new file mode 100644 index 0000000000..c66fed35fc --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/badges.component.ts @@ -0,0 +1,47 @@ +import { Component, Input } from '@angular/core'; +import { Context } from 'src/app/core/shared/context.model'; +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; + +/** + * List of MyDSpace Status Contexts + */ +const MY_DSPACE_STATUS_CONTEXTS = [ + Context.MyDSpaceArchived, + Context.MyDSpaceWorkspace, + Context.MyDSpaceWorkflow, + Context.MyDSpaceDeclined, + Context.MyDSpaceApproved, + Context.MyDSpaceWaitingController, + Context.MyDSpaceValidation +]; + +/** + * Component that renders all the badges for a listable object + */ +@Component({ + selector: 'ds-badges', + templateUrl: './badges.component.html', + styleUrls: ['./badges.component.scss'] +}) +export class BadgesComponent { + /** + * The DSpaceObject to render the badge for + */ + @Input() object: DSpaceObject; + /** + * The context that the badge is rendered in + */ + @Input() context?: Context; + + /** + * Whether or not to show the access status + */ + @Input() showAccessStatus = false; + + /** + * Returns whether or not this context is a MyDSpace status context + */ + get isMyDSpaceStatus(): boolean { + return MY_DSPACE_STATUS_CONTEXTS.includes(this.context); + } +} diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status-type.ts b/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-item-status-type.ts similarity index 100% rename from src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status-type.ts rename to src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-item-status-type.ts diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.html b/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.html similarity index 50% rename from src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.html rename to src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.html index 848dd502a4..4cf93cbd1d 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.html +++ b/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.html @@ -1,5 +1,5 @@
- {{badgeContent | translate}} + {{('mydspace.status.' + badgeContent) | translate}}
diff --git a/src/themes/custom/app/shared/object-list/collection-list-element/collection-list-element.component.html b/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.scss similarity index 100% rename from src/themes/custom/app/shared/object-list/collection-list-element/collection-list-element.component.html rename to src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.scss diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.spec.ts b/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.spec.ts similarity index 59% rename from src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.spec.ts rename to src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.spec.ts index 89bceea40d..65fe5b7ef1 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.spec.ts +++ b/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.spec.ts @@ -4,17 +4,17 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { of as observableOf } from 'rxjs'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; -import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model'; -import { EPersonMock } from '../../../testing/eperson.mock'; -import { MyDSpaceItemStatusComponent } from './my-dspace-item-status.component'; -import { MyDspaceItemStatusType } from './my-dspace-item-status-type'; -import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; +import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model'; +import { PoolTask } from '../../../../../core/tasks/models/pool-task-object.model'; +import { EPersonMock } from '../../../../testing/eperson.mock'; +import { MyDSpaceStatusBadgeComponent } from './my-dspace-status-badge.component'; +import { TranslateLoaderMock } from '../../../../mocks/translate-loader.mock'; import { By } from '@angular/platform-browser'; -import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils'; +import { createSuccessfulRemoteDataObject } from '../../../../remote-data.utils'; +import { Context } from '../../../../../core/shared/context.model'; -let component: MyDSpaceItemStatusComponent; -let fixture: ComponentFixture; +let component: MyDSpaceStatusBadgeComponent; +let fixture: ComponentFixture; let mockResultObject: PoolTask; @@ -34,15 +34,15 @@ describe('MyDSpaceItemStatusComponent', () => { } }) ], - declarations: [MyDSpaceItemStatusComponent], + declarations: [MyDSpaceStatusBadgeComponent], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(MyDSpaceItemStatusComponent, { + }).overrideComponent(MyDSpaceStatusBadgeComponent, { set: { changeDetection: ChangeDetectionStrategy.Default } }).compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(MyDSpaceItemStatusComponent); + fixture = TestBed.createComponent(MyDSpaceStatusBadgeComponent); component = fixture.componentInstance; }); @@ -52,37 +52,37 @@ describe('MyDSpaceItemStatusComponent', () => { }); it('should init badge content and class', () => { - component.status = MyDspaceItemStatusType.VALIDATION; + component.context = Context.MyDSpaceValidation; fixture.detectChanges(); - expect(component.badgeContent).toBe(MyDspaceItemStatusType.VALIDATION); + expect(component.badgeContent).toBe(Context.MyDSpaceValidation); expect(component.badgeClass).toBe('text-light badge badge-validation'); }); it('should init badge content and class', () => { - component.status = MyDspaceItemStatusType.WAITING_CONTROLLER; + component.context = Context.MyDSpaceWaitingController; fixture.detectChanges(); - expect(component.badgeContent).toBe(MyDspaceItemStatusType.WAITING_CONTROLLER); + expect(component.badgeContent).toBe(Context.MyDSpaceWaitingController); expect(component.badgeClass).toBe('text-light badge badge-waiting-controller'); }); it('should init badge content and class', () => { - component.status = MyDspaceItemStatusType.WORKSPACE; + component.context = Context.MyDSpaceWorkspace; fixture.detectChanges(); - expect(component.badgeContent).toBe(MyDspaceItemStatusType.WORKSPACE); + expect(component.badgeContent).toBe(Context.MyDSpaceWorkspace); expect(component.badgeClass).toBe('text-light badge badge-workspace'); }); it('should init badge content and class', () => { - component.status = MyDspaceItemStatusType.ARCHIVED; + component.context = Context.MyDSpaceArchived; fixture.detectChanges(); - expect(component.badgeContent).toBe(MyDspaceItemStatusType.ARCHIVED); + expect(component.badgeContent).toBe(Context.MyDSpaceArchived); expect(component.badgeClass).toBe('text-light badge badge-archived'); }); it('should init badge content and class', () => { - component.status = MyDspaceItemStatusType.WORKFLOW; + component.context = Context.MyDSpaceWorkflow; fixture.detectChanges(); - expect(component.badgeContent).toBe(MyDspaceItemStatusType.WORKFLOW); + expect(component.badgeContent).toBe(Context.MyDSpaceWorkflow); expect(component.badgeClass).toBe('text-light badge badge-workflow'); }); }); diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.ts b/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.ts similarity index 52% rename from src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.ts rename to src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.ts index 83b2656fbd..744a927812 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.ts +++ b/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component.ts @@ -1,20 +1,20 @@ import { Component, Input, OnInit } from '@angular/core'; -import { MyDspaceItemStatusType } from './my-dspace-item-status-type'; +import { Context } from 'src/app/core/shared/context.model'; /** * This component represents a badge with mydspace item status */ @Component({ - selector: 'ds-mydspace-item-status', - styleUrls: ['./my-dspace-item-status.component.scss'], - templateUrl: './my-dspace-item-status.component.html' + selector: 'ds-my-dspace-status-badge', + styleUrls: ['./my-dspace-status-badge.component.scss'], + templateUrl: './my-dspace-status-badge.component.html' }) -export class MyDSpaceItemStatusComponent implements OnInit { +export class MyDSpaceStatusBadgeComponent implements OnInit { /** - * This mydspace item status + * This mydspace item context */ - @Input() status: MyDspaceItemStatusType; + @Input() context: Context; /** * This badge class @@ -30,22 +30,22 @@ export class MyDSpaceItemStatusComponent implements OnInit { * Initialize badge content and class */ ngOnInit() { - this.badgeContent = this.status; + this.badgeContent = this.context; this.badgeClass = 'text-light badge '; - switch (this.status) { - case MyDspaceItemStatusType.VALIDATION: + switch (this.context) { + case Context.MyDSpaceValidation: this.badgeClass += 'badge-validation'; break; - case MyDspaceItemStatusType.WAITING_CONTROLLER: + case Context.MyDSpaceWaitingController: this.badgeClass += 'badge-waiting-controller'; break; - case MyDspaceItemStatusType.WORKSPACE: + case Context.MyDSpaceWorkspace: this.badgeClass += 'badge-workspace'; break; - case MyDspaceItemStatusType.ARCHIVED: + case Context.MyDSpaceArchived: this.badgeClass += 'badge-archived'; break; - case MyDspaceItemStatusType.WORKFLOW: + case Context.MyDSpaceWorkflow: this.badgeClass += 'badge-workflow'; break; } diff --git a/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/themed-my-dspace-status-badge.component.ts b/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/themed-my-dspace-status-badge.component.ts new file mode 100644 index 0000000000..4c375746c8 --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/my-dspace-status-badge/themed-my-dspace-status-badge.component.ts @@ -0,0 +1,30 @@ +import { Component, Input } from '@angular/core'; +import { Context } from 'src/app/core/shared/context.model'; +import { ThemedComponent } from '../../../../theme-support/themed.component'; +import { MyDSpaceStatusBadgeComponent } from './my-dspace-status-badge.component'; + +/** + * Themed wrapper for MyDSpaceStatusBadge + */ +@Component({ + selector: 'ds-themed-my-dspace-status-badge', + styleUrls: [], + templateUrl: '../../../../theme-support/themed.component.html', +}) +export class ThemedMyDSpaceStatusBadgeComponent extends ThemedComponent { + @Input() context: Context; + + protected inAndOutputNames: (keyof MyDSpaceStatusBadgeComponent & keyof this)[] = ['context']; + + protected getComponentName(): string { + return 'MyDSpaceStatusBadgeComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../../../../themes/${themeName}/app/shared/object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./my-dspace-status-badge.component`); + } +} diff --git a/src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.html b/src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.html new file mode 100644 index 0000000000..c864f22afa --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.html @@ -0,0 +1,6 @@ +
+ {{ "item.badge.private" | translate }} +
+
+ {{ "item.badge.withdrawn" | translate }} +
diff --git a/src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.spec.ts b/src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.spec.ts new file mode 100644 index 0000000000..f3115073c3 --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.spec.ts @@ -0,0 +1,91 @@ +import { Item } from '../../../../../core/shared/item.model'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { TruncatePipe } from '../../../../utils/truncate.pipe'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { StatusBadgeComponent } from './status-badge.component'; + +let comp: StatusBadgeComponent; +let fixture: ComponentFixture; + +let withdrawnItem = Object.assign(new Item(), { isWithdrawn: true }); +let notWithdrawnItem = Object.assign(new Item(), { isWithdrawn: false }); +let privateItem = Object.assign(new Item(), { isDiscoverable: false }); +let notPrivateItem = Object.assign(new Item(), { isDiscoverable: true }); + +describe('ItemStatusBadgeComponent', () => { + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [StatusBadgeComponent, TruncatePipe], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(StatusBadgeComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + init(); + })); + + function init() { + withdrawnItem = Object.assign(new Item(), { isWithdrawn: true }); + notWithdrawnItem = Object.assign(new Item(), { isWithdrawn: false }); + privateItem = Object.assign(new Item(), { isDiscoverable: false }); + notPrivateItem = Object.assign(new Item(), { isDiscoverable: true }); + } + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(StatusBadgeComponent); + comp = fixture.componentInstance; + })); + + + describe('when the item is not withdrawn', () => { + beforeEach(() => { + comp.object = notWithdrawnItem; + comp.ngOnInit(); + fixture.detectChanges(); + }); + + it('should not show the withdrawn badge', () => { + const badge = fixture.debugElement.query(By.css('div.withdrawn-badge')); + expect(badge).toBeNull(); + }); + }); + + describe('when the item is withdrawn', () => { + beforeEach(() => { + comp.object = withdrawnItem; + comp.ngOnInit(); + fixture.detectChanges(); + }); + + it('should show the withdrawn badge', () => { + const badge = fixture.debugElement.query(By.css('div.withdrawn-badge')); + expect(badge).not.toBeNull(); + }); + }); + + describe('when the item is not private', () => { + beforeEach(() => { + comp.object = notPrivateItem; + comp.ngOnInit(); + fixture.detectChanges(); + }); + it('should not show the private badge', () => { + const badge = fixture.debugElement.query(By.css('div.private-badge')); + expect(badge).toBeNull(); + }); + }); + + describe('when the item is private', () => { + beforeEach(() => { + comp.object = privateItem; + comp.ngOnInit(); + fixture.detectChanges(); + }); + + it('should show the private badge', () => { + const badge = fixture.debugElement.query(By.css('div.private-badge')); + expect(badge).not.toBeNull(); + }); + }); +}); diff --git a/src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.ts b/src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.ts new file mode 100644 index 0000000000..a52f3f6f32 --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/status-badge/status-badge.component.ts @@ -0,0 +1,41 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { DSpaceObject } from '../../../../../core/shared/dspace-object.model'; +import { hasValue } from '../../../../empty.util'; + +@Component({ + selector: 'ds-status-badge', + templateUrl: './status-badge.component.html' +}) +/** + * Component rendering the status of an item as a badge + */ +export class StatusBadgeComponent implements OnInit { + + /** + * The component used to retrieve the status from + */ + @Input() object: DSpaceObject; + + /** + * Whether or not the "Private" badge should be displayed for this listable object + */ + privateBadge = false; + + /** + * Whether or not the "Withdrawn" badge should be displayed for this listable object + */ + withdrawnBadge = false; + + /** + * Initialize which badges should be visible + */ + ngOnInit(): void { + let objectAsAny = this.object as any; + if (hasValue(objectAsAny.indexableObject)) { + objectAsAny = objectAsAny.indexableObject; + } + const objectExists = hasValue(objectAsAny); + this.privateBadge = objectExists && hasValue(objectAsAny.isDiscoverable) && !objectAsAny.isDiscoverable; + this.withdrawnBadge = objectExists && hasValue(objectAsAny.isWithdrawn) && objectAsAny.isWithdrawn; + } +} diff --git a/src/app/shared/object-collection/shared/badges/status-badge/themed-status-badge.component.ts b/src/app/shared/object-collection/shared/badges/status-badge/themed-status-badge.component.ts new file mode 100644 index 0000000000..f50d029fff --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/status-badge/themed-status-badge.component.ts @@ -0,0 +1,30 @@ +import { Component, Input } from '@angular/core'; +import { ThemedComponent } from '../../../../theme-support/themed.component'; +import { StatusBadgeComponent } from './status-badge.component'; +import { DSpaceObject } from '../../../../../core/shared/dspace-object.model'; + +/** + * Themed wrapper for StatusBadgeComponent + */ +@Component({ + selector: 'ds-themed-status-badge', + styleUrls: [], + templateUrl: '../../../../theme-support/themed.component.html', +}) +export class ThemedStatusBadgeComponent extends ThemedComponent { + @Input() object: DSpaceObject; + + protected inAndOutputNames: (keyof StatusBadgeComponent & keyof this)[] = ['object']; + + protected getComponentName(): string { + return 'StatusBadgeComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../../../../themes/${themeName}/app/shared/object-collection/shared/badges/status-badge/status-badge.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./status-badge.component`); + } +} diff --git a/src/app/shared/object-collection/shared/badges/themed-badges.component.ts b/src/app/shared/object-collection/shared/badges/themed-badges.component.ts new file mode 100644 index 0000000000..1671b0c5b0 --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/themed-badges.component.ts @@ -0,0 +1,33 @@ +import { Component, Input } from '@angular/core'; +import { BadgesComponent } from './badges.component'; +import { ThemedComponent } from '../../../theme-support/themed.component'; +import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; +import { Context } from 'src/app/core/shared/context.model'; + +/** + * Themed wrapper for BadgesComponent + */ +@Component({ + selector: 'ds-themed-badges', + styleUrls: [], + templateUrl: '../../../theme-support/themed.component.html', +}) +export class ThemedBadgesComponent extends ThemedComponent { + @Input() object: DSpaceObject; + @Input() context: Context; + @Input() showAccessStatus = false; + + protected inAndOutputNames: (keyof BadgesComponent & keyof this)[] = ['object', 'context', 'showAccessStatus']; + + protected getComponentName(): string { + return 'BadgesComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../../../themes/${themeName}/app/shared/object-collection/shared/badges/badges.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./badges.component`); + } +} diff --git a/src/app/shared/object-collection/shared/badges/type-badge/themed-type-badge.component.ts b/src/app/shared/object-collection/shared/badges/type-badge/themed-type-badge.component.ts new file mode 100644 index 0000000000..a8dd0ee04c --- /dev/null +++ b/src/app/shared/object-collection/shared/badges/type-badge/themed-type-badge.component.ts @@ -0,0 +1,30 @@ +import { Component, Input } from '@angular/core'; +import { ThemedComponent } from '../../../../theme-support/themed.component'; +import { TypeBadgeComponent } from './type-badge.component'; +import { DSpaceObject } from '../../../../../core/shared/dspace-object.model'; + +/** + * Themed wrapper for TypeBadgeComponent + */ +@Component({ + selector: 'ds-themed-type-badge', + styleUrls: [], + templateUrl: '../../../../theme-support/themed.component.html', +}) +export class ThemedTypeBadgeComponent extends ThemedComponent { + @Input() object: DSpaceObject; + + protected inAndOutputNames: (keyof TypeBadgeComponent & keyof this)[] = ['object']; + + protected getComponentName(): string { + return 'TypeBadgeComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../../../../themes/${themeName}/app/shared/object-collection/shared/badges/type-badge/type-badge.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./type-badge.component`); + } +} diff --git a/src/app/shared/object-list/type-badge/type-badge.component.html b/src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.html similarity index 66% rename from src/app/shared/object-list/type-badge/type-badge.component.html rename to src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.html index 0c2bd7544e..119702c375 100644 --- a/src/app/shared/object-list/type-badge/type-badge.component.html +++ b/src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.html @@ -1,3 +1,3 @@ -
+ {{ typeMessage | translate }} -
+ diff --git a/src/app/shared/object-list/type-badge/type-badge.component.spec.ts b/src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.spec.ts similarity index 94% rename from src/app/shared/object-list/type-badge/type-badge.component.spec.ts rename to src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.spec.ts index 9a7e22c551..87adc3761a 100644 --- a/src/app/shared/object-list/type-badge/type-badge.component.spec.ts +++ b/src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.spec.ts @@ -1,8 +1,8 @@ -import { Item } from '../../../core/shared/item.model'; +import { Item } from '../../../../../core/shared/item.model'; import { of as observableOf } from 'rxjs'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; -import { TruncatePipe } from '../../utils/truncate.pipe'; +import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { TypeBadgeComponent } from './type-badge.component'; diff --git a/src/app/shared/object-list/type-badge/type-badge.component.ts b/src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.ts similarity index 83% rename from src/app/shared/object-list/type-badge/type-badge.component.ts rename to src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.ts index 8e843af1e7..ea912063b6 100644 --- a/src/app/shared/object-list/type-badge/type-badge.component.ts +++ b/src/app/shared/object-collection/shared/badges/type-badge/type-badge.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; -import { DSpaceObject } from '../../../core/shared/dspace-object.model'; -import { hasValue, isEmpty } from '../../empty.util'; -import { getResourceTypeValueFor } from '../../../core/cache/object-cache.reducer'; +import { DSpaceObject } from '../../../../../core/shared/dspace-object.model'; +import { hasValue, isEmpty } from '../../../../empty.util'; +import { getResourceTypeValueFor } from '../../../../../core/cache/object-cache.reducer'; @Component({ selector: 'ds-type-badge', diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.html b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.html index 9ed4e9eaa8..58561f0277 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.html +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.html @@ -1,9 +1 @@ -
-
- {{ "item.badge.private" | translate }} -
-
- {{ "item.badge.withdrawn" | translate }} -
-
diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts index ff88256f6a..e893fe807b 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.spec.ts @@ -11,7 +11,6 @@ import { import { ListableObjectDirective } from './listable-object.directive'; import { TranslateModule } from '@ngx-translate/core'; import { By } from '@angular/platform-browser'; -import { Item } from '../../../../core/shared/item.model'; import { provideMockStore } from '@ngrx/store/testing'; import { ThemeService } from '../../../theme-support/theme.service'; @@ -74,64 +73,6 @@ describe('ListableObjectComponentLoaderComponent', () => { }); }); - describe('when the object is an item and viewMode is a list', () => { - beforeEach(() => { - comp.object = Object.assign(new Item()); - comp.viewMode = ViewMode.ListElement; - }); - - describe('when the item is not withdrawn', () => { - beforeEach(() => { - (comp.object as any).isWithdrawn = false; - comp.initBadges(); - fixture.detectChanges(); - }); - - it('should not show the withdrawn badge', () => { - const badge = fixture.debugElement.query(By.css('div.withdrawn-badge')); - expect(badge).toBeNull(); - }); - }); - - describe('when the item is withdrawn', () => { - beforeEach(() => { - (comp.object as any).isWithdrawn = true; - comp.initBadges(); - fixture.detectChanges(); - }); - - it('should show the withdrawn badge', () => { - const badge = fixture.debugElement.query(By.css('div.withdrawn-badge')); - expect(badge).not.toBeNull(); - }); - }); - - describe('when the item is not private', () => { - beforeEach(() => { - (comp.object as any).isDiscoverable = true; - comp.initBadges(); - fixture.detectChanges(); - }); - it('should not show the private badge', () => { - const badge = fixture.debugElement.query(By.css('div.private-badge')); - expect(badge).toBeNull(); - }); - }); - - describe('when the item is private', () => { - beforeEach(() => { - (comp.object as any).isDiscoverable = false; - comp.initBadges(); - fixture.detectChanges(); - }); - - it('should show the private badge', () => { - const badge = fixture.debugElement.query(By.css('div.private-badge')); - expect(badge).not.toBeNull(); - }); - }); - }); - describe('When a reloadedObject is emitted', () => { let listableComponent; let reloadedObject: any; @@ -150,7 +91,7 @@ describe('ListableObjectComponentLoaderComponent', () => { (listableComponent as any).reloadedObject.emit(reloadedObject); tick(200); - expect((comp as any).instantiateComponent).toHaveBeenCalledWith(reloadedObject); + expect((comp as any).instantiateComponent).toHaveBeenCalledWith(reloadedObject, undefined); })); it('should re-emit it as a contentChange', fakeAsync(() => { diff --git a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts index 6b75c59181..6d2307a7f0 100644 --- a/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts +++ b/src/app/shared/object-collection/shared/listable-object/listable-object-component-loader.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectorRef, Component, ComponentRef, - ElementRef, EventEmitter, Input, OnChanges, @@ -13,7 +12,7 @@ import { ViewChild } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { Subscription, combineLatest, of as observableOf, Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { ListableObject } from '../listable-object.model'; @@ -23,7 +22,7 @@ import { getListableObjectComponent } from './listable-object.decorator'; import { GenericConstructor } from '../../../../core/shared/generic-constructor'; import { ListableObjectDirective } from './listable-object.directive'; import { CollectionElementLinkType } from '../../collection-element-link.type'; -import { hasValue, isNotEmpty } from '../../../empty.util'; +import { hasValue, isNotEmpty, hasNoValue } from '../../../empty.util'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { ThemeService } from '../../../theme-support/theme.service'; @@ -76,37 +75,16 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges */ @Input() value: string; - /** - * Whether or not informational badges (e.g. Private, Withdrawn) should be hidden - */ - @Input() hideBadges = false; - /** * Directive hook used to place the dynamic child component */ @ViewChild(ListableObjectDirective, { static: true }) listableObjectDirective: ListableObjectDirective; - /** - * View on the badges template, to be passed on to the loaded component (which will place the badges in the desired - * location, or on top if not specified) - */ - @ViewChild('badges', { static: true }) badges: ElementRef; - /** * Emit when the listable object has been reloaded. */ @Output() contentChange = new EventEmitter(); - /** - * Whether or not the "Private" badge should be displayed for this listable object - */ - privateBadge = false; - - /** - * Whether or not the "Withdrawn" badge should be displayed for this listable object - */ - withdrawnBadge = false; - /** * Array to track all subscriptions and unsubscribe them onDestroy * @type {Array} @@ -148,8 +126,18 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges * Whenever the inputs change, update the inputs of the dynamic component */ ngOnChanges(changes: SimpleChanges): void { - if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) { - this.connectInputsAndOutputs(); + if (hasNoValue(this.compRef)) { + // sometimes the component has not been initialized yet, so it first needs to be initialized + // before being called again + this.instantiateComponent(this.object, changes); + } else { + // if an input or output has changed + if (this.inAndOutputNames.some((name: any) => hasValue(changes[name]))) { + this.connectInputsAndOutputs(); + if (this.compRef?.instance && 'ngOnChanges' in this.compRef.instance) { + (this.compRef.instance as any).ngOnChanges(changes); + } + } } } @@ -159,9 +147,7 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges .forEach((subscription) => subscription.unsubscribe()); } - private instantiateComponent(object) { - - this.initBadges(); + private instantiateComponent(object: ListableObject, changes?: SimpleChanges): void { const component = this.getComponent(object.getRenderTypes(), this.viewMode, this.context); @@ -171,23 +157,25 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges this.compRef = viewContainerRef.createComponent( component, { index: 0, - injector: undefined, - projectableNodes: [ - [this.badges.nativeElement], - ] + injector: undefined } ); - this.connectInputsAndOutputs(); + if (hasValue(changes)) { + this.ngOnChanges(changes); + } else { + this.connectInputsAndOutputs(); + } if ((this.compRef.instance as any).reloadedObject) { - (this.compRef.instance as any).reloadedObject.pipe( - take(1) - ).subscribe((reloadedObject: DSpaceObject) => { + combineLatest([ + observableOf(changes), + (this.compRef.instance as any).reloadedObject.pipe(take(1)) as Observable, + ]).subscribe(([simpleChanges, reloadedObject]: [SimpleChanges, DSpaceObject]) => { if (reloadedObject) { this.compRef.destroy(); this.object = reloadedObject; - this.instantiateComponent(reloadedObject); + this.instantiateComponent(reloadedObject, simpleChanges); this.cdr.detectChanges(); this.contentChange.emit(reloadedObject); } @@ -195,19 +183,6 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges } } - /** - * Initialize which badges should be visible in the listable component - */ - initBadges() { - let objectAsAny = this.object as any; - if (hasValue(objectAsAny.indexableObject)) { - objectAsAny = objectAsAny.indexableObject; - } - const objectExistsAndValidViewMode = hasValue(objectAsAny) && this.viewMode !== ViewMode.StandalonePage; - this.privateBadge = objectExistsAndValidViewMode && hasValue(objectAsAny.isDiscoverable) && !objectAsAny.isDiscoverable; - this.withdrawnBadge = objectExistsAndValidViewMode && hasValue(objectAsAny.isWithdrawn) && objectAsAny.isWithdrawn; - } - /** * Fetch the component depending on the item's entity type, view mode and context * @returns {GenericConstructor} @@ -224,7 +199,7 @@ export class ListableObjectComponentLoaderComponent implements OnInit, OnChanges */ protected connectInputsAndOutputs(): void { if (isNotEmpty(this.inAndOutputNames) && hasValue(this.compRef) && hasValue(this.compRef.instance)) { - this.inAndOutputNames.forEach((name: any) => { + this.inAndOutputNames.filter((name: any) => this[name] !== undefined).forEach((name: any) => { this.compRef.instance[name] = this[name]; }); } diff --git a/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.html b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.html index e17ba92a05..513c132432 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.html +++ b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.html @@ -1,7 +1,7 @@ \ No newline at end of file +
diff --git a/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts index 2f2a20ede3..87f79d3570 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts +++ b/src/app/shared/object-collection/shared/mydspace-item-collection/item-collection.component.ts @@ -10,6 +10,7 @@ import { Collection } from '../../../../core/shared/collection.model'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { followLink } from '../../../utils/follow-link-config.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; /** * This component represents a badge with collection information. @@ -31,8 +32,10 @@ export class ItemCollectionComponent implements OnInit { */ collection$: Observable; - public constructor(protected linkService: LinkService) { - + public constructor( + protected linkService: LinkService, + public dsoNameService: DSONameService, + ) { } /** diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html index db38f98b04..0f9cdf846e 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html +++ b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html @@ -1,3 +1,13 @@ -
- {{'submission.workflow.tasks.generic.submitter' | translate}}: {{(submitter$ | async)?.name}} +
+ + {{'submission.workflow.tasks.generic.submitter' | translate}}: + + + {{ dsoNameService.getName(submitter) }} + + + {{ 'submitter.empty' | translate }} + + +
diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts index cf1de597f6..abaf029155 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts +++ b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA, DebugElement } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { of as observableOf } from 'rxjs'; @@ -60,10 +60,18 @@ describe('ItemSubmitterComponent', () => { })); }); - it('should show a badge with submitter name', () => { - const badge = fixture.debugElement.query(By.css('.badge')); + it('should show N/A when submitter is null', () => { + component.submitter$ = observableOf(null); + fixture.detectChanges(); - expect(badge).not.toBeNull(); - expect(badge.nativeElement.innerHTML).toBe(EPersonMock.name); + const badge: DebugElement = fixture.debugElement.query(By.css('.badge')); + + expect(badge.nativeElement.innerText).toBe('submitter.empty'); + }); + + it('should show a badge with submitter name', () => { + const badge: DebugElement = fixture.debugElement.query(By.css('.badge')); + + expect(badge.nativeElement.innerText).toBe(EPersonMock.name); }); }); diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts index fb644017db..990aa9906b 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts +++ b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts @@ -10,6 +10,7 @@ import { WorkflowItem } from '../../../../core/submission/models/workflowitem.mo import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { followLink } from '../../../utils/follow-link-config.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; /** * This component represents a badge with submitter information. @@ -31,8 +32,10 @@ export class ItemSubmitterComponent implements OnInit { */ submitter$: Observable; - public constructor(protected linkService: LinkService) { - + public constructor( + public dsoNameService: DSONameService, + protected linkService: LinkService, + ) { } /** diff --git a/src/app/shared/object-collection/shared/object-collection-element/abstract-listable-element.component.ts b/src/app/shared/object-collection/shared/object-collection-element/abstract-listable-element.component.ts index 7d4e107b2b..6b9aff069f 100644 --- a/src/app/shared/object-collection/shared/object-collection-element/abstract-listable-element.component.ts +++ b/src/app/shared/object-collection/shared/object-collection-element/abstract-listable-element.component.ts @@ -4,6 +4,7 @@ import { CollectionElementLinkType } from '../../collection-element-link.type'; import { Context } from '../../../../core/shared/context.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-abstract-object-element', @@ -70,4 +71,10 @@ export class AbstractListableElementComponent { * The available contexts */ contexts = Context; + + constructor( + public dsoNameService: DSONameService, + ) { + } + } diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.html index 3695f4714d..9a80421fbf 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.html @@ -2,7 +2,7 @@ + [badgeContext]="badgeContext"> ; @@ -71,6 +78,7 @@ describe('ClaimedTaskSearchResultDetailElementComponent', () => { imports: [NoopAnimationsModule], declarations: [ClaimedTaskSearchResultDetailElementComponent, VarDirective], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: LinkService, useValue: linkService }, { provide: ObjectCacheService, useValue: objectCacheServiceMock } ], @@ -101,8 +109,8 @@ describe('ClaimedTaskSearchResultDetailElementComponent', () => { expect(component.item$.value).toEqual(item); })); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.VALIDATION); + it('should have the correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceValidation); }); it('should forward claimed-task-actions processComplete event to reloadObject event emitter', fakeAsync(() => { diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.ts index 2ee661ef38..7e463d3b3b 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component.ts @@ -8,17 +8,16 @@ import { ViewMode } from '../../../../core/shared/view-mode.model'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; import { SearchResultDetailElementComponent } from '../search-result-detail-element.component'; -import { - MyDspaceItemStatusType -} from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claimed-task-search-result.model'; import { followLink } from '../../../utils/follow-link-config.model'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { Item } from '../../../../core/shared/item.model'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; -import { isNotEmpty } from '../../../empty.util'; +import { isNotEmpty, hasValue } from '../../../empty.util'; import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; +import { Context } from 'src/app/core/shared/context.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; /** * This component renders claimed task object for the search result in the detail view. @@ -43,17 +42,21 @@ export class ClaimedTaskSearchResultDetailElementComponent extends SearchResultD public showSubmitter = true; /** - * Represent item's status + * Represents the badge context */ - public status = MyDspaceItemStatusType.VALIDATION; + public badgeContext = Context.MyDSpaceValidation; /** * The workflowitem object that belonging to the result object */ public workflowitem$: BehaviorSubject = new BehaviorSubject(null); - constructor(protected linkService: LinkService, protected objectCache: ObjectCacheService) { - super(); + constructor( + public dsoNameService: DSONameService, + protected linkService: LinkService, + protected objectCache: ObjectCacheService, + ) { + super(dsoNameService); } /** @@ -88,7 +91,9 @@ export class ClaimedTaskSearchResultDetailElementComponent extends SearchResultD ngOnDestroy() { // This ensures the object is removed from cache, when action is performed on task - this.objectCache.remove(this.dso._links.workflowitem.href); + if (hasValue(this.dso)) { + this.objectCache.remove(this.dso._links.workflowitem.href); + } } } diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html index 833281eeaf..bd00c9411f 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html @@ -1,21 +1,19 @@
- - - +
- +
diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts index 778e455004..2e290bfffa 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts @@ -6,12 +6,13 @@ import { BitstreamDataService } from '../../../../core/data/bitstream-data.servi import { Item } from '../../../../core/shared/item.model'; import { getFirstSucceededRemoteListPayload } from '../../../../core/shared/operators'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { fadeInOut } from '../../../animations/fade'; import { Bitstream } from '../../../../core/shared/bitstream.model'; import { FileService } from '../../../../core/shared/file.service'; import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; import { SearchResult } from '../../../search/models/search-result.model'; +import { Context } from '../../../../core/shared/context.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; /** * This component show metadata for the given item object in the detail view. @@ -23,7 +24,6 @@ import { SearchResult } from '../../../search/models/search-result.model'; animations: [fadeInOut] }) export class ItemDetailPreviewComponent { - /** * The item to display */ @@ -35,9 +35,9 @@ export class ItemDetailPreviewComponent { @Input() object: SearchResult; /** - * Represent item's status + * Represents the badge context */ - @Input() status: MyDspaceItemStatusType; + @Input() badgeContext: Context; /** * A boolean representing if to show submitter information @@ -54,16 +54,12 @@ export class ItemDetailPreviewComponent { */ public separator = ', '; - /** - * Initialize instance variables - * - * @param {FileService} fileService - * @param {HALEndpointService} halService - * @param {BitstreamDataService} bitstreamDataService - */ - constructor(private fileService: FileService, - private halService: HALEndpointService, - private bitstreamDataService: BitstreamDataService) { + constructor( + protected fileService: FileService, + protected halService: HALEndpointService, + protected bitstreamDataService: BitstreamDataService, + public dsoNameService: DSONameService, + ) { } /** diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.html index 2b687960ab..1c19201c63 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.html @@ -1,6 +1,6 @@ + [badgeContext]="badgeContext"> diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.spec.ts index 8d602d5eb2..2c8039c923 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.spec.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.spec.ts @@ -6,8 +6,10 @@ import { of as observableOf } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { ItemSearchResultDetailElementComponent } from './item-search-result-detail-element.component'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; +import { Context } from 'src/app/core/shared/context.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; let component: ItemSearchResultDetailElementComponent; let fixture: ComponentFixture; @@ -52,6 +54,9 @@ describe('ItemSearchResultDetailElementComponent', () => { TestBed.configureTestingModule({ imports: [NoopAnimationsModule], declarations: [ItemSearchResultDetailElementComponent], + providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, + ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(ItemSearchResultDetailElementComponent, { set: { changeDetection: ChangeDetectionStrategy.Default } @@ -68,7 +73,7 @@ describe('ItemSearchResultDetailElementComponent', () => { fixture.detectChanges(); }); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.ARCHIVED); + it('should have the correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceArchived); }); }); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.ts index 27a94b0cf5..40ed72dcf1 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component.ts @@ -3,9 +3,6 @@ import { Component } from '@angular/core'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { Item } from '../../../../core/shared/item.model'; import { SearchResultDetailElementComponent } from '../search-result-detail-element.component'; -import { - MyDspaceItemStatusType -} from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; import { Context } from '../../../../core/shared/context.model'; @@ -24,8 +21,8 @@ import { Context } from '../../../../core/shared/context.model'; export class ItemSearchResultDetailElementComponent extends SearchResultDetailElementComponent { /** - * Represent item's status + * Represents the badge context */ - public status = MyDspaceItemStatusType.ARCHIVED; + public badgeContext = Context.MyDSpaceArchived; } diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.html index c9165b416a..d2a15b016f 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.html @@ -2,7 +2,7 @@ + [badgeContext]="badgeContext"> ; @@ -71,6 +71,7 @@ describe('PoolSearchResultDetailElementComponent', () => { imports: [NoopAnimationsModule], declarations: [PoolSearchResultDetailElementComponent, VarDirective], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: 'objectElementProvider', useValue: (mockResultObject) }, { provide: 'indexElementProvider', useValue: (compIndex) }, { provide: LinkService, useValue: linkService }, @@ -103,8 +104,8 @@ describe('PoolSearchResultDetailElementComponent', () => { expect(component.item$.value).toEqual(item); })); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.WAITING_CONTROLLER); + it('should have the correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceWaitingController); }); it('should forward pool-task-actions processCompleted event to the reloadedObject event emitter', fakeAsync(() => { diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.ts index 6dec14f9cb..80a81efa81 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component.ts @@ -6,9 +6,6 @@ import { mergeMap, tap } from 'rxjs/operators'; import { RemoteData } from '../../../../core/data/remote-data'; import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model'; import { SearchResultDetailElementComponent } from '../search-result-detail-element.component'; -import { - MyDspaceItemStatusType -} from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; @@ -17,8 +14,10 @@ import { followLink } from '../../../utils/follow-link-config.model'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { Item } from '../../../../core/shared/item.model'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; -import { isNotEmpty } from '../../../empty.util'; +import { isNotEmpty, hasValue } from '../../../empty.util'; import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; +import { Context } from 'src/app/core/shared/context.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; /** * This component renders pool task object for the search result in the detail view. @@ -43,17 +42,21 @@ export class PoolSearchResultDetailElementComponent extends SearchResultDetailEl public showSubmitter = true; /** - * Represent item's status + * Represents the badge context */ - public status = MyDspaceItemStatusType.WAITING_CONTROLLER; + public badgeContext = Context.MyDSpaceWaitingController; /** * The workflowitem object that belonging to the result object */ public workflowitem$: BehaviorSubject = new BehaviorSubject(null); - constructor(protected linkService: LinkService, protected objectCache: ObjectCacheService) { - super(); + constructor( + public dsoNameService: DSONameService, + protected linkService: LinkService, + protected objectCache: ObjectCacheService, + ) { + super(dsoNameService); } /** @@ -89,7 +92,9 @@ export class PoolSearchResultDetailElementComponent extends SearchResultDetailEl ngOnDestroy() { // This ensures the object is removed from cache, when action is performed on task - this.objectCache.remove(this.dso._links.workflowitem.href); + if (hasValue(this.dso)) { + this.objectCache.remove(this.dso._links.workflowitem.href); + } } } diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.html index 7ff1a9bf72..ddd6af453c 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.html @@ -1,6 +1,6 @@ + [badgeContext]="badgeContext"> diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.spec.ts index 536bd6d0bb..f4bc6b88fa 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.spec.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.spec.ts @@ -7,13 +7,13 @@ import { of as observableOf } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { WorkflowItemSearchResultDetailElementComponent } from './workflow-item-search-result-detail-element.component'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils'; import { WorkflowItemSearchResult } from '../../../object-collection/shared/workflow-item-search-result.model'; import { getMockLinkService } from '../../../mocks/link-service.mock'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; +import { Context } from '../../../../core/shared/context.model'; let component: WorkflowItemSearchResultDetailElementComponent; let fixture: ComponentFixture; @@ -87,7 +87,7 @@ describe('WorkflowItemSearchResultDetailElementComponent', () => { expect(component.item).toEqual(item); }); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.WORKFLOW); + it('should have the correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceWorkflow); }); }); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.ts index 8dc510d2aa..bad0fcee87 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component.ts @@ -4,7 +4,6 @@ import { ViewMode } from '../../../../core/shared/view-mode.model'; import { Item } from '../../../../core/shared/item.model'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; import { SearchResultDetailElementComponent } from '../search-result-detail-element.component'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { Observable } from 'rxjs'; import { RemoteData } from '../../../../core/data/remote-data'; import { find } from 'rxjs/operators'; @@ -13,6 +12,8 @@ import { listableObjectComponent } from '../../../object-collection/shared/lista import { WorkflowItemSearchResult } from '../../../object-collection/shared/workflow-item-search-result.model'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { followLink } from '../../../utils/follow-link-config.model'; +import { Context } from 'src/app/core/shared/context.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; /** * This component renders workflowitem object for the search result in the detail view. @@ -32,14 +33,15 @@ export class WorkflowItemSearchResultDetailElementComponent extends SearchResult public item: Item; /** - * Represent item's status + * Represents the badge context */ - public status = MyDspaceItemStatusType.WORKFLOW; + public badgeContext = Context.MyDSpaceWorkflow; constructor( + public dsoNameService: DSONameService, protected linkService: LinkService ) { - super(); + super(dsoNameService); } /** diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.html index 8d4eee6ba9..4414933e02 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.html @@ -1,5 +1,5 @@ + [badgeContext]="badgeContext"> diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.spec.ts index 00a20b006a..e86bfdb5e6 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.spec.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.spec.ts @@ -7,13 +7,13 @@ import { of as observableOf } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { WorkspaceItemSearchResultDetailElementComponent } from './workspace-item-search-result-detail-element.component'; import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils'; import { WorkflowItemSearchResult } from '../../../object-collection/shared/workflow-item-search-result.model'; import { getMockLinkService } from '../../../mocks/link-service.mock'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; +import { Context } from 'src/app/core/shared/context.model'; let component: WorkspaceItemSearchResultDetailElementComponent; let fixture: ComponentFixture; @@ -87,7 +87,7 @@ describe('WorkspaceItemSearchResultDetailElementComponent', () => { expect(component.item).toEqual(item); }); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.WORKSPACE); + it('should have the correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceWorkspace); }); }); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.ts index e4781fe0b2..03b41e795c 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component.ts @@ -8,12 +8,13 @@ import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { isNotUndefined } from '../../../empty.util'; import { SearchResultDetailElementComponent } from '../search-result-detail-element.component'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; import { WorkspaceItemSearchResult } from '../../../object-collection/shared/workspace-item-search-result.model'; import { followLink } from '../../../utils/follow-link-config.model'; import { LinkService } from '../../../../core/cache/builders/link.service'; +import { Context } from '../../../../core/shared/context.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; /** * This component renders workspace item object for the search result in the detail view. @@ -33,14 +34,15 @@ export class WorkspaceItemSearchResultDetailElementComponent extends SearchResul public item: Item; /** - * Represent item's status + * Represents the badge context */ - status = MyDspaceItemStatusType.WORKSPACE; + public badgeContext = Context.MyDSpaceWorkspace; constructor( + public dsoNameService: DSONameService, protected linkService: LinkService ) { - super(); + super(dsoNameService); } /** diff --git a/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.html b/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.html index 4d6d5cb1c3..1608d845ea 100644 --- a/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.html +++ b/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.html @@ -1,14 +1,14 @@
- - + + - - + +
-

{{object.name}}

+

{{ dsoNameService.getName(object) }}

{{object.shortDescription}}

{{ 'search.results.view-result' | translate}} diff --git a/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts b/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts index b6a7cc056b..e2ecf7b1ae 100644 --- a/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts +++ b/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts @@ -6,6 +6,7 @@ import { listableObjectComponent } from '../../object-collection/shared/listable import { hasNoValue, hasValue } from '../../empty.util'; import { followLink } from '../../utils/follow-link-config.model'; import { LinkService } from '../../../core/cache/builders/link.service'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; /** * Component representing a grid element for collection @@ -21,8 +22,11 @@ export class CollectionGridElementComponent extends AbstractListableElementCompo > { private _object: Collection; - constructor(private linkService: LinkService) { - super(); + constructor( + public dsoNameService: DSONameService, + private linkService: LinkService, + ) { + super(dsoNameService); } // @ts-ignore diff --git a/src/app/shared/object-grid/community-grid-element/community-grid-element.component.html b/src/app/shared/object-grid/community-grid-element/community-grid-element.component.html index c9833b8829..c448c70991 100644 --- a/src/app/shared/object-grid/community-grid-element/community-grid-element.component.html +++ b/src/app/shared/object-grid/community-grid-element/community-grid-element.component.html @@ -1,14 +1,14 @@
- - + + - - + +
-

{{object.name}}

+

{{ dsoNameService.getName(object) }}

{{object.shortDescription}}

{{ 'search.results.view-result' | translate}} diff --git a/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts b/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts index 001c3e3080..59ac0693eb 100644 --- a/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts +++ b/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts @@ -6,6 +6,7 @@ import { listableObjectComponent } from '../../object-collection/shared/listable import { followLink } from '../../utils/follow-link-config.model'; import { LinkService } from '../../../core/cache/builders/link.service'; import { hasNoValue, hasValue } from '../../empty.util'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; /** * Component representing a grid element for a community @@ -20,8 +21,11 @@ import { hasNoValue, hasValue } from '../../empty.util'; export class CommunityGridElementComponent extends AbstractListableElementComponent { private _object: Community; - constructor( private linkService: LinkService) { - super(); + constructor( + public dsoNameService: DSONameService, + private linkService: LinkService, + ) { + super(dsoNameService); } // @ts-ignore diff --git a/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.spec.ts b/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.spec.ts index 645897ebbf..b137c2e708 100644 --- a/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/item-grid-element/item-types/item/item-grid-element.component.spec.ts @@ -10,6 +10,8 @@ import { Item } from '../../../../../core/shared/item.model'; import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; import { buildPaginatedList } from '../../../../../core/data/paginated-list.model'; import { PageInfo } from '../../../../../core/shared/page-info.model'; +import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../mocks/dso-name.service.mock'; const mockItem = Object.assign(new Item(), { bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), @@ -54,6 +56,7 @@ describe('ItemGridElementComponent', () => { imports: [NoopAnimationsModule], declarations: [ItemGridElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.html b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.html index b05bb4f7ba..1e46f11144 100644 --- a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.html +++ b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.html @@ -1,15 +1,15 @@
- - + + - - + +
- -

{{dso.name}}

+ +

{{ dsoNameService.getName(dso) }}

{{dso.shortDescription}}

{{ 'search.results.view-result' | translate}} diff --git a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts index 9f715b0e79..e4ea2e48f5 100644 --- a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts @@ -9,6 +9,7 @@ import { followLink } from '../../../utils/follow-link-config.model'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { TruncatableService } from '../../../truncatable/truncatable.service'; import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-collection-search-result-grid-element', @@ -23,11 +24,12 @@ export class CollectionSearchResultGridElementComponent extends SearchResultGrid private _dso: Collection; constructor( + public dsoNameService: DSONameService, private linkService: LinkService, protected truncatableService: TruncatableService, protected bitstreamDataService: BitstreamDataService ) { - super(truncatableService, bitstreamDataService); + super(dsoNameService, truncatableService, bitstreamDataService); } // @ts-ignore diff --git a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.html b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.html index d915cdb7a4..96e10156c3 100644 --- a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.html +++ b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.html @@ -1,15 +1,15 @@
- - + + - - + +
- -

{{dso.name}}

+ +

{{ dsoNameService.getName(dso) }}

{{dso.shortDescription}}

{{ 'search.results.view-result' | translate}} diff --git a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts index 18994e2cd7..039b01c567 100644 --- a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts @@ -9,6 +9,7 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; import { hasNoValue, hasValue } from '../../../empty.util'; import { followLink } from '../../../utils/follow-link-config.model'; +import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-community-search-result-grid-element', @@ -26,11 +27,12 @@ export class CommunitySearchResultGridElementComponent extends SearchResultGridE private _dso: Community; constructor( + public dsoNameService: DSONameService, private linkService: LinkService, protected truncatableService: TruncatableService, protected bitstreamDataService: BitstreamDataService ) { - super(truncatableService, bitstreamDataService); + super(dsoNameService, truncatableService, bitstreamDataService); } // @ts-ignore diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html index c7e7e2d056..3d6e251238 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.html @@ -5,19 +5,18 @@
- - + +
- - + +
- - +

diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts index 303e4681a2..7d237e3f79 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.ts @@ -32,11 +32,11 @@ export class ItemSearchResultGridElementComponent extends SearchResultGridElemen dsoTitle: string; constructor( + public dsoNameService: DSONameService, protected truncatableService: TruncatableService, protected bitstreamDataService: BitstreamDataService, - private dsoNameService: DSONameService, ) { - super(truncatableService, bitstreamDataService); + super(dsoNameService, truncatableService, bitstreamDataService); } ngOnInit(): void { diff --git a/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts index 6e72eaa942..4c3431bb55 100644 --- a/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts @@ -8,6 +8,7 @@ import { Metadata } from '../../../core/shared/metadata.utils'; import { hasValue } from '../../empty.util'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; import { TruncatableService } from '../../truncatable/truncatable.service'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-search-result-grid-element', @@ -25,10 +26,11 @@ export class SearchResultGridElementComponent, K exten isCollapsed$: Observable; public constructor( + public dsoNameService: DSONameService, protected truncatableService: TruncatableService, protected bitstreamDataService: BitstreamDataService ) { - super(); + super(dsoNameService); } /** diff --git a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.spec.ts b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.spec.ts index a4490bd951..15d1d9df39 100644 --- a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.spec.ts +++ b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.spec.ts @@ -7,6 +7,8 @@ import { BrowseEntry } from '../../../core/shared/browse-entry.model'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { RouteService } from '../../../core/services/route.service'; import { of as observableOf } from 'rxjs'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../mocks/dso-name.service.mock'; let browseEntryListElementComponent: BrowseEntryListElementComponent; let fixture: ComponentFixture; @@ -34,6 +36,7 @@ describe('BrowseEntryListElementComponent', () => { TestBed.configureTestingModule({ declarations: [BrowseEntryListElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: 'objectElementProvider', useValue: { mockValue } }, {provide: PaginationService, useValue: paginationService}, {provide: RouteService, useValue: routeService}, diff --git a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts index 667da726ed..e67dd4489e 100644 --- a/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts +++ b/src/app/shared/object-list/browse-entry-list-element/browse-entry-list-element.component.ts @@ -10,6 +10,7 @@ import { BBM_PAGINATION_ID } from '../../../browse-by/browse-by-metadata-page/br import { RouteService } from 'src/app/core/services/route.service'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-browse-entry-list-element', @@ -27,8 +28,12 @@ export class BrowseEntryListElementComponent extends AbstractListableElementComp */ queryParams$: Observable; - constructor(private paginationService: PaginationService, private routeService: RouteService) { - super(); + constructor( + public dsoNameService: DSONameService, + protected paginationService: PaginationService, + protected routeService: RouteService, + ) { + super(dsoNameService); } ngOnInit() { diff --git a/src/app/shared/object-list/bundle-list-element/bundle-list-element.component.html b/src/app/shared/object-list/bundle-list-element/bundle-list-element.component.html index dfe08144a8..77ed1e3b21 100644 --- a/src/app/shared/object-list/bundle-list-element/bundle-list-element.component.html +++ b/src/app/shared/object-list/bundle-list-element/bundle-list-element.component.html @@ -1 +1 @@ -
{{object.name}}
+
{{ dsoNameService.getName(object) }}
diff --git a/src/app/shared/object-list/collection-list-element/collection-list-element.component.html b/src/app/shared/object-list/collection-list-element/collection-list-element.component.html index c61adf5dad..6c2cff5215 100644 --- a/src/app/shared/object-list/collection-list-element/collection-list-element.component.html +++ b/src/app/shared/object-list/collection-list-element/collection-list-element.component.html @@ -1,8 +1,8 @@ - {{object.name}} + {{ dsoNameService.getName(object) }} - {{object.name}} + {{ dsoNameService.getName(object) }}
{{object.shortDescription}} diff --git a/src/app/shared/object-list/collection-list-element/collection-list-element.component.spec.ts b/src/app/shared/object-list/collection-list-element/collection-list-element.component.spec.ts index c41d5a7314..04715983c9 100644 --- a/src/app/shared/object-list/collection-list-element/collection-list-element.component.spec.ts +++ b/src/app/shared/object-list/collection-list-element/collection-list-element.component.spec.ts @@ -3,6 +3,8 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { Collection } from '../../../core/shared/collection.model'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../mocks/dso-name.service.mock'; let collectionListElementComponent: CollectionListElementComponent; let fixture: ComponentFixture; @@ -34,6 +36,7 @@ describe('CollectionListElementComponent', () => { TestBed.configureTestingModule({ declarations: [CollectionListElementComponent], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: 'objectElementProvider', useValue: (mockCollectionWithAbstract) } ], diff --git a/src/app/shared/object-list/community-list-element/community-list-element.component.html b/src/app/shared/object-list/community-list-element/community-list-element.component.html index af01999ca7..462db7be91 100644 --- a/src/app/shared/object-list/community-list-element/community-list-element.component.html +++ b/src/app/shared/object-list/community-list-element/community-list-element.component.html @@ -1,8 +1,8 @@ - {{object.name}} + {{ dsoNameService.getName(object) }} - {{object.name}} + {{ dsoNameService.getName(object) }}
{{object.shortDescription}} diff --git a/src/app/shared/object-list/community-list-element/community-list-element.component.spec.ts b/src/app/shared/object-list/community-list-element/community-list-element.component.spec.ts index cd212e0399..8f7350d860 100644 --- a/src/app/shared/object-list/community-list-element/community-list-element.component.spec.ts +++ b/src/app/shared/object-list/community-list-element/community-list-element.component.spec.ts @@ -3,6 +3,8 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { Community } from '../../../core/shared/community.model'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../mocks/dso-name.service.mock'; let communityListElementComponent: CommunityListElementComponent; let fixture: ComponentFixture; @@ -34,6 +36,7 @@ describe('CommunityListElementComponent', () => { TestBed.configureTestingModule({ declarations: [CommunityListElementComponent], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: 'objectElementProvider', useValue: (mockCommunityWithAbstract) } ], diff --git a/src/app/shared/object-list/community-list-element/community-list-element.component.ts b/src/app/shared/object-list/community-list-element/community-list-element.component.ts index b41cd78107..4a83fd2834 100644 --- a/src/app/shared/object-list/community-list-element/community-list-element.component.ts +++ b/src/app/shared/object-list/community-list-element/community-list-element.component.ts @@ -4,6 +4,7 @@ import { Community } from '../../../core/shared/community.model'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; import { ViewMode } from '../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../object-collection/shared/listable-object/listable-object.decorator'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-community-list-element', @@ -14,4 +15,12 @@ import { listableObjectComponent } from '../../object-collection/shared/listable * Component representing a list element for a community */ @listableObjectComponent(Community, ViewMode.ListElement) -export class CommunityListElementComponent extends AbstractListableElementComponent {} +export class CommunityListElementComponent extends AbstractListableElementComponent { + + constructor( + public dsoNameService: DSONameService, + ) { + super(dsoNameService); + } + +} diff --git a/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts index de30c23216..d75576e8eb 100644 --- a/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/item/item-list-element.component.spec.ts @@ -6,6 +6,8 @@ import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { of as observableOf } from 'rxjs'; +import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../../mocks/dso-name.service.mock'; const mockItem: Item = Object.assign(new Item(), { bundles: observableOf({}), @@ -55,6 +57,7 @@ describe('ItemListElementComponent', () => { TestBed.configureTestingModule({ declarations: [ItemListElementComponent, TruncatePipe], providers: [ + { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: TruncatableService, useValue: truncatableServiceStub }, ], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.html b/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.html index 8d3afea273..e2f55ce10c 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.html +++ b/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.html @@ -1,9 +1,9 @@ diff --git a/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.spec.ts b/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.spec.ts index 32919d9758..3527b9fddd 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.spec.ts +++ b/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.spec.ts @@ -30,7 +30,7 @@ describe('BrowseLinkMetadataListElementComponent', () => { beforeEach(waitForAsync(() => { fixture = TestBed.createComponent(BrowseLinkMetadataListElementComponent); comp = fixture.componentInstance; - comp.metadataRepresentation = mockMetadataRepresentation; + comp.mdRepresentation = mockMetadataRepresentation; fixture.detectChanges(); })); @@ -46,7 +46,7 @@ describe('BrowseLinkMetadataListElementComponent', () => { beforeEach(waitForAsync(() => { fixture = TestBed.createComponent(BrowseLinkMetadataListElementComponent); comp = fixture.componentInstance; - comp.metadataRepresentation = mockMetadataRepresentationWithUrl; + comp.mdRepresentation = mockMetadataRepresentationWithUrl; fixture.detectChanges(); })); diff --git a/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.ts index 0eb0ce05b0..51907b5641 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.ts +++ b/src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.ts @@ -20,9 +20,9 @@ export class BrowseLinkMetadataListElementComponent extends MetadataRepresentati * expects 'startsWith' (eg browse by date) or 'value' (eg browse by title) */ getQueryParams() { - let queryParams = {startsWith: this.metadataRepresentation.getValue()}; - if (this.metadataRepresentation.browseDefinition.metadataBrowse) { - return {value: this.metadataRepresentation.getValue()}; + let queryParams = {startsWith: this.mdRepresentation.getValue()}; + if (this.mdRepresentation.browseDefinition.metadataBrowse) { + return {value: this.mdRepresentation.getValue()}; } return queryParams; } diff --git a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html index 91219c7189..904ea95c20 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html +++ b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html @@ -1 +1 @@ - + diff --git a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts index 6e48ba3a6f..99052b6b14 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts +++ b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts @@ -23,7 +23,7 @@ describe('ItemMetadataListElementComponent', () => { beforeEach(waitForAsync(() => { fixture = TestBed.createComponent(ItemMetadataListElementComponent); comp = fixture.componentInstance; - comp.metadataRepresentation = mockItemMetadataRepresentation; + comp.mdRepresentation = mockItemMetadataRepresentation; fixture.detectChanges(); })); diff --git a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component.ts index 967b09986d..c4a6903129 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component.ts +++ b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component.ts @@ -1,5 +1,5 @@ import { MetadataRepresentationListElementComponent } from '../metadata-representation-list-element.component'; -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, Input } from '@angular/core'; import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; import { getItemPageRoute } from '../../../../item-page/item-page-routing-paths'; @@ -11,7 +11,7 @@ import { getItemPageRoute } from '../../../../item-page/item-page-routing-paths' * An abstract class for displaying a single ItemMetadataRepresentation */ export class ItemMetadataRepresentationListElementComponent extends MetadataRepresentationListElementComponent implements OnInit { - metadataRepresentation: ItemMetadataRepresentation; + @Input() mdRepresentation: ItemMetadataRepresentation; /** * Route to the item's page @@ -19,6 +19,6 @@ export class ItemMetadataRepresentationListElementComponent extends MetadataRepr itemPageRoute: string; ngOnInit(): void { - this.itemPageRoute = getItemPageRoute(this.metadataRepresentation); + this.itemPageRoute = getItemPageRoute(this.mdRepresentation); } } diff --git a/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.spec.ts b/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.spec.ts index f0cc150b3e..dc8febe84a 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.spec.ts +++ b/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.spec.ts @@ -36,7 +36,7 @@ describe('MetadataRepresentationListElementComponent', () => { describe('when the value is not a URL', () => { beforeEach(() => { - comp.metadataRepresentation = mockMetadataRepresentation; + comp.mdRepresentation = mockMetadataRepresentation; }); it('isLink correctly detects a non-URL string as false', () => { waitForAsync(() => { @@ -47,7 +47,7 @@ describe('MetadataRepresentationListElementComponent', () => { describe('when the value is a URL', () => { beforeEach(() => { - comp.metadataRepresentation = mockMetadataRepresentationUrl; + comp.mdRepresentation = mockMetadataRepresentationUrl; }); it('isLink correctly detects a URL string as true', () => { waitForAsync(() => { diff --git a/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts index b69f6b37dc..d8f8621ca6 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts +++ b/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts @@ -1,5 +1,6 @@ -import { Component } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model'; +import { Context } from '../../../core/shared/context.model'; @Component({ selector: 'ds-metadata-representation-list-element', @@ -9,10 +10,15 @@ import { MetadataRepresentation } from '../../../core/shared/metadata-representa * An abstract class for displaying a single MetadataRepresentation */ export class MetadataRepresentationListElementComponent { + /** + * The optional context + */ + @Input() context: Context; + /** * The metadata representation of this component */ - metadataRepresentation: MetadataRepresentation; + @Input() mdRepresentation: MetadataRepresentation; /** * Returns true if this component's value matches a basic regex "Is this an HTTP URL" test @@ -20,7 +26,7 @@ export class MetadataRepresentationListElementComponent { isLink(): boolean { // Match any string that begins with http:// or https:// const linkPattern = new RegExp(/^https?\/\/.*/); - return linkPattern.test(this.metadataRepresentation.getValue()); + return linkPattern.test(this.mdRepresentation.getValue()); } } diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html index 7b611a7d1f..7d416e9f3e 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html +++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html @@ -1,17 +1,17 @@ diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts index cfb812a475..91d7db3562 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts +++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts @@ -29,7 +29,7 @@ describe('PlainTextMetadataListElementComponent', () => { beforeEach(waitForAsync(() => { fixture = TestBed.createComponent(PlainTextMetadataListElementComponent); comp = fixture.componentInstance; - comp.metadataRepresentation = mockMetadataRepresentation; + comp.mdRepresentation = mockMetadataRepresentation; fixture.detectChanges(); })); diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts index 2d21a7afe8..4ff16e949a 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts +++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts @@ -20,9 +20,9 @@ export class PlainTextMetadataListElementComponent extends MetadataRepresentatio * expects 'startsWith' (eg browse by date) or 'value' (eg browse by title) */ getQueryParams() { - let queryParams = {startsWith: this.metadataRepresentation.getValue()}; - if (this.metadataRepresentation.browseDefinition.metadataBrowse) { - return {value: this.metadataRepresentation.getValue()}; + let queryParams = {startsWith: this.mdRepresentation.getValue()}; + if (this.mdRepresentation.browseDefinition.metadataBrowse) { + return {value: this.mdRepresentation.getValue()}; } return queryParams; } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.html index 79e870e130..bb8e4ebfc8 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.html @@ -4,7 +4,7 @@
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.spec.ts index c05a4fc390..777a0258b0 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.spec.ts @@ -12,13 +12,13 @@ import { getMockLinkService } from '../../../../mocks/link-service.mock'; import { VarDirective } from '../../../../utils/var.directive'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { LinkService } from '../../../../../core/cache/builders/link.service'; -import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { ClaimedApprovedTaskSearchResult } from '../../../../object-collection/shared/claimed-approved-task-search-result.model'; import { ClaimedApprovedSearchResultListElementComponent } from './claimed-approved-search-result-list-element.component'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../mocks/dso-name.service.mock'; import { APP_CONFIG } from '../../../../../../config/app-config.interface'; import { environment } from '../../../../../../environments/environment'; +import { Context } from '../../../../../core/shared/context.model'; import { TranslateModule } from '@ngx-translate/core'; let component: ClaimedApprovedSearchResultListElementComponent; @@ -104,8 +104,8 @@ describe('ClaimedApprovedSearchResultListElementComponent', () => { }); }); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.APPROVED); + it('should have the correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceApproved); }); }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts index 5dda0e44ea..10e29bbab9 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-approved-search-result/claimed-approved-search-result-list-element.component.ts @@ -4,7 +4,6 @@ import { ClaimedApprovedTaskSearchResult } from '../../../../object-collection/s import { listableObjectComponent } from '../../../../object-collection/shared/listable-object/listable-object.decorator'; import { LinkService } from '../../../../../core/cache/builders/link.service'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; -import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { Observable } from 'rxjs'; import { RemoteData } from '../../../../../core/data/remote-data'; import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model'; @@ -14,6 +13,7 @@ import { ClaimedTaskSearchResult } from '../../../../object-collection/shared/cl import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface'; +import { Context } from 'src/app/core/shared/context.model'; /** * This component renders claimed task approved object for the search result in the list view. @@ -32,9 +32,9 @@ export class ClaimedApprovedSearchResultListElementComponent extends SearchResul public showSubmitter = true; /** - * Represent item's status + * Represents the badge context */ - public status = MyDspaceItemStatusType.APPROVED; + public badgeContext = Context.MyDSpaceApproved; /** * The workflowitem object that belonging to the result object @@ -44,7 +44,7 @@ export class ClaimedApprovedSearchResultListElementComponent extends SearchResul public constructor( protected linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService, + public dsoNameService: DSONameService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { super(truncatableService, dsoNameService, appConfig); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.html index 83fb3646c6..08f3f79a98 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.html @@ -4,7 +4,7 @@
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.spec.ts index dc06446cbc..ccd742f854 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.spec.ts @@ -14,11 +14,11 @@ import { getMockLinkService } from '../../../../mocks/link-service.mock'; import { VarDirective } from '../../../../utils/var.directive'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { LinkService } from '../../../../../core/cache/builders/link.service'; -import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../mocks/dso-name.service.mock'; import { APP_CONFIG } from '../../../../../../config/app-config.interface'; import { environment } from '../../../../../../environments/environment'; +import { Context } from '../../../../../core/shared/context.model'; import { TranslateModule } from '@ngx-translate/core'; let component: ClaimedDeclinedSearchResultListElementComponent; @@ -104,8 +104,8 @@ describe('ClaimedDeclinedSearchResultListElementComponent', () => { }); }); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.DECLINED); + it('should have the correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceDeclined); }); }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts index 90f523a7fb..0d24ac9968 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-search-result/claimed-declined-search-result-list-element.component.ts @@ -5,7 +5,6 @@ import { ClaimedDeclinedTaskSearchResult } from '../../../../object-collection/s import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { LinkService } from '../../../../../core/cache/builders/link.service'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; -import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { Observable } from 'rxjs'; import { RemoteData } from '../../../../../core/data/remote-data'; import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model'; @@ -15,6 +14,7 @@ import { ClaimedTaskSearchResult } from '../../../../object-collection/shared/cl import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface'; +import { Context } from '../../../../../core/shared/context.model'; /** * This component renders claimed task declined object for the search result in the list view. @@ -33,9 +33,9 @@ export class ClaimedDeclinedSearchResultListElementComponent extends SearchResul public showSubmitter = true; /** - * Represent item's status + * Represents the badge context */ - public status = MyDspaceItemStatusType.DECLINED; + public badgeContext = Context.MyDSpaceDeclined; /** * The workflowitem object that belonging to the result object @@ -45,7 +45,7 @@ export class ClaimedDeclinedSearchResultListElementComponent extends SearchResul public constructor( protected linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService, + public dsoNameService: DSONameService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { super(truncatableService, dsoNameService, appConfig); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.html index a8ec21fa6c..715d350739 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.html @@ -4,7 +4,7 @@
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.spec.ts index 1b23def2e4..5e082ef4cf 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.spec.ts @@ -12,7 +12,6 @@ import { getMockLinkService } from '../../../../mocks/link-service.mock'; import { VarDirective } from '../../../../utils/var.directive'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { LinkService } from '../../../../../core/cache/builders/link.service'; -import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../../mocks/dso-name.service.mock'; import { APP_CONFIG } from '../../../../../../config/app-config.interface'; @@ -102,8 +101,4 @@ describe('ClaimedDeclinedTaskSearchResultListElementComponent', () => { }); }); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.DECLINED_TASk); - }); - }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.ts index 131f4b6730..fb374ff52d 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component.ts @@ -4,7 +4,6 @@ import { ClaimedDeclinedTaskTaskSearchResult } from 'src/app/shared/object-colle import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { LinkService } from '../../../../../core/cache/builders/link.service'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; -import { MyDspaceItemStatusType } from '../../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { Observable } from 'rxjs'; import { RemoteData } from '../../../../../core/data/remote-data'; import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model'; @@ -14,6 +13,7 @@ import { ClaimedTaskSearchResult } from '../../../../object-collection/shared/cl import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model'; import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service'; import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface'; +import { Context } from '../../../../../core/shared/context.model'; /** * This component renders claimed task declined task object for the search result in the list view. @@ -32,9 +32,9 @@ export class ClaimedDeclinedTaskSearchResultListElementComponent extends SearchR public showSubmitter = true; /** - * Represent item's status + * Represents the badge context */ - public status = MyDspaceItemStatusType.DECLINED_TASk; + public badgeContext = Context.MyDSpaceDeclined; /** * The workflowitem object that belonging to the result object @@ -44,7 +44,7 @@ export class ClaimedDeclinedTaskSearchResultListElementComponent extends SearchR public constructor( protected linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService, + public dsoNameService: DSONameService, @Inject(APP_CONFIG) protected appConfig: AppConfig, ) { super(truncatableService, dsoNameService, appConfig); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html index 4584b12550..42af008cdd 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.html @@ -2,8 +2,8 @@ + [badgeContext]="badgeContext" + [workflowItem]="workflowitem$.value">
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts index 4ea716cc2a..c1778c1792 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.spec.ts @@ -1,5 +1,12 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { ComponentFixture, fakeAsync, flush, TestBed, tick, waitForAsync } from '@angular/core/testing'; +import { + ComponentFixture, + fakeAsync, + flush, + TestBed, + tick, + waitForAsync +} from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { of as observableOf } from 'rxjs'; @@ -7,9 +14,6 @@ import { of as observableOf } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { ClaimedSearchResultListElementComponent } from './claimed-search-result-list-element.component'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; -import { - MyDspaceItemStatusType -} from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils'; import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claimed-task-search-result.model'; @@ -23,6 +27,7 @@ import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; import { APP_CONFIG } from '../../../../../config/app-config.interface'; import { environment } from '../../../../../environments/environment'; import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; +import { Context } from '../../../../core/shared/context.model'; let component: ClaimedSearchResultListElementComponent; let fixture: ComponentFixture; @@ -107,8 +112,8 @@ describe('ClaimedSearchResultListElementComponent', () => { expect(component.item$.value).toEqual(item); })); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.VALIDATION); + it('should have the correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceValidation); }); it('should forward claimed-task-actions processComplete event to reloadObject event emitter', fakeAsync(() => { diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts index 237a5f516e..18148b6a8c 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component.ts @@ -5,9 +5,6 @@ import { listableObjectComponent } from '../../../object-collection/shared/lista import { ClaimedTaskSearchResult } from '../../../object-collection/shared/claimed-task-search-result.model'; import { LinkService } from '../../../../core/cache/builders/link.service'; import { TruncatableService } from '../../../truncatable/truncatable.service'; -import { - MyDspaceItemStatusType -} from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { BehaviorSubject, EMPTY, Observable } from 'rxjs'; import { RemoteData } from '../../../../core/data/remote-data'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; @@ -22,7 +19,8 @@ import { ObjectCacheService } from '../../../../core/cache/object-cache.service' import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; import { Item } from '../../../../core/shared/item.model'; import { mergeMap, tap } from 'rxjs/operators'; -import { isNotEmpty } from '../../../empty.util'; +import { isNotEmpty, hasValue } from '../../../empty.util'; +import { Context } from '../../../../core/shared/context.model'; @Component({ selector: 'ds-claimed-search-result-list-element', @@ -38,9 +36,9 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle public showSubmitter = true; /** - * Represent item's status + * Represents the badge context */ - public status = MyDspaceItemStatusType.VALIDATION; + public badgeContext = Context.MyDSpaceValidation; /** * The item object that belonging to the result object @@ -60,7 +58,7 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle public constructor( protected linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService, + public dsoNameService: DSONameService, protected objectCache: ObjectCacheService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { @@ -101,7 +99,9 @@ export class ClaimedSearchResultListElementComponent extends SearchResultListEle ngOnDestroy() { // This ensures the object is removed from cache, when action is performed on task - this.objectCache.remove(this.dso._links.workflowitem.href); + if (hasValue(this.dso)) { + this.objectCache.remove(this.dso._links.workflowitem.href); + } } } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html index 94426136b5..c8ef730faf 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html @@ -5,12 +5,8 @@
- - -
- - +

@@ -47,4 +43,4 @@
-
\ No newline at end of file +
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts index aaddebd8eb..aa618add35 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts @@ -178,8 +178,8 @@ describe('ItemListPreviewComponent', () => { fixture.detectChanges(); }); - it('should show the entity type span', () => { - const entityField = fixture.debugElement.query(By.css('ds-type-badge')); + it('should show the badges', () => { + const entityField = fixture.debugElement.query(By.css('ds-themed-badges')); expect(entityField).not.toBeNull(); }); }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts index 39f83bc371..be94eb85aa 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts @@ -2,12 +2,10 @@ import { Component, Inject, Input, OnInit } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; import { fadeInOut } from '../../../animations/fade'; -import { - MyDspaceItemStatusType -} from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { SearchResult } from '../../../search/models/search-result.model'; import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; +import { Context } from 'src/app/core/shared/context.model'; import { WorkflowItem } from 'src/app/core/submission/models/workflowitem.model'; /** @@ -32,9 +30,9 @@ export class ItemListPreviewComponent implements OnInit { @Input() object: SearchResult; /** - * Represent item's status + * Represents the badge context */ - @Input() status: MyDspaceItemStatusType; + @Input() badgeContext: Context; /** * A boolean representing if to show submitter information @@ -55,7 +53,7 @@ export class ItemListPreviewComponent implements OnInit { constructor( @Inject(APP_CONFIG) protected appConfig: AppConfig, - private dsoNameService: DSONameService, + public dsoNameService: DSONameService, ) { } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts index 9166e42040..fc50b49d51 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component.ts @@ -2,8 +2,8 @@ import { Component, Input } from '@angular/core'; import { ThemedComponent } from '../../../theme-support/themed.component'; import { ItemListPreviewComponent } from './item-list-preview.component'; import { Item } from '../../../../core/shared/item.model'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { SearchResult } from '../../../search/models/search-result.model'; +import { Context } from 'src/app/core/shared/context.model'; import { WorkflowItem } from 'src/app/core/submission/models/workflowitem.model'; /** @@ -15,13 +15,13 @@ import { WorkflowItem } from 'src/app/core/submission/models/workflowitem.model' templateUrl: '../../../theme-support/themed.component.html' }) export class ThemedItemListPreviewComponent extends ThemedComponent { - protected inAndOutputNames: (keyof ItemListPreviewComponent & keyof this)[] = ['item', 'object', 'status', 'showSubmitter', 'workflowItem']; + protected inAndOutputNames: (keyof ItemListPreviewComponent & keyof this)[] = ['item', 'object', 'badgeContext', 'showSubmitter', 'workflowItem']; @Input() item: Item; @Input() object: SearchResult; - @Input() status: MyDspaceItemStatusType; + @Input() badgeContext: Context; @Input() showSubmitter: boolean; diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.html index b5e9155b81..c1f7098d5f 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.html @@ -1,6 +1,6 @@ + [badgeContext]="badgeContext">
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.spec.ts index 1dc221f2ed..a26fbaaf57 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.spec.ts @@ -1,11 +1,10 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { waitForAsync, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { of as observableOf } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; import { ItemSearchResultListElementSubmissionComponent } from './item-search-result-list-element-submission.component'; import { TruncatableService } from '../../../truncatable/truncatable.service'; @@ -14,6 +13,7 @@ import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; import { APP_CONFIG } from '../../../../../config/app-config.interface'; import { environment } from '../../../../../environments/environment'; +import { Context } from '../../../../core/shared/context.model'; let component: ItemSearchResultListElementSubmissionComponent; let fixture: ComponentFixture; @@ -77,8 +77,8 @@ describe('ItemMyDSpaceResultListElementComponent', () => { fixture.detectChanges(); }); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.ARCHIVED); + it('should have correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceArchived); }); it('should forward item-actions processComplete event to reloadObject event emitter', fakeAsync(() => { diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.ts index 26cbafc102..91c2576dee 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component.ts @@ -1,7 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; import { Context } from '../../../../core/shared/context.model'; @@ -21,9 +20,10 @@ import { SearchResultListElementComponent } from '../../search-result-list-eleme @listableObjectComponent(ItemSearchResult, ViewMode.ListElement, Context.Workflow) export class ItemSearchResultListElementSubmissionComponent extends SearchResultListElementComponent implements OnInit { /** - * Represent item's status + * Represents the badge context */ - public status = MyDspaceItemStatusType.ARCHIVED; + public badgeContext = Context.MyDSpaceArchived; + /** * Display thumbnails if required by configuration diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html index d863371446..9fe6e37c9e 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.html @@ -2,8 +2,8 @@ + [badgeContext]="badgeContext" + [workflowItem]="workflowitem$.value">
; @@ -113,8 +118,8 @@ describe('PoolSearchResultListElementComponent', () => { expect(component.item$.value).toEqual(item); })); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.WAITING_CONTROLLER); + it('should have correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceWaitingController); }); it('should forward pool-task-actions processCompleted event to the reloadedObject event emitter', fakeAsync(() => { diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts index cb924af40f..19723a7e49 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component.ts @@ -7,9 +7,6 @@ import { ViewMode } from '../../../../core/shared/view-mode.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model'; -import { - MyDspaceItemStatusType -} from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; import { PoolTaskSearchResult } from '../../../object-collection/shared/pool-task-search-result.model'; import { @@ -23,7 +20,8 @@ import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interfac import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; import { getFirstCompletedRemoteData } from '../../../../core/shared/operators'; import { Item } from '../../../../core/shared/item.model'; -import { isNotEmpty } from '../../../empty.util'; +import { isNotEmpty, hasValue } from '../../../empty.util'; +import { Context } from '../../../../core/shared/context.model'; /** * This component renders pool task object for the search result in the list view. @@ -43,9 +41,9 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen public showSubmitter = true; /** - * Represent item's status + * Represents the badge Context */ - public status = MyDspaceItemStatusType.WAITING_CONTROLLER; + public badgeContext = Context.MyDSpaceWaitingController; /** * The item object that belonging to the result object @@ -70,7 +68,7 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen constructor( protected linkService: LinkService, protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService, + public dsoNameService: DSONameService, protected objectCache: ObjectCacheService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { @@ -111,6 +109,8 @@ export class PoolSearchResultListElementComponent extends SearchResultListElemen ngOnDestroy() { // This ensures the object is removed from cache, when action is performed on task - this.objectCache.remove(this.dso._links.workflowitem.href); + if (hasValue(this.dso)) { + this.objectCache.remove(this.dso._links.workflowitem.href); + } } } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html index c3db054f8c..ffc3df53f8 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.html @@ -1,12 +1,8 @@ -
-
- -
-
+ [object]="derivedSearchResult$ | async" [linkType]="LinkTypes.None" + [context]="badgeContext">
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.spec.ts index 576b103ea7..52a13a69e1 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.spec.ts @@ -10,7 +10,6 @@ import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; import { getMockLinkService } from '../../../mocks/link-service.mock'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { WorkflowItemSearchResult } from '../../../object-collection/shared/workflow-item-search-result.model'; import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils'; import { TruncatableService } from '../../../truncatable/truncatable.service'; @@ -19,6 +18,7 @@ import { By } from '@angular/platform-browser'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; import { APP_CONFIG } from '../../../../../config/app-config.interface'; +import { Context } from '../../../../core/shared/context.model'; let component: WorkflowItemSearchResultListElementComponent; let fixture: ComponentFixture; @@ -105,8 +105,8 @@ describe('WorkflowItemSearchResultListElementComponent', () => { }); }); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.WORKFLOW); + it('should have the correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceWorkflow); }); it('should forward workflowitem-actions processCompleted event to the reloadedObject event emitter', fakeAsync(() => { diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts index 11d4e18137..6218f69490 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component.ts @@ -8,7 +8,6 @@ import { Item } from '../../../../core/shared/item.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { WorkflowItemSearchResult } from '../../../object-collection/shared/workflow-item-search-result.model'; import { TruncatableService } from '../../../truncatable/truncatable.service'; import { followLink } from '../../../utils/follow-link-config.model'; @@ -18,6 +17,7 @@ import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interfac import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; import { CollectionElementLinkType } from '../../../object-collection/collection-element-link.type'; +import { Context } from '../../../../core/shared/context.model'; /** * This component renders workflowitem object for the search result in the list view. @@ -40,9 +40,9 @@ export class WorkflowItemSearchResultListElementComponent extends SearchResultLi derivedSearchResult$: Observable; /** - * Represent item's status + * Represents the badge context */ - public status = MyDspaceItemStatusType.WORKFLOW; + public badgeContext = Context.MyDSpaceWorkflow; /** * Display thumbnails if required by configuration @@ -52,7 +52,7 @@ export class WorkflowItemSearchResultListElementComponent extends SearchResultLi constructor( protected truncatableService: TruncatableService, protected linkService: LinkService, - protected dsoNameService: DSONameService, + public dsoNameService: DSONameService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { super(truncatableService, dsoNameService, appConfig); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html index 4e86132df6..724db2892d 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.html @@ -1,12 +1,8 @@ -
-
- -
-
+ [object]="derivedSearchResult$ | async" [linkType]="LinkTypes.None" + [context]="badgeContext">
diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.spec.ts index 2d64b1b581..54344bb879 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.spec.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { waitForAsync, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { of as observableOf } from 'rxjs'; @@ -10,7 +10,6 @@ import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; import { getMockLinkService } from '../../../mocks/link-service.mock'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { WorkflowItemSearchResult } from '../../../object-collection/shared/workflow-item-search-result.model'; import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils'; import { TruncatableService } from '../../../truncatable/truncatable.service'; @@ -19,6 +18,7 @@ import { By } from '@angular/platform-browser'; import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service'; import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock'; import { APP_CONFIG } from '../../../../../config/app-config.interface'; +import { Context } from '../../../../core/shared/context.model'; let component: WorkspaceItemSearchResultListElementComponent; let fixture: ComponentFixture; @@ -104,8 +104,8 @@ describe('WorkspaceItemSearchResultListElementComponent', () => { }); }); - it('should have properly status', () => { - expect(component.status).toEqual(MyDspaceItemStatusType.WORKSPACE); + it('should have correct badge context', () => { + expect(component.badgeContext).toEqual(Context.MyDSpaceWorkspace); }); it('should forward workspaceitem-actions processCompleted event to the reloadedObject event emitter', fakeAsync(() => { diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts index 08ac896035..7ff9a2ef3d 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component.ts @@ -7,7 +7,6 @@ import { Item } from '../../../../core/shared/item.model'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { WorkspaceItem } from '../../../../core/submission/models/workspaceitem.model'; import { listableObjectComponent } from '../../../object-collection/shared/listable-object/listable-object.decorator'; -import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { WorkspaceItemSearchResult } from '../../../object-collection/shared/workspace-item-search-result.model'; import { TruncatableService } from '../../../truncatable/truncatable.service'; import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component'; @@ -18,6 +17,7 @@ import { map } from 'rxjs/operators'; import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; import { CollectionElementLinkType } from '../../../object-collection/collection-element-link.type'; import { followLink } from '../../../utils/follow-link-config.model'; +import { Context } from '../../../../core/shared/context.model'; /** * This component renders workspaceitem object for the search result in the list view. @@ -40,9 +40,9 @@ export class WorkspaceItemSearchResultListElementComponent extends SearchResult derivedSearchResult$: Observable; /** - * Represent item's status + * Represents the badge context */ - status = MyDspaceItemStatusType.WORKSPACE; + public badgeContext = Context.MyDSpaceWorkspace; /** * Display thumbnails if required by configuration @@ -52,7 +52,7 @@ export class WorkspaceItemSearchResultListElementComponent extends SearchResult constructor( protected truncatableService: TruncatableService, protected linkService: LinkService, - protected dsoNameService: DSONameService, + public dsoNameService: DSONameService, @Inject(APP_CONFIG) protected appConfig: AppConfig ) { super(truncatableService, dsoNameService, appConfig); diff --git a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html index 83a8f4fdfa..6856c63cce 100644 --- a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.html @@ -1,7 +1,7 @@
- +
diff --git a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html index 22be357a53..037b62e736 100644 --- a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.html @@ -1,7 +1,7 @@
- +
diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html index 2b643aed9d..88d6ab6e07 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html @@ -12,8 +12,7 @@
- - +
diff --git a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts index e56b7e970a..e13be91e63 100644 --- a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts @@ -22,9 +22,9 @@ export class SearchResultListElementComponent, K exten dsoTitle: string; public constructor(protected truncatableService: TruncatableService, - protected dsoNameService: DSONameService, + public dsoNameService: DSONameService, @Inject(APP_CONFIG) protected appConfig?: AppConfig) { - super(); + super(dsoNameService); } /** diff --git a/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts b/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts index c87559e11d..0ffe2d58b4 100644 --- a/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts +++ b/src/app/shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.ts @@ -35,7 +35,7 @@ export class SidebarSearchListElementComponent, K exte public constructor(protected truncatableService: TruncatableService, protected linkService: LinkService, - protected dsoNameService: DSONameService + public dsoNameService: DSONameService, ) { super(truncatableService, dsoNameService, null); } diff --git a/src/app/shared/object-select/collection-select/collection-select.component.html b/src/app/shared/object-select/collection-select/collection-select.component.html index 84577f645e..9b87f69c04 100644 --- a/src/app/shared/object-select/collection-select/collection-select.component.html +++ b/src/app/shared/object-select/collection-select/collection-select.component.html @@ -18,7 +18,7 @@ - {{collection.name}} + {{ dsoNameService.getName(collection) }} diff --git a/src/app/shared/object-select/collection-select/collection-select.component.scss b/src/app/shared/object-select/collection-select/collection-select.component.scss new file mode 100644 index 0000000000..b505f2895d --- /dev/null +++ b/src/app/shared/object-select/collection-select/collection-select.component.scss @@ -0,0 +1,3 @@ +table tr th:first-of-type, table tr td:first-of-type, { + width: 1rem !important; +} diff --git a/src/app/shared/object-select/collection-select/collection-select.component.ts b/src/app/shared/object-select/collection-select/collection-select.component.ts index a02305f116..2d36f80274 100644 --- a/src/app/shared/object-select/collection-select/collection-select.component.ts +++ b/src/app/shared/object-select/collection-select/collection-select.component.ts @@ -4,10 +4,12 @@ import { ObjectSelectComponent } from '../object-select/object-select.component' import { isNotEmpty } from '../../empty.util'; import { ObjectSelectService } from '../object-select.service'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-collection-select', - templateUrl: './collection-select.component.html' + templateUrl: './collection-select.component.html', + styleUrls: ['./collection-select.component.scss'], }) /** @@ -15,8 +17,11 @@ import { AuthorizationDataService } from '../../../core/data/feature-authorizati */ export class CollectionSelectComponent extends ObjectSelectComponent { - constructor(protected objectSelectService: ObjectSelectService, - protected authorizationService: AuthorizationDataService) { + constructor( + protected objectSelectService: ObjectSelectService, + protected authorizationService: AuthorizationDataService, + public dsoNameService: DSONameService, + ) { super(objectSelectService, authorizationService); } diff --git a/src/app/shared/object-select/item-select/item-select.component.html b/src/app/shared/object-select/item-select/item-select.component.html index b7fce78289..7f8ff943a3 100644 --- a/src/app/shared/object-select/item-select/item-select.component.html +++ b/src/app/shared/object-select/item-select/item-select.component.html @@ -22,11 +22,13 @@ - {{collection?.name}} + + {{ dsoNameService.getName(collection) }} + {{item.firstMetadataValue(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])}} - {{item.firstMetadataValue("dc.title")}} + {{ dsoNameService.getName(item) }} diff --git a/src/app/shared/object-select/item-select/item-select.component.ts b/src/app/shared/object-select/item-select/item-select.component.ts index 5cf32c2953..dd0266ff83 100644 --- a/src/app/shared/object-select/item-select/item-select.component.ts +++ b/src/app/shared/object-select/item-select/item-select.component.ts @@ -8,6 +8,7 @@ import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators import { map } from 'rxjs/operators'; import { getItemPageRoute } from '../../../item-page/item-page-routing-paths'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-item-select', @@ -34,8 +35,11 @@ export class ItemSelectComponent extends ObjectSelectComponent { [itemId: string]: string }>; - constructor(protected objectSelectService: ObjectSelectService, - protected authorizationService: AuthorizationDataService ) { + constructor( + protected objectSelectService: ObjectSelectService, + protected authorizationService: AuthorizationDataService, + public dsoNameService: DSONameService, + ) { super(objectSelectService, authorizationService); } diff --git a/src/app/shared/search-form/search-form.component.html b/src/app/shared/search-form/search-form.component.html index 226962cc61..f916785804 100644 --- a/src/app/shared/search-form/search-form.component.html +++ b/src/app/shared/search-form/search-form.component.html @@ -2,9 +2,14 @@
- +
- diff --git a/src/app/shared/search-form/search-form.component.spec.ts b/src/app/shared/search-form/search-form.component.spec.ts index 4b5844f660..584b7c5584 100644 --- a/src/app/shared/search-form/search-form.component.spec.ts +++ b/src/app/shared/search-form/search-form.component.spec.ts @@ -33,7 +33,7 @@ describe('SearchFormComponent', () => { }; beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ + return TestBed.configureTestingModule({ imports: [FormsModule, RouterTestingModule, TranslateModule.forRoot()], providers: [ { provide: Router, useValue: router }, @@ -96,7 +96,7 @@ describe('SearchFormComponent', () => { tick(); const scopeSelect = de.query(By.css('.scope-button')).nativeElement; - expect(scopeSelect.textContent).toBe(testCommunity.name); + expect(scopeSelect.textContent).toContain('Sample Community'); })); describe('updateSearch', () => { @@ -172,32 +172,9 @@ describe('SearchFormComponent', () => { expect(comp.updateSearch).toHaveBeenCalledWith(searchQuery); }); }); - - // it('should call updateSearch when clicking the submit button with correct parameters', fakeAsync(() => { - // comp.query = 'Test String' - // fixture.detectChanges(); - // spyOn(comp, 'updateSearch').and.callThrough(); - // fixture.detectChanges(); - // - // const submit = de.query(By.css('button.search-button')).nativeElement; - // const scope = '123456'; - // const query = 'test'; - // const select = de.query(By.css('select')).nativeElement; - // const input = de.query(By.css('input')).nativeElement; - // - // tick(); - // select.value = scope; - // input.value = query; - // - // fixture.detectChanges(); - // - // submit.click(); - // - // expect(comp.updateSearch).toHaveBeenCalledWith({ scope: scope, query: query }); - // })); }); -export const objects: DSpaceObject[] = [ +const objects: DSpaceObject[] = [ Object.assign(new Community(), { logo: { self: { diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts index 9a569d1bb2..151ded6f9e 100644 --- a/src/app/shared/search-form/search-form.component.ts +++ b/src/app/shared/search-form/search-form.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, OnChanges } from '@angular/core'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { Router } from '@angular/router'; import { isNotEmpty } from '../empty.util'; @@ -12,23 +12,17 @@ import { take } from 'rxjs/operators'; import { BehaviorSubject } from 'rxjs'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; - -/** - * This component renders a simple item page. - * The route parameter 'id' is used to request the item it represents. - * All fields of the item that should be displayed, are defined in its template. - */ +import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; @Component({ selector: 'ds-search-form', styleUrls: ['./search-form.component.scss'], templateUrl: './search-form.component.html' }) - /** * Component that represents the search form */ -export class SearchFormComponent implements OnInit { +export class SearchFormComponent implements OnChanges { /** * The search query */ @@ -81,13 +75,14 @@ export class SearchFormComponent implements OnInit { protected searchConfig: SearchConfigurationService, protected modalService: NgbModal, protected dsoService: DSpaceObjectDataService, + public dsoNameService: DSONameService, ) { } /** * Retrieve the scope object from the URL so we can show its name */ - ngOnInit(): void { + ngOnChanges(): void { if (isNotEmpty(this.scope)) { this.dsoService.findById(this.scope).pipe(getFirstSucceededRemoteDataPayload()) .subscribe((scope: DSpaceObject) => this.selectedScope.next(scope)); @@ -127,13 +122,6 @@ export class SearchFormComponent implements OnInit { }); } - /** - * For usage of the isNotEmpty function in the template - */ - isNotEmpty(object: any) { - return isNotEmpty(object); - } - /** * @returns {string} The base path to the search page, or the current page when inPlaceSearch is true */ diff --git a/src/app/shared/search/search-filters/themed-search-filters.component.ts b/src/app/shared/search/search-filters/themed-search-filters.component.ts new file mode 100644 index 0000000000..a9a9c10e51 --- /dev/null +++ b/src/app/shared/search/search-filters/themed-search-filters.component.ts @@ -0,0 +1,38 @@ +import { Component, Input } from '@angular/core'; +import { ThemedComponent } from '../../theme-support/themed.component'; +import { SearchFiltersComponent } from './search-filters.component'; +import { Observable } from 'rxjs/internal/Observable'; +import { RemoteData } from '../../../core/data/remote-data'; +import { SearchFilterConfig } from '../models/search-filter-config.model'; + +/** + * Themed wrapper for SearchFiltersComponent + */ +@Component({ + selector: 'ds-themed-search-filters', + styleUrls: [], + templateUrl: '../../theme-support/themed.component.html', +}) +export class ThemedSearchFiltersComponent extends ThemedComponent { + + @Input() currentConfiguration; + @Input() currentScope: string; + @Input() inPlaceSearch; + @Input() refreshFilters: Observable; + @Input() filters: Observable>; + + protected inAndOutputNames: (keyof SearchFiltersComponent & keyof this)[] = [ + 'filters', 'currentConfiguration', 'currentScope', 'inPlaceSearch', 'refreshFilters']; + + protected getComponentName(): string { + return 'SearchFiltersComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../../themes/${themeName}/app/shared/search/search-filters/search-filters.component`); + } + + protected importUnthemedComponent(): Promise { + return import('./search-filters.component'); + } +} diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html index 59d84ee383..f489de5d28 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.html +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html @@ -17,11 +17,11 @@ [defaultConfiguration]="configuration" [inPlaceSearch]="inPlaceSearch" (changeConfiguration)="changeConfiguration.emit($event)"> - + [inPlaceSearch]="inPlaceSearch">
diff --git a/src/app/shared/search/search-sidebar/themed-search-sidebar.component.ts b/src/app/shared/search/search-sidebar/themed-search-sidebar.component.ts new file mode 100644 index 0000000000..935f797532 --- /dev/null +++ b/src/app/shared/search/search-sidebar/themed-search-sidebar.component.ts @@ -0,0 +1,54 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { ThemedComponent } from '../../theme-support/themed.component'; +import { SearchSidebarComponent } from './search-sidebar.component'; +import { SearchConfigurationOption } from '../search-switch-configuration/search-configuration-option.model'; +import { SortOptions } from '../../../core/cache/models/sort-options.model'; +import { ViewMode } from '../../../core/shared/view-mode.model'; +import { PaginatedSearchOptions } from '../models/paginated-search-options.model'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { RemoteData } from '../../../core/data/remote-data'; +import { SearchFilterConfig } from '../models/search-filter-config.model'; + +/** + * Themed wrapper for SearchSidebarComponent + */ +@Component({ + selector: 'ds-themed-search-sidebar', + styleUrls: [], + templateUrl: '../../theme-support/themed.component.html', +}) +export class ThemedSearchSidebarComponent extends ThemedComponent { + + @Input() configuration; + @Input() configurationList: SearchConfigurationOption[]; + @Input() currentScope: string; + @Input() currentSortOption: SortOptions; + @Input() filters: Observable>; + @Input() resultCount; + @Input() viewModeList: ViewMode[]; + @Input() showViewModes = true; + @Input() inPlaceSearch; + @Input() searchOptions: PaginatedSearchOptions; + @Input() sortOptionsList: SortOptions[]; + @Input() refreshFilters: BehaviorSubject; + @Output() toggleSidebar = new EventEmitter(); + @Output() changeConfiguration: EventEmitter = new EventEmitter(); + @Output() changeViewMode: EventEmitter = new EventEmitter(); + + protected inAndOutputNames: (keyof SearchSidebarComponent & keyof this)[] = [ + 'configuration', 'configurationList', 'currentScope', 'currentSortOption', + 'resultCount', 'filters', 'viewModeList', 'showViewModes', 'inPlaceSearch', + 'searchOptions', 'sortOptionsList', 'refreshFilters', 'toggleSidebar', 'changeConfiguration', 'changeViewMode']; + + protected getComponentName(): string { + return 'SearchSidebarComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../../themes/${themeName}/app/shared/search/search-sidebar/search-sidebar.component`); + } + + protected importUnthemedComponent(): Promise { + return import('./search-sidebar.component'); + } +} diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index ed834c65bc..e7523cd277 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -46,7 +46,7 @@ - - + - + diff --git a/src/app/shared/search/search.component.spec.ts b/src/app/shared/search/search.component.spec.ts index 3f00cf354f..d0d9bdda86 100644 --- a/src/app/shared/search/search.component.spec.ts +++ b/src/app/shared/search/search.component.spec.ts @@ -31,6 +31,8 @@ import { SearchObjects } from './models/search-objects.model'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { SearchFilterConfig } from './models/search-filter-config.model'; import { FilterType } from './models/filter-type.model'; +import { getCommunityPageRoute } from '../../community-page/community-page-routing-paths'; +import { getCollectionPageRoute } from '../../collection-page/collection-page-routing-paths'; let comp: SearchComponent; let fixture: ComponentFixture; @@ -101,8 +103,9 @@ const searchServiceStub = jasmine.createSpyObj('SearchService', { search: mockResultsRD$, getSearchLink: '/search', getScopes: observableOf(['test-scope']), - getSearchConfigurationFor: createSuccessfulRemoteDataObject$(searchConfig) -}); + getSearchConfigurationFor: createSuccessfulRemoteDataObject$(searchConfig), + trackSearch: {}, +}) as SearchService; const configurationParam = 'default'; const queryParam = 'test query'; const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; @@ -327,4 +330,64 @@ describe('SearchComponent', () => { })); }); + + describe('getDsoUUIDFromUrl', () => { + let url: string; + let result: string; + + describe('when the navigated URL is an entity route', () => { + beforeEach(() => { + url = '/entities/publication/9a364471-3f19-4e7b-916a-a24a44ff48e3'; + result = (comp as any).getDsoUUIDFromUrl(url); + }); + + it('should return the UUID', () => { + expect(result).toEqual('9a364471-3f19-4e7b-916a-a24a44ff48e3'); + }); + }); + + describe('when the navigated URL is a community route', () => { + beforeEach(() => { + url = `${getCommunityPageRoute('9a364471-3f19-4e7b-916a-a24a44ff48e3')}`; + result = (comp as any).getDsoUUIDFromUrl(url); + }); + + it('should return the UUID', () => { + expect(result).toEqual('9a364471-3f19-4e7b-916a-a24a44ff48e3'); + }); + }); + + describe('when the navigated URL is a collection route', () => { + beforeEach(() => { + url = `${getCollectionPageRoute('9a364471-3f19-4e7b-916a-a24a44ff48e3')}`; + result = (comp as any).getDsoUUIDFromUrl(url); + }); + + it('should return the UUID', () => { + expect(result).toEqual('9a364471-3f19-4e7b-916a-a24a44ff48e3'); + }); + }); + + describe('when the navigated URL is an item route', () => { + beforeEach(() => { + url = '/items/9a364471-3f19-4e7b-916a-a24a44ff48e3'; + result = (comp as any).getDsoUUIDFromUrl(url); + }); + + it('should return the UUID', () => { + expect(result).toEqual('9a364471-3f19-4e7b-916a-a24a44ff48e3'); + }); + }); + + describe('when the navigated URL is an invalid route', () => { + beforeEach(() => { + url = '/invalid/object/route/9a364471-3f19-4e7b-916a-a24a44ff48e3'; + result = (comp as any).getDsoUUIDFromUrl(url); + }); + + it('should return null', () => { + expect(result).toBeNull(); + }); + }); + }); }); diff --git a/src/app/shared/search/search.component.ts b/src/app/shared/search/search.component.ts index a5b9fb9c7d..2195d428c9 100644 --- a/src/app/shared/search/search.component.ts +++ b/src/app/shared/search/search.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core'; -import { Router } from '@angular/router'; +import { NavigationStart, Router } from '@angular/router'; import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators'; @@ -11,7 +11,7 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { pushInOut } from '../animations/push'; import { HostWindowService } from '../host-window.service'; import { SidebarService } from '../sidebar/sidebar.service'; -import { hasValue } from '../empty.util'; +import { hasValue, hasValueOperator, isNotEmpty } from '../empty.util'; import { RouteService } from '../../core/services/route.service'; import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-page.component'; import { PaginatedSearchOptions } from './models/paginated-search-options.model'; @@ -35,6 +35,9 @@ import { environment } from 'src/environments/environment'; import { SubmissionObject } from '../../core/submission/models/submission-object.model'; import { SearchFilterConfig } from './models/search-filter-config.model'; import { WorkspaceItem } from '../..//core/submission/models/workspaceitem.model'; +import { ITEM_MODULE_PATH } from '../../item-page/item-page-routing-paths'; +import { COLLECTION_MODULE_PATH } from '../../collection-page/collection-page-routing-paths'; +import { COMMUNITY_MODULE_PATH } from '../../community-page/community-page-routing-paths'; @Component({ selector: 'ds-search', @@ -229,9 +232,21 @@ export class SearchComponent implements OnInit { searchLink: string; /** - * Subscription to unsubscribe from + * Regex to match UUIDs */ - sub: Subscription; + uuidRegex = /\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/g; + + /** + * List of paths that are considered to be the start of a route to an object page (excluding "/", e.g. "items") + * These are expected to end on an object UUID + * If they match the route we're navigating to, an object property will be added to the search event sent + */ + allowedObjectPaths: string[] = ['entities', ITEM_MODULE_PATH, COLLECTION_MODULE_PATH, COMMUNITY_MODULE_PATH]; + + /** + * Subscriptions to unsubscribe from + */ + subs: Subscription[] = []; /** * Emits an event with the current search result entries @@ -301,7 +316,7 @@ export class SearchComponent implements OnInit { ); const searchOptions$: Observable = this.getSearchOptions().pipe(distinctUntilChanged()); - this.sub = combineLatest([configuration$, searchSortOptions$, searchOptions$, sortOption$]).pipe( + this.subs.push(combineLatest([configuration$, searchSortOptions$, searchOptions$, sortOption$]).pipe( filter(([configuration, searchSortOptions, searchOptions, sortOption]: [string, SortOptions[], PaginatedSearchOptions, SortOptions]) => { // filter for search options related to instanced paginated id return searchOptions.pagination.id === this.paginationId; @@ -332,7 +347,9 @@ export class SearchComponent implements OnInit { this.retrieveSearchResults(newSearchOptions); this.retrieveFilters(searchOptions); } - }); + })); + + this.subscribeToRoutingEvents(); } /** @@ -374,12 +391,10 @@ export class SearchComponent implements OnInit { } /** - * Unsubscribe from the subscription + * Unsubscribe from the subscriptions */ ngOnDestroy(): void { - if (hasValue(this.sub)) { - this.sub.unsubscribe(); - } + this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); } /** @@ -440,6 +455,43 @@ export class SearchComponent implements OnInit { }); } + /** + * Subscribe to routing events to detect when a user moves away from the search page + * When the user is routing to an object page, it needs to send out a separate search event containing that object's UUID + * This method should only be called once and is essentially what SearchTrackingComponent used to do (now removed) + * @private + */ + private subscribeToRoutingEvents() { + this.subs.push( + this.router.events.pipe( + filter((event) => event instanceof NavigationStart), + map((event: NavigationStart) => this.getDsoUUIDFromUrl(event.url)), + hasValueOperator(), + ).subscribe((uuid) => { + if (this.resultsRD$.value.hasSucceeded) { + this.service.trackSearch(this.searchOptions$.value, this.resultsRD$.value.payload as SearchObjects, uuid); + } + }), + ); + } + + /** + * Get the UUID from a DSO url + * Return null if the url isn't an object page (allowedObjectPaths) or the UUID couldn't be found + * @param url + */ + private getDsoUUIDFromUrl(url: string): string { + if (isNotEmpty(url)) { + if (this.allowedObjectPaths.some((path) => url.startsWith(`/${path}`))) { + const uuid = url.substring(url.lastIndexOf('/') + 1); + if (uuid.match(this.uuidRegex)) { + return uuid; + } + } + } + return null; + } + /** * Check if the sidebar is collapsed * @returns {Observable} emits true if the sidebar is currently collapsed, false if it is expanded diff --git a/src/app/shared/search/search.module.ts b/src/app/shared/search/search.module.ts index 713b9925a6..69500999a8 100644 --- a/src/app/shared/search/search.module.ts +++ b/src/app/shared/search/search.module.ts @@ -32,6 +32,8 @@ import { ThemedSearchComponent } from './themed-search.component'; import { ThemedSearchResultsComponent } from './search-results/themed-search-results.component'; import { ThemedSearchSettingsComponent } from './search-settings/themed-search-settings.component'; import { NouisliderModule } from 'ng2-nouislider'; +import { ThemedSearchFiltersComponent } from './search-filters/themed-search-filters.component'; +import { ThemedSearchSidebarComponent } from './search-sidebar/themed-search-sidebar.component'; const COMPONENTS = [ SearchComponent, @@ -58,6 +60,8 @@ const COMPONENTS = [ ThemedConfigurationSearchPageComponent, ThemedSearchResultsComponent, ThemedSearchSettingsComponent, + ThemedSearchFiltersComponent, + ThemedSearchSidebarComponent, ]; const ENTRY_COMPONENTS = [ diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index dafbcb703d..5245f244c4 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -50,6 +50,7 @@ import { ErrorComponent } from './error/error.component'; import { LoadingComponent } from './loading/loading.component'; import { PaginationComponent } from './pagination/pagination.component'; import { ThumbnailComponent } from '../thumbnail/thumbnail.component'; +import { ThemedThumbnailComponent } from '../thumbnail/themed-thumbnail.component'; import { SearchFormComponent } from './search-form/search-form.component'; import { ThemedSearchFormComponent } from './search-form/themed-search-form.component'; import { @@ -164,8 +165,8 @@ import { DsoInputSuggestionsComponent } from './input-suggestions/dso-input-suggestions/dso-input-suggestions.component'; import { ItemGridElementComponent } from './object-grid/item-grid-element/item-types/item/item-grid-element.component'; -import { TypeBadgeComponent } from './object-list/type-badge/type-badge.component'; -import { AccessStatusBadgeComponent } from './object-list/access-status-badge/access-status-badge.component'; +import { TypeBadgeComponent } from './object-collection/shared/badges/type-badge/type-badge.component'; +import { AccessStatusBadgeComponent } from './object-collection/shared/badges/access-status-badge/access-status-badge.component'; import { MetadataRepresentationLoaderComponent } from './metadata-representation/metadata-representation-loader.component'; @@ -251,6 +252,16 @@ import { } from './object-list/listable-notification-object/listable-notification-object.component'; import { ThemedCollectionDropdownComponent } from './collection-dropdown/themed-collection-dropdown.component'; import { MetadataFieldWrapperComponent } from './metadata-field-wrapper/metadata-field-wrapper.component'; + +import { StatusBadgeComponent } from './object-collection/shared/badges/status-badge/status-badge.component'; +import { BadgesComponent } from './object-collection/shared/badges/badges.component'; +import { ThemedBadgesComponent } from './object-collection/shared/badges/themed-badges.component'; +import { ThemedStatusBadgeComponent } from './object-collection/shared/badges/status-badge/themed-status-badge.component'; +import { ThemedTypeBadgeComponent } from './object-collection/shared/badges/type-badge/themed-type-badge.component'; +import { ThemedMyDSpaceStatusBadgeComponent } from './object-collection/shared/badges/my-dspace-status-badge/themed-my-dspace-status-badge.component'; +import { ThemedAccessStatusBadgeComponent } from './object-collection/shared/badges/access-status-badge/themed-access-status-badge.component'; +import { MyDSpaceStatusBadgeComponent } from './object-collection/shared/badges/my-dspace-status-badge/my-dspace-status-badge.component'; + import { ShortNumberPipe } from './utils/short-number.pipe'; import { LogInExternalProviderComponent @@ -338,6 +349,9 @@ const COMPONENTS = [ PageWithSidebarComponent, SidebarDropdownComponent, ThumbnailComponent, + ThemedThumbnailComponent, + MyDSpaceStatusBadgeComponent, + ThemedMyDSpaceStatusBadgeComponent, ViewModeSwitchComponent, TruncatableComponent, TruncatablePartComponent, @@ -353,6 +367,13 @@ const COMPONENTS = [ ComcolMetadataComponent, TypeBadgeComponent, AccessStatusBadgeComponent, + ThemedAccessStatusBadgeComponent, + ThemedTypeBadgeComponent, + StatusBadgeComponent, + ThemedStatusBadgeComponent, + BadgesComponent, + ThemedBadgesComponent, + ItemSelectComponent, CollectionSelectComponent, MetadataRepresentationLoaderComponent, diff --git a/src/app/shared/starts-with/date/starts-with-date.component.ts b/src/app/shared/starts-with/date/starts-with-date.component.ts index d65d12b413..89d9361b6a 100644 --- a/src/app/shared/starts-with/date/starts-with-date.component.ts +++ b/src/app/shared/starts-with/date/starts-with-date.component.ts @@ -131,7 +131,6 @@ export class StartsWithDateComponent extends StartsWithAbstractComponent { } else { this.startsWithYear = +startsWith; } - this.setStartsWithParam(false); } /** diff --git a/src/app/shared/starts-with/starts-with-abstract.component.ts b/src/app/shared/starts-with/starts-with-abstract.component.ts index 3b35a11269..ad9c56c970 100644 --- a/src/app/shared/starts-with/starts-with-abstract.component.ts +++ b/src/app/shared/starts-with/starts-with-abstract.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Subscription } from 'rxjs'; -import { FormControl, FormGroup } from '@angular/forms'; +import { UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { hasValue } from '../empty.util'; import { PaginationService } from '../../core/pagination/pagination.service'; @@ -21,7 +21,7 @@ export abstract class StartsWithAbstractComponent implements OnInit, OnDestroy { /** * The formdata controlling the StartsWith input */ - formData: FormGroup; + formData: UntypedFormGroup; /** * List of subscriptions @@ -43,8 +43,8 @@ export abstract class StartsWithAbstractComponent implements OnInit, OnDestroy { } }) ); - this.formData = new FormGroup({ - startsWith: new FormControl() + this.formData = new UntypedFormGroup({ + startsWith: new UntypedFormControl() }); } @@ -70,7 +70,6 @@ export abstract class StartsWithAbstractComponent implements OnInit, OnDestroy { */ setStartsWith(startsWith: string) { this.startsWith = startsWith; - this.setStartsWithParam(false); } /** diff --git a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html index a71498f002..287175bfa1 100644 --- a/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html +++ b/src/app/shared/subscriptions/subscription-modal/subscription-modal.component.html @@ -6,8 +6,8 @@