Merge remote-tracking branch 'upstream/main' into w2p-101127_browse-by-controlled-vocabulary-7.6.0-next

Conflicts:
	src/app/browse-by/browse-by-guard.spec.ts
	src/app/shared/object-list/metadata-representation-list-element/browse-link/browse-link-metadata-list-element.component.ts
	src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts
This commit is contained in:
Nona Luypaert
2023-05-24 17:26:53 +02:00
510 changed files with 18368 additions and 5648 deletions

View File

@@ -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.

View File

@@ -31,6 +31,8 @@ jobs:
# When Chrome version is specified, we pin to a specific version of Chrome # When Chrome version is specified, we pin to a specific version of Chrome
# Comment this out to use the latest release # Comment this out to use the latest release
#CHROME_VERSION: "90.0.4430.212-1" #CHROME_VERSION: "90.0.4430.212-1"
# Bump Node heap size (OOM in CI after upgrading to Angular 15)
NODE_OPTIONS: '--max-old-space-size=4096'
strategy: strategy:
# Create a matrix of Node versions to test against (in parallel) # Create a matrix of Node versions to test against (in parallel)
matrix: matrix:

View File

@@ -274,9 +274,18 @@
} }
} }
}, },
"defaultProject": "dspace-angular",
"cli": { "cli": {
"analytics": false, "analytics": false,
"defaultCollection": "@angular-eslint/schematics" "schematicCollections": [
"@angular-eslint/schematics"
]
},
"schematics": {
"@angular-eslint/schematics:application": {
"setParserOptionsProject": true
},
"@angular-eslint/schematics:library": {
"setParserOptionsProject": true
}
} }
} }

View File

@@ -187,6 +187,9 @@ languages:
- code: gd - code: gd
label: Gàidhlig label: Gàidhlig
active: true active: true
- code: it
label: Italiano
active: true
- code: lv - code: lv
label: Latviešu label: Latviešu
active: true active: true
@@ -214,6 +217,9 @@ languages:
- code: tr - code: tr
label: Türkçe label: Türkçe
active: true active: true
- code: vi
label: Tiếng Việt
active: true
- code: kk - code: kk
label: Қазақ label: Қазақ
active: true active: true

View File

@@ -17,9 +17,9 @@
"build:stats": "ng build --stats-json", "build:stats": "ng build --stats-json",
"build:prod": "yarn run build:ssr", "build:prod": "yarn run build:ssr",
"build:ssr": "ng build --configuration production && ng run dspace-angular:server:production", "build:ssr": "ng build --configuration production && ng run dspace-angular:server:production",
"test": "ng test --sourceMap=true --watch=false --configuration test", "test": "ng test --source-map=true --watch=false --configuration test",
"test:watch": "nodemon --exec \"ng test --sourceMap=true --watch=true --configuration test\"", "test:watch": "nodemon --exec \"ng test --source-map=true --watch=true --configuration test\"",
"test:headless": "ng test --sourceMap=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage", "test:headless": "ng test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage",
"lint": "ng lint", "lint": "ng lint",
"lint-fix": "ng lint --fix=true", "lint-fix": "ng lint --fix=true",
"e2e": "ng e2e", "e2e": "ng e2e",
@@ -55,136 +55,136 @@
"ts-node": "10.2.1" "ts-node": "10.2.1"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "~13.3.12", "@angular/animations": "^15.2.8",
"@angular/cdk": "^13.2.6", "@angular/cdk": "^15.2.8",
"@angular/common": "~13.3.12", "@angular/common": "^15.2.8",
"@angular/compiler": "~13.3.12", "@angular/compiler": "^15.2.8",
"@angular/core": "~13.3.12", "@angular/core": "^15.2.8",
"@angular/forms": "~13.3.12", "@angular/forms": "^15.2.8",
"@angular/localize": "13.3.12", "@angular/localize": "15.2.8",
"@angular/platform-browser": "~13.3.12", "@angular/platform-browser": "^15.2.8",
"@angular/platform-browser-dynamic": "~13.3.12", "@angular/platform-browser-dynamic": "^15.2.8",
"@angular/platform-server": "~13.3.12", "@angular/platform-server": "^15.2.8",
"@angular/router": "~13.3.12", "@angular/router": "^15.2.8",
"@babel/runtime": "7.17.2", "@babel/runtime": "7.21.0",
"@kolkov/ngx-gallery": "^2.0.1", "@kolkov/ngx-gallery": "^2.0.1",
"@material-ui/core": "^4.11.0", "@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1", "@material-ui/icons": "^4.11.3",
"@ng-bootstrap/ng-bootstrap": "^11.0.0", "@ng-bootstrap/ng-bootstrap": "^11.0.0",
"@ng-dynamic-forms/core": "^15.0.0", "@ng-dynamic-forms/core": "^15.0.0",
"@ng-dynamic-forms/ui-ng-bootstrap": "^15.0.0", "@ng-dynamic-forms/ui-ng-bootstrap": "^15.0.0",
"@ngrx/effects": "^13.0.2", "@ngrx/effects": "^15.4.0",
"@ngrx/router-store": "^13.0.2", "@ngrx/router-store": "^15.4.0",
"@ngrx/store": "^13.0.2", "@ngrx/store": "^15.4.0",
"@nguniversal/express-engine": "^13.0.2", "@nguniversal/express-engine": "^15.2.1",
"@ngx-translate/core": "^13.0.0", "@ngx-translate/core": "^14.0.0",
"@nicky-lenaers/ngx-scroll-to": "^13.0.0", "@nicky-lenaers/ngx-scroll-to": "^14.0.0",
"@types/grecaptcha": "^3.0.4", "@types/grecaptcha": "^3.0.4",
"angular-idle-preload": "3.0.0", "angular-idle-preload": "3.0.0",
"angulartics2": "^12.0.0", "angulartics2": "^12.2.0",
"axios": "^0.27.2", "axios": "^0.27.2",
"bootstrap": "^4.6.1", "bootstrap": "^4.6.1",
"cerialize": "0.1.18", "cerialize": "0.1.18",
"cli-progress": "^3.8.0", "cli-progress": "^3.12.0",
"colors": "^1.4.0", "colors": "^1.4.0",
"compression": "^1.7.4", "compression": "^1.7.4",
"cookie-parser": "1.4.5", "cookie-parser": "1.4.6",
"core-js": "^3.7.0", "core-js": "^3.30.1",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
"date-fns-tz": "^1.3.7", "date-fns-tz": "^1.3.7",
"deepmerge": "^4.2.2", "deepmerge": "^4.3.1",
"ejs": "^3.1.8", "ejs": "^3.1.9",
"express": "^4.17.1", "express": "^4.18.2",
"express-rate-limit": "^5.1.3", "express-rate-limit": "^5.1.3",
"fast-json-patch": "^3.0.0-1", "fast-json-patch": "^3.1.1",
"filesize": "^6.1.0", "filesize": "^6.1.0",
"http-proxy-middleware": "^1.0.5", "http-proxy-middleware": "^1.0.5",
"isbot": "^3.6.5", "isbot": "^3.6.10",
"js-cookie": "2.2.1", "js-cookie": "2.2.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"json5": "^2.2.2", "json5": "^2.2.3",
"jsonschema": "1.4.0", "jsonschema": "1.4.1",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"klaro": "^0.7.18", "klaro": "^0.7.18",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lru-cache": "^7.14.1", "lru-cache": "^7.14.1",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"markdown-it-mathjax3": "^4.3.1", "markdown-it-mathjax3": "^4.3.2",
"mirador": "^3.3.0", "mirador": "^3.3.0",
"mirador-dl-plugin": "^0.13.0", "mirador-dl-plugin": "^0.13.0",
"mirador-share-plugin": "^0.11.0", "mirador-share-plugin": "^0.11.0",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"ng-mocks": "^13.1.1", "ng-mocks": "^14.10.0",
"ng2-file-upload": "1.4.0", "ng2-file-upload": "1.4.0",
"ng2-nouislider": "^1.8.3", "ng2-nouislider": "^1.8.3",
"ngx-infinite-scroll": "^10.0.1", "ngx-infinite-scroll": "^15.0.0",
"ngx-pagination": "5.0.0", "ngx-pagination": "6.0.3",
"ngx-sortablejs": "^11.1.0", "ngx-sortablejs": "^11.1.0",
"ngx-ui-switch": "^13.0.2", "ngx-ui-switch": "^14.0.3",
"nouislider": "^14.6.3", "nouislider": "^14.6.3",
"pem": "1.14.4", "pem": "1.14.7",
"prop-types": "^15.7.2", "prop-types": "^15.8.1",
"react-copy-to-clipboard": "^5.0.1", "react-copy-to-clipboard": "^5.1.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rxjs": "^7.5.5", "rxjs": "^7.8.0",
"sanitize-html": "^2.7.2", "sanitize-html": "^2.10.0",
"sortablejs": "1.13.0", "sortablejs": "1.15.0",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"webfontloader": "1.6.28", "webfontloader": "1.6.28",
"zone.js": "~0.11.5" "zone.js": "~0.11.5"
}, },
"devDependencies": { "devDependencies": {
"@angular-builders/custom-webpack": "~13.1.0", "@angular-builders/custom-webpack": "~15.0.0",
"@angular-devkit/build-angular": "~13.3.10", "@angular-devkit/build-angular": "^15.2.6",
"@angular-eslint/builder": "13.1.0", "@angular-eslint/builder": "15.2.1",
"@angular-eslint/eslint-plugin": "13.1.0", "@angular-eslint/eslint-plugin": "15.2.1",
"@angular-eslint/eslint-plugin-template": "13.1.0", "@angular-eslint/eslint-plugin-template": "15.2.1",
"@angular-eslint/schematics": "13.1.0", "@angular-eslint/schematics": "15.2.1",
"@angular-eslint/template-parser": "13.1.0", "@angular-eslint/template-parser": "15.2.1",
"@angular/cli": "~13.3.10", "@angular/cli": "^15.2.6",
"@angular/compiler-cli": "~13.3.12", "@angular/compiler-cli": "^15.2.8",
"@angular/language-service": "~13.3.12", "@angular/language-service": "^15.2.8",
"@cypress/schematic": "^1.5.0", "@cypress/schematic": "^1.5.0",
"@fortawesome/fontawesome-free": "^6.2.1", "@fortawesome/fontawesome-free": "^6.4.0",
"@ngrx/store-devtools": "^13.0.2", "@ngrx/store-devtools": "^15.4.0",
"@ngtools/webpack": "^13.2.6", "@ngtools/webpack": "^15.2.6",
"@nguniversal/builders": "^13.1.1", "@nguniversal/builders": "^15.2.1",
"@types/deep-freeze": "0.1.2", "@types/deep-freeze": "0.1.2",
"@types/ejs": "^3.1.1", "@types/ejs": "^3.1.2",
"@types/express": "^4.17.9", "@types/express": "^4.17.17",
"@types/jasmine": "~3.6.0", "@types/jasmine": "~3.6.0",
"@types/js-cookie": "2.2.6", "@types/js-cookie": "2.2.6",
"@types/lodash": "^4.14.165", "@types/lodash": "^4.14.194",
"@types/node": "^14.14.9", "@types/node": "^14.14.9",
"@types/sanitize-html": "^2.6.2", "@types/sanitize-html": "^2.9.0",
"@typescript-eslint/eslint-plugin": "5.11.0", "@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "5.11.0", "@typescript-eslint/parser": "^5.59.1",
"axe-core": "^4.4.3", "axe-core": "^4.7.0",
"compression-webpack-plugin": "^9.2.0", "compression-webpack-plugin": "^9.2.0",
"copy-webpack-plugin": "^6.4.1", "copy-webpack-plugin": "^6.4.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cypress": "12.9.0", "cypress": "12.10.0",
"cypress-axe": "^1.1.0", "cypress-axe": "^1.4.0",
"deep-freeze": "0.0.1", "deep-freeze": "0.0.1",
"eslint": "^8.2.0", "eslint": "^8.39.0",
"eslint-plugin-deprecation": "^1.3.2", "eslint-plugin-deprecation": "^1.4.1",
"eslint-plugin-import": "^2.25.4", "eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsdoc": "^39.6.4", "eslint-plugin-jsdoc": "^39.6.4",
"eslint-plugin-jsonc": "^2.6.0", "eslint-plugin-jsonc": "^2.6.0",
"eslint-plugin-lodash": "^7.4.0", "eslint-plugin-lodash": "^7.4.0",
"eslint-plugin-unused-imports": "^2.0.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-core": "^3.8.0",
"jasmine-marbles": "0.9.2", "jasmine-marbles": "0.9.2",
"karma": "^6.3.14", "karma": "^6.4.2",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.2.0",
"karma-coverage-istanbul-reporter": "~3.0.2", "karma-coverage-istanbul-reporter": "~3.0.3",
"karma-jasmine": "~4.0.0", "karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0", "karma-jasmine-html-reporter": "^1.5.0",
"karma-mocha-reporter": "2.2.5", "karma-mocha-reporter": "2.2.5",
"ngx-mask": "^13.1.7", "ngx-mask": "^13.1.7",
"nodemon": "^2.0.20", "nodemon": "^2.0.22",
"postcss": "^8.1", "postcss": "^8.4",
"postcss-apply": "0.12.0", "postcss-apply": "0.12.0",
"postcss-import": "^14.0.0", "postcss-import": "^14.0.0",
"postcss-loader": "^4.0.3", "postcss-loader": "^4.0.3",
@@ -194,14 +194,14 @@
"react-dom": "^16.14.0", "react-dom": "^16.14.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rxjs-spy": "^8.0.2", "rxjs-spy": "^8.0.2",
"sass": "~1.33.0", "sass": "~1.62.0",
"sass-loader": "^12.6.0", "sass-loader": "^12.6.0",
"sass-resources-loader": "^2.1.1", "sass-resources-loader": "^2.2.5",
"ts-node": "^8.10.2", "ts-node": "^8.10.2",
"typescript": "~4.5.5", "typescript": "~4.8.4",
"webpack": "^5.76.0", "webpack": "5.76.1",
"webpack-bundle-analyzer": "^4.4.0", "webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^4.2.0", "webpack-cli": "^4.2.0",
"webpack-dev-server": "^4.5.0" "webpack-dev-server": "^4.13.3"
} }
} }

View File

@@ -68,18 +68,18 @@
<tr *ngFor="let epersonDto of (ePeopleDto$ | async)?.page" <tr *ngFor="let epersonDto of (ePeopleDto$ | async)?.page"
[ngClass]="{'table-primary' : isActive(epersonDto.eperson) | async}"> [ngClass]="{'table-primary' : isActive(epersonDto.eperson) | async}">
<td>{{epersonDto.eperson.id}}</td> <td>{{epersonDto.eperson.id}}</td>
<td>{{epersonDto.eperson.name}}</td> <td>{{ dsoNameService.getName(epersonDto.eperson) }}</td>
<td>{{epersonDto.eperson.email}}</td> <td>{{epersonDto.eperson.email}}</td>
<td> <td>
<div class="btn-group edit-field"> <div class="btn-group edit-field">
<button (click)="toggleEditEPerson(epersonDto.eperson)" <button (click)="toggleEditEPerson(epersonDto.eperson)"
class="btn btn-outline-primary btn-sm access-control-editEPersonButton" class="btn btn-outline-primary btn-sm access-control-editEPersonButton"
title="{{labelPrefix + 'table.edit.buttons.edit' | translate: {name: epersonDto.eperson.name} }}"> title="{{labelPrefix + 'table.edit.buttons.edit' | translate: { name: dsoNameService.getName(epersonDto.eperson) } }}">
<i class="fas fa-edit fa-fw"></i> <i class="fas fa-edit fa-fw"></i>
</button> </button>
<button [disabled]="!epersonDto.ableToDelete" (click)="deleteEPerson(epersonDto.eperson)" <button [disabled]="!epersonDto.ableToDelete" (click)="deleteEPerson(epersonDto.eperson)"
class="delete-button btn btn-outline-danger btn-sm access-control-deleteEPersonButton" class="delete-button btn btn-outline-danger btn-sm access-control-deleteEPersonButton"
title="{{labelPrefix + 'table.edit.buttons.remove' | translate: {name: epersonDto.eperson.name} }}"> title="{{labelPrefix + 'table.edit.buttons.remove' | translate: { name: dsoNameService.getName(epersonDto.eperson) } }}">
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
</button> </button>
</div> </div>

View File

@@ -1,5 +1,5 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { UntypedFormBuilder } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs'; 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 { PageInfo } from '../../core/shared/page-info.model';
import { NoContent } from '../../core/shared/NoContent.model'; import { NoContent } from '../../core/shared/NoContent.model';
import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationService } from '../../core/pagination/pagination.service';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-epeople-registry', selector: 'ds-epeople-registry',
@@ -89,11 +90,13 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
private translateService: TranslateService, private translateService: TranslateService,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private authorizationService: AuthorizationDataService, private authorizationService: AuthorizationDataService,
private formBuilder: FormBuilder, private formBuilder: UntypedFormBuilder,
private router: Router, private router: Router,
private modalService: NgbModal, private modalService: NgbModal,
private paginationService: PaginationService, private paginationService: PaginationService,
public requestService: RequestService) { public requestService: RequestService,
public dsoNameService: DSONameService,
) {
this.currentSearchQuery = ''; this.currentSearchQuery = '';
this.currentSearchScope = 'metadata'; this.currentSearchScope = 'metadata';
this.searchForm = this.formBuilder.group(({ this.searchForm = this.formBuilder.group(({
@@ -121,7 +124,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
this.subs.push(this.ePeople$.pipe( this.subs.push(this.ePeople$.pipe(
switchMap((epeople: PaginatedList<EPerson>) => { switchMap((epeople: PaginatedList<EPerson>) => {
if (epeople.pageInfo.totalElements > 0) { if (epeople.pageInfo.totalElements > 0) {
return combineLatest(...epeople.page.map((eperson) => { return combineLatest([...epeople.page.map((eperson: EPerson) => {
return this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined).pipe( return this.authorizationService.isAuthorized(FeatureID.CanDelete, hasValue(eperson) ? eperson.self : undefined).pipe(
map((authorized) => { map((authorized) => {
const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel(); const epersonDtoModel: EpersonDtoModel = new EpersonDtoModel();
@@ -130,7 +133,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
return epersonDtoModel; return epersonDtoModel;
}) })
); );
})).pipe(map((dtos: EpersonDtoModel[]) => { })]).pipe(map((dtos: EpersonDtoModel[]) => {
return buildPaginatedList(epeople.pageInfo, dtos); return buildPaginatedList(epeople.pageInfo, dtos);
})); }));
} else { } else {
@@ -237,7 +240,7 @@ export class EPeopleRegistryComponent implements OnInit, OnDestroy {
if (hasValue(ePerson.id)) { if (hasValue(ePerson.id)) {
this.epersonService.deleteEPerson(ePerson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData<NoContent>) => { this.epersonService.deleteEPerson(ePerson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData<NoContent>) => {
if (restResponse.hasSucceeded) { 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 { } else {
this.notificationsService.error('Error occured when trying to delete EPerson with id: ' + ePerson.id + ' with code: ' + restResponse.statusCode + ' and message: ' + restResponse.errorMessage); this.notificationsService.error('Error occured when trying to delete EPerson with id: ' + ePerson.id + ' with code: ' + restResponse.statusCode + ' and message: ' + restResponse.errorMessage);
} }

View File

@@ -65,9 +65,13 @@
<tbody> <tbody>
<tr *ngFor="let group of (groups | async)?.payload?.page"> <tr *ngFor="let group of (groups | async)?.payload?.page">
<td class="align-middle">{{group.id}}</td> <td class="align-middle">{{group.id}}</td>
<td class="align-middle"><a (click)="groupsDataService.startEditingNewGroup(group)" <td class="align-middle">
[routerLink]="[groupsDataService.getGroupEditPageRouterLink(group)]">{{group.name}}</a></td> <a (click)="groupsDataService.startEditingNewGroup(group)"
<td class="align-middle">{{(group.object | async)?.payload?.name}}</td> [routerLink]="[groupsDataService.getGroupEditPageRouterLink(group)]">
{{ dsoNameService.getName(group) }}
</a>
</td>
<td class="align-middle">{{ dsoNameService.getName(undefined) }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -2,7 +2,7 @@ import { Observable, of as observableOf } from 'rxjs';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { 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 { BrowserModule, By } from '@angular/platform-browser';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
@@ -116,9 +116,9 @@ describe('EPersonFormComponent', () => {
const controlModel = model; const controlModel = model;
const controlState = { value: controlModel.value, disabled: controlModel.disabled }; const controlState = { value: controlModel.value, disabled: controlModel.disabled };
const controlOptions = this.createAbstractControlOptions(controlModel.validators, controlModel.asyncValidators, controlModel.updateOn); 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) { createAbstractControlOptions(validatorsConfig = null, asyncValidatorsConfig = null, updateOn = null) {
return { return {

View File

@@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
import { FormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { import {
DynamicCheckboxModel, DynamicCheckboxModel,
DynamicFormControlModel, DynamicFormControlModel,
@@ -37,6 +37,7 @@ import { ValidateEmailNotTaken } from './validators/email-taken.validator';
import { Registration } from '../../../core/shared/registration.model'; import { Registration } from '../../../core/shared/registration.model';
import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service'; import { EpersonRegistrationService } from '../../../core/data/eperson-registration.service';
import { TYPE_REQUEST_FORGOT } from '../../../register-email-form/register-email-form.component'; import { TYPE_REQUEST_FORGOT } from '../../../register-email-form/register-email-form.component';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-eperson-form', selector: 'ds-eperson-form',
@@ -108,7 +109,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
/** /**
* A FormGroup that combines all inputs * A FormGroup that combines all inputs
*/ */
formGroup: FormGroup; formGroup: UntypedFormGroup;
/** /**
* An EventEmitter that's fired whenever the form is being submitted * An EventEmitter that's fired whenever the form is being submitted
@@ -192,6 +193,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
private paginationService: PaginationService, private paginationService: PaginationService,
public requestService: RequestService, public requestService: RequestService,
private epersonRegistrationService: EpersonRegistrationService, private epersonRegistrationService: EpersonRegistrationService,
public dsoNameService: DSONameService,
) { ) {
this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => { this.subs.push(this.epersonService.getActiveEPerson().subscribe((eperson: EPerson) => {
this.epersonInitial = eperson; this.epersonInitial = eperson;
@@ -212,14 +214,14 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
*/ */
initialisePage() { initialisePage() {
observableCombineLatest( observableCombineLatest([
this.translateService.get(`${this.messagePrefix}.firstName`), this.translateService.get(`${this.messagePrefix}.firstName`),
this.translateService.get(`${this.messagePrefix}.lastName`), this.translateService.get(`${this.messagePrefix}.lastName`),
this.translateService.get(`${this.messagePrefix}.email`), this.translateService.get(`${this.messagePrefix}.email`),
this.translateService.get(`${this.messagePrefix}.canLogIn`), this.translateService.get(`${this.messagePrefix}.canLogIn`),
this.translateService.get(`${this.messagePrefix}.requireCertificate`), this.translateService.get(`${this.messagePrefix}.requireCertificate`),
this.translateService.get(`${this.messagePrefix}.emailHint`), this.translateService.get(`${this.messagePrefix}.emailHint`),
).subscribe(([firstName, lastName, email, canLogIn, requireCertificate, emailHint]) => { ]).subscribe(([firstName, lastName, email, canLogIn, requireCertificate, emailHint]) => {
this.firstName = new DynamicInputModel({ this.firstName = new DynamicInputModel({
id: 'firstName', id: 'firstName',
label: firstName, label: firstName,
@@ -386,10 +388,10 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
getFirstCompletedRemoteData() getFirstCompletedRemoteData()
).subscribe((rd: RemoteData<EPerson>) => { ).subscribe((rd: RemoteData<EPerson>) => {
if (rd.hasSucceeded) { if (rd.hasSucceeded) {
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', { name: ePersonToCreate.name })); this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', { name: this.dsoNameService.getName(ePersonToCreate) }));
this.submitForm.emit(ePersonToCreate); this.submitForm.emit(ePersonToCreate);
} else { } 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(); this.cancelForm.emit();
} }
}); });
@@ -425,10 +427,10 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
const response = this.epersonService.updateEPerson(editedEperson); const response = this.epersonService.updateEPerson(editedEperson);
response.pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData<EPerson>) => { response.pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData<EPerson>) => {
if (rd.hasSucceeded) { 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); this.submitForm.emit(editedEperson);
} else { } 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(); this.cancelForm.emit();
} }
}); });
@@ -476,7 +478,7 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
if (hasValue(eperson.id)) { if (hasValue(eperson.id)) {
this.epersonService.deleteEPerson(eperson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData<NoContent>) => { this.epersonService.deleteEPerson(eperson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData<NoContent>) => {
if (restResponse.hasSucceeded) { 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(); this.submitForm.emit();
} else { } else {
this.notificationsService.error('Error occured when trying to delete EPerson with id: ' + eperson.id + ' with code: ' + restResponse.statusCode + ' and message: ' + restResponse.errorMessage); this.notificationsService.error('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<EPerson>) => { .subscribe((list: PaginatedList<EPerson>) => {
if (list.totalElements > 0) { if (list.totalElements > 0) {
this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.' + notificationSection + '.failure.emailInUse', { this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.' + notificationSection + '.failure.emailInUse', {
name: ePerson.name, name: this.dsoNameService.getName(ePerson),
email: ePerson.email email: ePerson.email
})); }));
} }

View File

@@ -26,7 +26,7 @@
<ds-alert *ngIf="groupBeingEdited?.permanent" [type]="AlertTypeEnum.Warning" <ds-alert *ngIf="groupBeingEdited?.permanent" [type]="AlertTypeEnum.Warning"
[content]="messagePrefix + '.alert.permanent'"></ds-alert> [content]="messagePrefix + '.alert.permanent'"></ds-alert>
<ds-alert *ngIf="!(canEdit$ | async) && (groupDataService.getActiveGroup() | async)" [type]="AlertTypeEnum.Warning" <ds-alert *ngIf="!(canEdit$ | async) && (groupDataService.getActiveGroup() | async)" [type]="AlertTypeEnum.Warning"
[content]="(messagePrefix + '.alert.workflowGroup' | translate:{ name: (getLinkedDSO(groupBeingEdited) | async)?.payload?.name, comcol: (getLinkedDSO(groupBeingEdited) | async)?.payload?.type, comcolEditRolesRoute: (getLinkedEditRolesRoute(groupBeingEdited) | async) })"> [content]="(messagePrefix + '.alert.workflowGroup' | translate:{ name: dsoNameService.getName((getLinkedDSO(groupBeingEdited) | async)?.payload), comcol: (getLinkedDSO(groupBeingEdited) | async)?.payload?.type, comcolEditRolesRoute: (getLinkedEditRolesRoute(groupBeingEdited) | async) })">
</ds-alert> </ds-alert>
<ds-form [formId]="formId" <ds-form [formId]="formId"

View File

@@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { NO_ERRORS_SCHEMA } from '@angular/core'; import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { 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 { BrowserModule, By } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@@ -36,6 +36,8 @@ import { NotificationsServiceStub } from '../../../shared/testing/notifications-
import { Operation } from 'fast-json-patch'; import { Operation } from 'fast-json-patch';
import { ValidateGroupExists } from './validators/group-exists.validator'; import { ValidateGroupExists } from './validators/group-exists.validator';
import { NoContent } from '../../../core/shared/NoContent.model'; import { NoContent } from '../../../core/shared/NoContent.model';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../../../shared/mocks/dso-name.service.mock';
describe('GroupFormComponent', () => { describe('GroupFormComponent', () => {
let component: GroupFormComponent; let component: GroupFormComponent;
@@ -130,9 +132,9 @@ describe('GroupFormComponent', () => {
const controlModel = model; const controlModel = model;
const controlState = { value: controlModel.value, disabled: controlModel.disabled }; const controlState = { value: controlModel.value, disabled: controlModel.disabled };
const controlOptions = this.createAbstractControlOptions(controlModel.validators, controlModel.asyncValidators, controlModel.updateOn); 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) { createAbstractControlOptions(validatorsConfig = null, asyncValidatorsConfig = null, updateOn = null) {
return { return {
@@ -188,7 +190,7 @@ describe('GroupFormComponent', () => {
translateService = getMockTranslateService(); translateService = getMockTranslateService();
router = new RouterMock(); router = new RouterMock();
notificationService = new NotificationsServiceStub(); notificationService = new NotificationsServiceStub();
TestBed.configureTestingModule({ return TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
@@ -198,7 +200,8 @@ describe('GroupFormComponent', () => {
}), }),
], ],
declarations: [GroupFormComponent], declarations: [GroupFormComponent],
providers: [GroupFormComponent, providers: [
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: EPersonDataService, useValue: ePersonDataServiceStub },
{ provide: GroupDataService, useValue: groupsDataServiceStub }, { provide: GroupDataService, useValue: groupsDataServiceStub },
{ provide: DSpaceObjectDataService, useValue: dsoDataServiceStub }, { provide: DSpaceObjectDataService, useValue: dsoDataServiceStub },
@@ -240,8 +243,8 @@ describe('GroupFormComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should emit a new group using the correct values', waitForAsync(() => { it('should emit a new group using the correct values', (async () => {
fixture.whenStable().then(() => { await fixture.whenStable().then(() => {
expect(component.submitForm.emit).toHaveBeenCalledWith(expected); expect(component.submitForm.emit).toHaveBeenCalledWith(expected);
}); });
})); }));
@@ -303,8 +306,8 @@ describe('GroupFormComponent', () => {
expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations); expect(groupsDataServiceStub.patch).toHaveBeenCalledWith(expected, operations);
}); });
it('should emit the existing group using the correct new values', waitForAsync(() => { it('should emit the existing group using the correct new values', (async () => {
fixture.whenStable().then(() => { await fixture.whenStable().then(() => {
expect(component.submitForm.emit).toHaveBeenCalledWith(expected2); expect(component.submitForm.emit).toHaveBeenCalledWith(expected2);
}); });
})); }));

View File

@@ -1,5 +1,5 @@
import { Component, EventEmitter, HostListener, OnDestroy, OnInit, Output, ChangeDetectorRef } from '@angular/core'; 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 { ActivatedRoute, Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { import {
@@ -46,6 +46,7 @@ import { followLink } from '../../../shared/utils/follow-link-config.model';
import { NoContent } from '../../../core/shared/NoContent.model'; import { NoContent } from '../../../core/shared/NoContent.model';
import { Operation } from 'fast-json-patch'; import { Operation } from 'fast-json-patch';
import { ValidateGroupExists } from './validators/group-exists.validator'; import { ValidateGroupExists } from './validators/group-exists.validator';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { environment } from '../../../../environments/environment'; import { environment } from '../../../../environments/environment';
@Component({ @Component({
@@ -95,7 +96,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
/** /**
* A FormGroup that combines all inputs * A FormGroup that combines all inputs
*/ */
formGroup: FormGroup; formGroup: UntypedFormGroup;
/** /**
* An EventEmitter that's fired whenever the form is being submitted * An EventEmitter that's fired whenever the form is being submitted
@@ -134,7 +135,8 @@ export class GroupFormComponent implements OnInit, OnDestroy {
groupNameValueChangeSubscribe: Subscription; groupNameValueChangeSubscribe: Subscription;
constructor(public groupDataService: GroupDataService, constructor(
public groupDataService: GroupDataService,
private ePersonDataService: EPersonDataService, private ePersonDataService: EPersonDataService,
private dSpaceObjectDataService: DSpaceObjectDataService, private dSpaceObjectDataService: DSpaceObjectDataService,
private formBuilderService: FormBuilderService, private formBuilderService: FormBuilderService,
@@ -145,7 +147,9 @@ export class GroupFormComponent implements OnInit, OnDestroy {
private authorizationService: AuthorizationDataService, private authorizationService: AuthorizationDataService,
private modalService: NgbModal, private modalService: NgbModal,
public requestService: RequestService, public requestService: RequestService,
protected changeDetectorRef: ChangeDetectorRef) { protected changeDetectorRef: ChangeDetectorRef,
public dsoNameService: DSONameService,
) {
} }
ngOnInit() { ngOnInit() {
@@ -331,7 +335,7 @@ export class GroupFormComponent implements OnInit, OnDestroy {
.subscribe((list: PaginatedList<Group>) => { .subscribe((list: PaginatedList<Group>) => {
if (list.totalElements > 0) { if (list.totalElements > 0) {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.' + notificationSection + '.failure.groupNameInUse', { 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() getFirstCompletedRemoteData()
).subscribe((rd: RemoteData<Group>) => { ).subscribe((rd: RemoteData<Group>) => {
if (rd.hasSucceeded) { if (rd.hasSucceeded) {
this.notificationsService.success(this.translateService.get(this.messagePrefix + '.notification.edited.success', { name: 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); this.submitForm.emit(rd.payload);
} else { } 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(); this.cancelForm.emit();
} }
}); });
@@ -427,11 +431,11 @@ export class GroupFormComponent implements OnInit, OnDestroy {
this.groupDataService.delete(group.id).pipe(getFirstCompletedRemoteData()) this.groupDataService.delete(group.id).pipe(getFirstCompletedRemoteData())
.subscribe((rd: RemoteData<NoContent>) => { .subscribe((rd: RemoteData<NoContent>) => {
if (rd.hasSucceeded) { 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(); this.onCancel();
} else { } else {
this.notificationsService.error( 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 })); this.translateService.get(this.messagePrefix + '.notification.deleted.failure.content', { cause: rd.errorMessage }));
} }
}); });

View File

@@ -57,8 +57,12 @@
<tbody> <tbody>
<tr *ngFor="let ePerson of (ePeopleSearchDtos | async)?.page"> <tr *ngFor="let ePerson of (ePeopleSearchDtos | async)?.page">
<td class="align-middle">{{ePerson.eperson.id}}</td> <td class="align-middle">{{ePerson.eperson.id}}</td>
<td class="align-middle"><a (click)="ePersonDataService.startEditingNewEPerson(ePerson.eperson)" <td class="align-middle">
[routerLink]="[ePersonDataService.getEPeoplePageRouterLink()]">{{ePerson.eperson.name}}</a></td> <a (click)="ePersonDataService.startEditingNewEPerson(ePerson.eperson)"
[routerLink]="[ePersonDataService.getEPeoplePageRouterLink()]">
{{ dsoNameService.getName(ePerson.eperson) }}
</a>
</td>
<td class="align-middle"> <td class="align-middle">
{{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}<br/> {{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}<br/>
{{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }} {{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }}
@@ -69,7 +73,7 @@
(click)="deleteMemberFromGroup(ePerson)" (click)="deleteMemberFromGroup(ePerson)"
[disabled]="actionConfig.remove.disabled" [disabled]="actionConfig.remove.disabled"
[ngClass]="['btn btn-sm', actionConfig.remove.css]" [ngClass]="['btn btn-sm', actionConfig.remove.css]"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: {name: ePerson.eperson.name} }}"> title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}">
<i [ngClass]="actionConfig.remove.icon"></i> <i [ngClass]="actionConfig.remove.icon"></i>
</button> </button>
@@ -77,7 +81,7 @@
(click)="addMemberToGroup(ePerson)" (click)="addMemberToGroup(ePerson)"
[disabled]="actionConfig.add.disabled" [disabled]="actionConfig.add.disabled"
[ngClass]="['btn btn-sm', actionConfig.add.css]" [ngClass]="['btn btn-sm', actionConfig.add.css]"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: {name: ePerson.eperson.name} }}"> title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}">
<i [ngClass]="actionConfig.add.icon"></i> <i [ngClass]="actionConfig.add.icon"></i>
</button> </button>
</div> </div>
@@ -117,8 +121,12 @@
<tbody> <tbody>
<tr *ngFor="let ePerson of (ePeopleMembersOfGroupDtos | async)?.page"> <tr *ngFor="let ePerson of (ePeopleMembersOfGroupDtos | async)?.page">
<td class="align-middle">{{ePerson.eperson.id}}</td> <td class="align-middle">{{ePerson.eperson.id}}</td>
<td class="align-middle"><a (click)="ePersonDataService.startEditingNewEPerson(ePerson.eperson)" <td class="align-middle">
[routerLink]="[ePersonDataService.getEPeoplePageRouterLink()]">{{ePerson.eperson.name}}</a></td> <a (click)="ePersonDataService.startEditingNewEPerson(ePerson.eperson)"
[routerLink]="[ePersonDataService.getEPeoplePageRouterLink()]">
{{ dsoNameService.getName(ePerson.eperson) }}
</a>
</td>
<td class="align-middle"> <td class="align-middle">
{{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}<br/> {{messagePrefix + '.table.email' | translate}}: {{ ePerson.eperson.email ? ePerson.eperson.email : '-' }}<br/>
{{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }} {{messagePrefix + '.table.netid' | translate}}: {{ ePerson.eperson.netid ? ePerson.eperson.netid : '-' }}
@@ -129,14 +137,14 @@
(click)="deleteMemberFromGroup(ePerson)" (click)="deleteMemberFromGroup(ePerson)"
[disabled]="actionConfig.remove.disabled" [disabled]="actionConfig.remove.disabled"
[ngClass]="['btn btn-sm', actionConfig.remove.css]" [ngClass]="['btn btn-sm', actionConfig.remove.css]"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: {name: ePerson.eperson.name} }}"> title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}">
<i [ngClass]="actionConfig.remove.icon"></i> <i [ngClass]="actionConfig.remove.icon"></i>
</button> </button>
<button *ngIf="!ePerson.memberOfGroup" <button *ngIf="!ePerson.memberOfGroup"
(click)="addMemberToGroup(ePerson)" (click)="addMemberToGroup(ePerson)"
[disabled]="actionConfig.add.disabled" [disabled]="actionConfig.add.disabled"
[ngClass]="['btn btn-sm', actionConfig.add.css]" [ngClass]="['btn btn-sm', actionConfig.add.css]"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: {name: ePerson.eperson.name} }}"> title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(ePerson.eperson) } }}">
<i [ngClass]="actionConfig.add.icon"></i> <i [ngClass]="actionConfig.add.icon"></i>
</button> </button>
</div> </div>

View File

@@ -28,6 +28,8 @@ import { NotificationsServiceStub } from '../../../../shared/testing/notificatio
import { RouterMock } from '../../../../shared/mocks/router.mock'; import { RouterMock } from '../../../../shared/mocks/router.mock';
import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationService } from '../../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; import { 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', () => { describe('MembersListComponent', () => {
let component: MembersListComponent; let component: MembersListComponent;
@@ -118,7 +120,7 @@ describe('MembersListComponent', () => {
translateService = getMockTranslateService(); translateService = getMockTranslateService();
paginationService = new PaginationServiceStub(); paginationService = new PaginationServiceStub();
TestBed.configureTestingModule({ return TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
@@ -135,6 +137,7 @@ describe('MembersListComponent', () => {
{ provide: FormBuilderService, useValue: builderService }, { provide: FormBuilderService, useValue: builderService },
{ provide: Router, useValue: new RouterMock() }, { provide: Router, useValue: new RouterMock() },
{ provide: PaginationService, useValue: paginationService }, { provide: PaginationService, useValue: paginationService },
{ provide: DSONameService, useValue: new DSONameServiceMock() },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]
}).compileComponents(); }).compileComponents();

View File

@@ -1,5 +1,5 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { UntypedFormBuilder } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { import {
@@ -27,6 +27,7 @@ import { NotificationsService } from '../../../../shared/notifications/notificat
import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../../../shared/pagination/pagination-component-options.model';
import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model'; import { EpersonDtoModel } from '../../../../core/eperson/models/eperson-dto.model';
import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationService } from '../../../../core/pagination/pagination.service';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
/** /**
* Keys to keep track of specific subscriptions * Keys to keep track of specific subscriptions
@@ -141,9 +142,10 @@ export class MembersListComponent implements OnInit, OnDestroy {
public ePersonDataService: EPersonDataService, public ePersonDataService: EPersonDataService,
protected translateService: TranslateService, protected translateService: TranslateService,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected formBuilder: FormBuilder, protected formBuilder: UntypedFormBuilder,
protected paginationService: PaginationService, protected paginationService: PaginationService,
private router: Router protected router: Router,
public dsoNameService: DSONameService,
) { ) {
this.currentSearchQuery = ''; this.currentSearchQuery = '';
this.currentSearchScope = 'metadata'; this.currentSearchScope = 'metadata';
@@ -253,7 +255,7 @@ export class MembersListComponent implements OnInit, OnDestroy {
this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => {
if (activeGroup != null) { if (activeGroup != null) {
const response = this.groupDataService.deleteMemberFromGroup(activeGroup, ePerson.eperson); const response = this.groupDataService.deleteMemberFromGroup(activeGroup, ePerson.eperson);
this.showNotifications('deleteMember', response, ePerson.eperson.name, activeGroup); this.showNotifications('deleteMember', response, this.dsoNameService.getName(ePerson.eperson), activeGroup);
} else { } else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); 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) => { this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => {
if (activeGroup != null) { if (activeGroup != null) {
const response = this.groupDataService.addMemberToGroup(activeGroup, ePerson.eperson); const response = this.groupDataService.addMemberToGroup(activeGroup, ePerson.eperson);
this.showNotifications('addMember', response, ePerson.eperson.name, activeGroup); this.showNotifications('addMember', response, this.dsoNameService.getName(ePerson.eperson), activeGroup);
} else { } else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup'));
} }

View File

@@ -53,15 +53,19 @@
<tbody> <tbody>
<tr *ngFor="let group of (searchResults$ | async)?.payload?.page"> <tr *ngFor="let group of (searchResults$ | async)?.payload?.page">
<td class="align-middle">{{group.id}}</td> <td class="align-middle">{{group.id}}</td>
<td class="align-middle"><a (click)="groupDataService.startEditingNewGroup(group)" <td class="align-middle">
[routerLink]="[groupDataService.getGroupEditPageRouterLink(group)]">{{group.name}}</a></td> <a (click)="groupDataService.startEditingNewGroup(group)"
<td class="align-middle">{{(group.object | async)?.payload?.name}}</td> [routerLink]="[groupDataService.getGroupEditPageRouterLink(group)]">
{{ dsoNameService.getName(group) }}
</a>
</td>
<td class="align-middle">{{ dsoNameService.getName((group.object | async)?.payload) }}</td>
<td class="align-middle"> <td class="align-middle">
<div class="btn-group edit-field"> <div class="btn-group edit-field">
<button *ngIf="(isSubgroupOfGroup(group) | async) && !(isActiveGroup(group) | async)" <button *ngIf="(isSubgroupOfGroup(group) | async) && !(isActiveGroup(group) | async)"
(click)="deleteSubgroupFromGroup(group)" (click)="deleteSubgroupFromGroup(group)"
class="btn btn-outline-danger btn-sm deleteButton" class="btn btn-outline-danger btn-sm deleteButton"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: {name: group.name} }}"> title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(group) } }}">
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
</button> </button>
@@ -70,7 +74,7 @@
<button *ngIf="!(isSubgroupOfGroup(group) | async) && !(isActiveGroup(group) | async)" <button *ngIf="!(isSubgroupOfGroup(group) | async) && !(isActiveGroup(group) | async)"
(click)="addSubgroupToGroup(group)" (click)="addSubgroupToGroup(group)"
class="btn btn-outline-primary btn-sm addButton" class="btn btn-outline-primary btn-sm addButton"
title="{{messagePrefix + '.table.edit.buttons.add' | translate: {name: group.name} }}"> title="{{messagePrefix + '.table.edit.buttons.add' | translate: { name: dsoNameService.getName(group) } }}">
<i class="fas fa-plus fa-fw"></i> <i class="fas fa-plus fa-fw"></i>
</button> </button>
</div> </div>
@@ -108,14 +112,18 @@
<tbody> <tbody>
<tr *ngFor="let group of (subGroups$ | async)?.payload?.page"> <tr *ngFor="let group of (subGroups$ | async)?.payload?.page">
<td class="align-middle">{{group.id}}</td> <td class="align-middle">{{group.id}}</td>
<td class="align-middle"><a (click)="groupDataService.startEditingNewGroup(group)" <td class="align-middle">
[routerLink]="[groupDataService.getGroupEditPageRouterLink(group)]">{{group.name}}</a></td> <a (click)="groupDataService.startEditingNewGroup(group)"
<td class="align-middle">{{(group.object | async)?.payload?.name}}</td> [routerLink]="[groupDataService.getGroupEditPageRouterLink(group)]">
{{ dsoNameService.getName(group) }}
</a>
</td>
<td class="align-middle">{{ dsoNameService.getName((group.object | async)?.payload)}}</td>
<td class="align-middle"> <td class="align-middle">
<div class="btn-group edit-field"> <div class="btn-group edit-field">
<button (click)="deleteSubgroupFromGroup(group)" <button (click)="deleteSubgroupFromGroup(group)"
class="btn btn-outline-danger btn-sm deleteButton" class="btn btn-outline-danger btn-sm deleteButton"
title="{{messagePrefix + '.table.edit.buttons.remove' | translate: {name: group.name} }}"> title="{{messagePrefix + '.table.edit.buttons.remove' | translate: { name: dsoNameService.getName(group) } }}">
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
</button> </button>
</div> </div>

View File

@@ -29,6 +29,8 @@ import { NotificationsServiceStub } from '../../../../shared/testing/notificatio
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationService } from '../../../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../../../shared/testing/pagination-service.stub'; import { 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', () => { describe('SubgroupsListComponent', () => {
let component: SubgroupsListComponent; let component: SubgroupsListComponent;
@@ -108,6 +110,7 @@ describe('SubgroupsListComponent', () => {
], ],
declarations: [SubgroupsListComponent], declarations: [SubgroupsListComponent],
providers: [SubgroupsListComponent, providers: [SubgroupsListComponent,
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: GroupDataService, useValue: groupsDataServiceStub }, { provide: GroupDataService, useValue: groupsDataServiceStub },
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: FormBuilderService, useValue: builderService }, { provide: FormBuilderService, useValue: builderService },

View File

@@ -1,5 +1,5 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { UntypedFormBuilder } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs'; 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 { NoContent } from '../../../../core/shared/NoContent.model';
import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationService } from '../../../../core/pagination/pagination.service';
import { followLink } from '../../../../shared/utils/follow-link-config.model'; import { followLink } from '../../../../shared/utils/follow-link-config.model';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
/** /**
* Keys to keep track of specific subscriptions * Keys to keep track of specific subscriptions
@@ -86,9 +87,11 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
constructor(public groupDataService: GroupDataService, constructor(public groupDataService: GroupDataService,
private translateService: TranslateService, private translateService: TranslateService,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private formBuilder: FormBuilder, private formBuilder: UntypedFormBuilder,
private paginationService: PaginationService, private paginationService: PaginationService,
private router: Router) { private router: Router,
public dsoNameService: DSONameService,
) {
this.currentSearchQuery = ''; this.currentSearchQuery = '';
} }
@@ -177,7 +180,7 @@ export class SubgroupsListComponent implements OnInit, OnDestroy {
this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => { this.groupDataService.getActiveGroup().pipe(take(1)).subscribe((activeGroup: Group) => {
if (activeGroup != null) { if (activeGroup != null) {
const response = this.groupDataService.deleteSubGroupFromGroup(activeGroup, subgroup); const response = this.groupDataService.deleteSubGroupFromGroup(activeGroup, subgroup);
this.showNotifications('deleteSubgroup', response, subgroup.name, activeGroup); this.showNotifications('deleteSubgroup', response, this.dsoNameService.getName(subgroup), activeGroup);
} else { } else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.noActiveGroup')); 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 != null) {
if (activeGroup.uuid !== subgroup.uuid) { if (activeGroup.uuid !== subgroup.uuid) {
const response = this.groupDataService.addSubGroupToGroup(activeGroup, subgroup); const response = this.groupDataService.addSubGroupToGroup(activeGroup, subgroup);
this.showNotifications('addSubgroup', response, subgroup.name, activeGroup); this.showNotifications('addSubgroup', response, this.dsoNameService.getName(subgroup), activeGroup);
} else { } else {
this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.subgroupToAddIsActiveGroup')); this.notificationsService.error(this.translateService.get(this.messagePrefix + '.notification.failure.subgroupToAddIsActiveGroup'));
} }

View File

@@ -56,8 +56,8 @@
<tbody> <tbody>
<tr *ngFor="let groupDto of (groupsDto$ | async)?.page"> <tr *ngFor="let groupDto of (groupsDto$ | async)?.page">
<td>{{groupDto.group.id}}</td> <td>{{groupDto.group.id}}</td>
<td>{{groupDto.group.name}}</td> <td>{{ dsoNameService.getName(groupDto.group) }}</td>
<td>{{(groupDto.group.object | async)?.payload?.name}}</td> <td>{{ dsoNameService.getName((groupDto.group.object | async)?.payload) }}</td>
<td>{{groupDto.epersons?.totalElements + groupDto.subgroups?.totalElements}}</td> <td>{{groupDto.epersons?.totalElements + groupDto.subgroups?.totalElements}}</td>
<td> <td>
<div class="btn-group edit-field"> <div class="btn-group edit-field">
@@ -65,7 +65,7 @@
<button *ngSwitchCase="true" <button *ngSwitchCase="true"
[routerLink]="groupService.getGroupEditPageRouterLink(groupDto.group)" [routerLink]="groupService.getGroupEditPageRouterLink(groupDto.group)"
class="btn btn-outline-primary btn-sm btn-edit" class="btn btn-outline-primary btn-sm btn-edit"
title="{{messagePrefix + 'table.edit.buttons.edit' | translate: {name: groupDto.group.name} }}" title="{{messagePrefix + 'table.edit.buttons.edit' | translate: {name: dsoNameService.getName(groupDto.group) } }}"
> >
<i class="fas fa-edit fa-fw"></i> <i class="fas fa-edit fa-fw"></i>
</button> </button>
@@ -80,7 +80,7 @@
</ng-container> </ng-container>
<button *ngIf="!groupDto.group?.permanent && groupDto.ableToDelete" <button *ngIf="!groupDto.group?.permanent && groupDto.ableToDelete"
(click)="deleteGroup(groupDto)" class="btn btn-outline-danger btn-sm btn-delete" (click)="deleteGroup(groupDto)" class="btn btn-outline-danger btn-sm btn-delete"
title="{{messagePrefix + 'table.edit.buttons.remove' | translate: {name: groupDto.group.name} }}"> title="{{messagePrefix + 'table.edit.buttons.remove' | translate: {name: dsoNameService.getName(groupDto.group) } }}">
<i class="fas fa-trash-alt fa-fw"></i> <i class="fas fa-trash-alt fa-fw"></i>
</button> </button>
</div> </div>

View File

@@ -32,8 +32,10 @@ import { PaginationService } from '../../core/pagination/pagination.service';
import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub';
import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { NoContent } from '../../core/shared/NoContent.model'; import { NoContent } from '../../core/shared/NoContent.model';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock, UNDEFINED_NAME } from '../../shared/mocks/dso-name.service.mock';
describe('GroupRegistryComponent', () => { describe('GroupsRegistryComponent', () => {
let component: GroupsRegistryComponent; let component: GroupsRegistryComponent;
let fixture: ComponentFixture<GroupsRegistryComponent>; let fixture: ComponentFixture<GroupsRegistryComponent>;
let ePersonDataServiceStub: any; let ePersonDataServiceStub: any;
@@ -160,7 +162,7 @@ describe('GroupRegistryComponent', () => {
authorizationService = jasmine.createSpyObj('authorizationService', ['isAuthorized']); authorizationService = jasmine.createSpyObj('authorizationService', ['isAuthorized']);
setIsAuthorized(true, true); setIsAuthorized(true, true);
paginationService = new PaginationServiceStub(); paginationService = new PaginationServiceStub();
TestBed.configureTestingModule({ return TestBed.configureTestingModule({
imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule,
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
@@ -171,6 +173,7 @@ describe('GroupRegistryComponent', () => {
], ],
declarations: [GroupsRegistryComponent], declarations: [GroupsRegistryComponent],
providers: [GroupsRegistryComponent, providers: [GroupsRegistryComponent,
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: EPersonDataService, useValue: ePersonDataServiceStub }, { provide: EPersonDataService, useValue: ePersonDataServiceStub },
{ provide: GroupDataService, useValue: groupsDataServiceStub }, { provide: GroupDataService, useValue: groupsDataServiceStub },
{ provide: DSpaceObjectDataService, useValue: dsoDataServiceStub }, { provide: DSpaceObjectDataService, useValue: dsoDataServiceStub },
@@ -208,7 +211,7 @@ describe('GroupRegistryComponent', () => {
it('should display community/collection name if present', () => { it('should display community/collection name if present', () => {
const collectionNamesFound = fixture.debugElement.queryAll(By.css('#groups tr td:nth-child(3)')); const collectionNamesFound = fixture.debugElement.queryAll(By.css('#groups tr td:nth-child(3)'));
expect(collectionNamesFound.length).toEqual(2); 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'); expect(collectionNamesFound[1].nativeElement.textContent).toEqual('testgroupid2objectName');
}); });

View File

@@ -1,5 +1,5 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { UntypedFormBuilder } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { import {
@@ -37,6 +37,7 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c
import { NoContent } from '../../core/shared/NoContent.model'; import { NoContent } from '../../core/shared/NoContent.model';
import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationService } from '../../core/pagination/pagination.service';
import { followLink } from '../../shared/utils/follow-link-config.model'; import { followLink } from '../../shared/utils/follow-link-config.model';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-groups-registry', selector: 'ds-groups-registry',
@@ -99,12 +100,14 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
private dSpaceObjectDataService: DSpaceObjectDataService, private dSpaceObjectDataService: DSpaceObjectDataService,
private translateService: TranslateService, private translateService: TranslateService,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private formBuilder: FormBuilder, private formBuilder: UntypedFormBuilder,
protected routeService: RouteService, protected routeService: RouteService,
private router: Router, private router: Router,
private authorizationService: AuthorizationDataService, private authorizationService: AuthorizationDataService,
private paginationService: PaginationService, private paginationService: PaginationService,
public requestService: RequestService) { public requestService: RequestService,
public dsoNameService: DSONameService,
) {
this.currentSearchQuery = ''; this.currentSearchQuery = '';
this.searchForm = this.formBuilder.group(({ this.searchForm = this.formBuilder.group(({
query: this.currentSearchQuery, query: this.currentSearchQuery,
@@ -201,10 +204,10 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy {
.subscribe((rd: RemoteData<NoContent>) => { .subscribe((rd: RemoteData<NoContent>) => {
if (rd.hasSucceeded) { if (rd.hasSucceeded) {
this.deletedGroupsIds = [...this.deletedGroupsIds, group.group.id]; 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 { } else {
this.notificationsService.error( 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 })); this.translateService.get(this.messagePrefix + 'notification.deleted.failure.content', { cause: rd.errorMessage }));
} }
}); });

View File

@@ -5,7 +5,7 @@ import {
DynamicFormLayout, DynamicFormLayout,
DynamicInputModel DynamicInputModel
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { FormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { RegistryService } from '../../../../core/registry/registry.service'; import { RegistryService } from '../../../../core/registry/registry.service';
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
@@ -66,7 +66,7 @@ export class MetadataSchemaFormComponent implements OnInit, OnDestroy {
/** /**
* A FormGroup that combines all inputs * A FormGroup that combines all inputs
*/ */
formGroup: FormGroup; formGroup: UntypedFormGroup;
/** /**
* An EventEmitter that's fired whenever the form is being submitted * An EventEmitter that's fired whenever the form is being submitted

View File

@@ -5,7 +5,7 @@ import {
DynamicFormLayout, DynamicFormLayout,
DynamicInputModel DynamicInputModel
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { FormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { RegistryService } from '../../../../core/registry/registry.service'; import { RegistryService } from '../../../../core/registry/registry.service';
import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service'; import { FormBuilderService } from '../../../../shared/form/builder/form-builder.service';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
@@ -82,7 +82,7 @@ export class MetadataFieldFormComponent implements OnInit, OnDestroy {
/** /**
* A FormGroup that combines all inputs * A FormGroup that combines all inputs
*/ */
formGroup: FormGroup; formGroup: UntypedFormGroup;
/** /**
* An EventEmitter that's fired whenever the form is being submitted * An EventEmitter that's fired whenever the form is being submitted

View File

@@ -19,7 +19,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-
import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock'; import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock';
import { ThemeService } from '../../../../../shared/theme-support/theme.service'; import { ThemeService } from '../../../../../shared/theme-support/theme.service';
import { AccessStatusDataService } from '../../../../../core/data/access-status-data.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 { AuthService } from '../../../../../core/auth/auth.service';
import { AuthServiceStub } from '../../../../../shared/testing/auth-service.stub'; import { AuthServiceStub } from '../../../../../shared/testing/auth-service.stub';
import { FileService } from '../../../../../core/shared/file.service'; import { FileService } from '../../../../../core/shared/file.service';

View File

@@ -13,6 +13,7 @@ import { BitstreamDataService } from '../../../../../core/data/bitstream-data.se
import { GenericConstructor } from '../../../../../core/shared/generic-constructor'; import { GenericConstructor } from '../../../../../core/shared/generic-constructor';
import { ListableObjectDirective } from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive'; import { ListableObjectDirective } from '../../../../../shared/object-collection/shared/listable-object/listable-object.directive';
import { ThemeService } from '../../../../../shared/theme-support/theme.service'; import { ThemeService } from '../../../../../shared/theme-support/theme.service';
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
@listableObjectComponent(ItemSearchResult, ViewMode.GridElement, Context.AdminSearch) @listableObjectComponent(ItemSearchResult, ViewMode.GridElement, Context.AdminSearch)
@Component({ @Component({
@@ -28,12 +29,14 @@ export class ItemAdminSearchResultGridElementComponent extends SearchResultGridE
@ViewChild('badges', { static: true }) badges: ElementRef; @ViewChild('badges', { static: true }) badges: ElementRef;
@ViewChild('buttons', { static: true }) buttons: ElementRef; @ViewChild('buttons', { static: true }) buttons: ElementRef;
constructor(protected truncatableService: TruncatableService, constructor(
protected bitstreamDataService: BitstreamDataService, public dsoNameService: DSONameService,
private themeService: ThemeService, protected truncatableService: TruncatableService,
private componentFactoryResolver: ComponentFactoryResolver protected bitstreamDataService: BitstreamDataService,
private themeService: ThemeService,
private componentFactoryResolver: ComponentFactoryResolver,
) { ) {
super(truncatableService, bitstreamDataService); super(dsoNameService, truncatableService, bitstreamDataService);
} }
/** /**

View File

@@ -2,6 +2,5 @@
[viewMode]="viewModes.ListElement" [viewMode]="viewModes.ListElement"
[index]="index" [index]="index"
[linkType]="linkType" [linkType]="linkType"
[listID]="listID" [listID]="listID"></ds-listable-object-component-loader>
[hideBadges]="true"></ds-listable-object-component-loader>
<ds-item-admin-search-result-actions-element [item]="dso" [small]="false"></ds-item-admin-search-result-actions-element> <ds-item-admin-search-result-actions-element [item]="dso" [small]="false"></ds-item-admin-search-result-actions-element>

View File

@@ -82,7 +82,7 @@ export class SupervisionOrderGroupSelectorComponent {
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
).subscribe((rd: RemoteData<SupervisionOrder>) => { ).subscribe((rd: RemoteData<SupervisionOrder>) => {
if (rd.state === 'Success') { 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.create.emit(rd.payload);
this.close(); this.close();
} else { } else {

View File

@@ -7,7 +7,7 @@
<a class="badge badge-primary mr-1 mb-1 text-capitalize mw-100 text-truncate" *ngFor="let supervisionOrder of supervisionOrders" data-test="soBadge" <a class="badge badge-primary mr-1 mb-1 text-capitalize mw-100 text-truncate" *ngFor="let supervisionOrder of supervisionOrders" data-test="soBadge"
[ngbTooltip]="'workflow-item.search.result.list.element.supervised.remove-tooltip' | translate" [ngbTooltip]="'workflow-item.search.result.list.element.supervised.remove-tooltip' | translate"
(click)="$event.preventDefault(); $event.stopImmediatePropagation(); deleteSupervisionOrder(supervisionOrder)" aria-label="Close"> (click)="$event.preventDefault(); $event.stopImmediatePropagation(); deleteSupervisionOrder(supervisionOrder)" aria-label="Close">
{{supervisionOrder.group.name}} {{ dsoNameService.getName(supervisionOrder.group) }}
<span aria-hidden="true"> ×</span> <span aria-hidden="true"> ×</span>
</a> </a>
</div> </div>

View File

@@ -8,6 +8,7 @@ import { Group } from '../../../../../../core/eperson/models/group.model';
import { getFirstCompletedRemoteData } from '../../../../../../core/shared/operators'; import { getFirstCompletedRemoteData } from '../../../../../../core/shared/operators';
import { isNotEmpty } from '../../../../../../shared/empty.util'; import { isNotEmpty } from '../../../../../../shared/empty.util';
import { RemoteData } from '../../../../../../core/data/remote-data'; import { RemoteData } from '../../../../../../core/data/remote-data';
import { DSONameService } from '../../../../../../core/breadcrumbs/dso-name.service';
export interface SupervisionOrderListEntry { export interface SupervisionOrderListEntry {
supervisionOrder: SupervisionOrder; supervisionOrder: SupervisionOrder;
@@ -33,6 +34,11 @@ export class SupervisionOrderStatusComponent implements OnChanges {
@Output() delete: EventEmitter<SupervisionOrderListEntry> = new EventEmitter<SupervisionOrderListEntry>(); @Output() delete: EventEmitter<SupervisionOrderListEntry> = new EventEmitter<SupervisionOrderListEntry>();
constructor(
public dsoNameService: DSONameService,
) {
}
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes && changes.supervisionOrderList) { if (changes && changes.supervisionOrderList) {
this.getSupervisionOrderEntries(changes.supervisionOrderList.currentValue) this.getSupervisionOrderEntries(changes.supervisionOrderList.currentValue)

View File

@@ -11,7 +11,7 @@ import { URLCombiner } from '../../../../../core/url-combiner/url-combiner';
import { WorkspaceItemAdminWorkflowActionsComponent } from './workspace-item-admin-workflow-actions.component'; import { WorkspaceItemAdminWorkflowActionsComponent } from './workspace-item-admin-workflow-actions.component';
import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model';
import { import {
getWorkflowItemDeleteRoute, getWorkspaceItemDeleteRoute,
} from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; } from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths';
import { Item } from '../../../../../core/shared/item.model'; import { Item } from '../../../../../core/shared/item.model';
import { RemoteData } from '../../../../../core/data/remote-data'; import { RemoteData } from '../../../../../core/data/remote-data';
@@ -83,7 +83,7 @@ describe('WorkspaceItemAdminWorkflowActionsComponent', () => {
it('should render a delete button with the correct link', () => { it('should render a delete button with the correct link', () => {
const button = fixture.debugElement.query(By.css('a.delete-link')); const button = fixture.debugElement.query(By.css('a.delete-link'));
const link = button.nativeElement.href; 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', () => { it('should render a policies button with the correct link', () => {

View File

@@ -11,7 +11,7 @@ import {
SupervisionOrderGroupSelectorComponent SupervisionOrderGroupSelectorComponent
} from './supervision-order-group-selector/supervision-order-group-selector.component'; } from './supervision-order-group-selector/supervision-order-group-selector.component';
import { import {
getWorkflowItemDeleteRoute getWorkspaceItemDeleteRoute
} from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; } 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 { ITEM_EDIT_AUTHORIZATIONS_PATH } from '../../../../../item-page/edit-item-page/edit-item-page.routing-paths';
import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; 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 { getDeleteRoute(): string {
return getWorkflowItemDeleteRoute(this.wsi.id); return getWorkspaceItemDeleteRoute(this.wsi.id);
} }
/** /**

View File

@@ -23,6 +23,7 @@ import {
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model'; import { WorkflowItemSearchResult } from '../../../../../shared/object-collection/shared/workflow-item-search-result.model';
import { ThemeService } from '../../../../../shared/theme-support/theme.service'; import { ThemeService } from '../../../../../shared/theme-support/theme.service';
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
@listableObjectComponent(WorkflowItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch) @listableObjectComponent(WorkflowItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch)
@Component({ @Component({
@@ -55,13 +56,14 @@ export class WorkflowItemSearchResultAdminWorkflowGridElementComponent extends S
public item$: Observable<Item>; public item$: Observable<Item>;
constructor( constructor(
public dsoNameService: DSONameService,
private componentFactoryResolver: ComponentFactoryResolver, private componentFactoryResolver: ComponentFactoryResolver,
private linkService: LinkService, private linkService: LinkService,
protected truncatableService: TruncatableService, protected truncatableService: TruncatableService,
private themeService: ThemeService, private themeService: ThemeService,
protected bitstreamDataService: BitstreamDataService protected bitstreamDataService: BitstreamDataService
) { ) {
super(truncatableService, bitstreamDataService); super(dsoNameService, truncatableService, bitstreamDataService);
} }
/** /**

View File

@@ -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 { BehaviorSubject, Observable } from 'rxjs';
import { map, mergeMap, take, tap } from 'rxjs/operators'; 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 { SupervisionOrder } from '../../../../../core/supervision-order/models/supervision-order.model';
import { PaginatedList } from '../../../../../core/data/paginated-list.model'; import { PaginatedList } from '../../../../../core/data/paginated-list.model';
import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service'; import { SupervisionOrderDataService } from '../../../../../core/supervision-order/supervision-order-data.service';
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
@listableObjectComponent(WorkspaceItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch) @listableObjectComponent(WorkspaceItemSearchResult, ViewMode.GridElement, Context.AdminWorkflowSearch)
@Component({ @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 * The component for displaying a grid element for an workflow item on the admin workflow search page
*/ */
export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent<WorkspaceItemSearchResult, WorkspaceItem> { export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends SearchResultGridElementComponent<WorkspaceItemSearchResult, WorkspaceItem> implements OnInit {
/** /**
* The item linked to the workspace item * The item linked to the workspace item
@@ -79,6 +80,7 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends
@ViewChild('buttons', { static: true }) buttons: ElementRef; @ViewChild('buttons', { static: true }) buttons: ElementRef;
constructor( constructor(
public dsoNameService: DSONameService,
private componentFactoryResolver: ComponentFactoryResolver, private componentFactoryResolver: ComponentFactoryResolver,
private linkService: LinkService, private linkService: LinkService,
protected truncatableService: TruncatableService, protected truncatableService: TruncatableService,
@@ -86,7 +88,7 @@ export class WorkspaceItemSearchResultAdminWorkflowGridElementComponent extends
protected bitstreamDataService: BitstreamDataService, protected bitstreamDataService: BitstreamDataService,
protected supervisionOrderDataService: SupervisionOrderDataService, protected supervisionOrderDataService: SupervisionOrderDataService,
) { ) {
super(truncatableService, bitstreamDataService); super(dsoNameService, truncatableService, bitstreamDataService);
} }
/** /**

View File

@@ -39,7 +39,7 @@ export class WorkflowItemSearchResultAdminWorkflowListElementComponent extends S
constructor(private linkService: LinkService, constructor(private linkService: LinkService,
protected truncatableService: TruncatableService, protected truncatableService: TruncatableService,
protected dsoNameService: DSONameService, public dsoNameService: DSONameService,
@Inject(APP_CONFIG) protected appConfig: AppConfig @Inject(APP_CONFIG) protected appConfig: AppConfig
) { ) {
super(truncatableService, dsoNameService, appConfig); super(truncatableService, dsoNameService, appConfig);

View File

@@ -59,7 +59,7 @@ export class WorkspaceItemSearchResultAdminWorkflowListElementComponent extends
public supervisionOrder$: BehaviorSubject<SupervisionOrder[]> = new BehaviorSubject<SupervisionOrder[]>([]); public supervisionOrder$: BehaviorSubject<SupervisionOrder[]> = new BehaviorSubject<SupervisionOrder[]>([]);
constructor(private linkService: LinkService, constructor(private linkService: LinkService,
protected dsoNameService: DSONameService, public dsoNameService: DSONameService,
protected supervisionOrderDataService: SupervisionOrderDataService, protected supervisionOrderDataService: SupervisionOrderDataService,
protected truncatableService: TruncatableService, protected truncatableService: TruncatableService,
@Inject(APP_CONFIG) protected appConfig: AppConfig @Inject(APP_CONFIG) protected appConfig: AppConfig

View File

@@ -209,7 +209,7 @@ import { ThemedPageErrorComponent } from './page-error/themed-page-error.compone
{ {
path: REQUEST_COPY_MODULE_PATH, path: REQUEST_COPY_MODULE_PATH,
loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule), loadChildren: () => import('./request-copy/request-copy.module').then((m) => m.RequestCopyModule),
canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard] canActivate: [EndUserAgreementCurrentUserGuard]
}, },
{ {
path: FORBIDDEN_PATH, path: FORBIDDEN_PATH,

View File

@@ -1,5 +1,5 @@
<div class="container"> <div class="container">
<h3>{{'bitstream.download.page' | translate:{bitstream: (bitstream$ | async)?.name} }}</h3> <h3>{{'bitstream.download.page' | translate:{ bitstream: dsoNameService.getName((bitstream$ | async)) } }}</h3>
<div class="pt-3"> <div class="pt-3">
<button (click)="back()" class="btn btn-outline-secondary"> <button (click)="back()" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left"></i> {{'bitstream.download.page.back' | translate}} <i class="fas fa-arrow-left"></i> {{'bitstream.download.page.back' | translate}}

View File

@@ -14,6 +14,7 @@ import { getForbiddenRoute } from '../../app-routing-paths';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { redirectOn4xx } from '../../core/shared/authorized.operators'; import { redirectOn4xx } from '../../core/shared/authorized.operators';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-bitstream-download-page', selector: 'ds-bitstream-download-page',
@@ -36,6 +37,7 @@ export class BitstreamDownloadPageComponent implements OnInit {
private fileService: FileService, private fileService: FileService,
private hardRedirectService: HardRedirectService, private hardRedirectService: HardRedirectService,
private location: Location, private location: Location,
public dsoNameService: DSONameService,
) { ) {
} }

View File

@@ -2,13 +2,13 @@
<div class="container" *ngVar="(bitstreamFormatsRD$ | async) as formatsRD"> <div class="container" *ngVar="(bitstreamFormatsRD$ | async) as formatsRD">
<div class="row" *ngIf="bitstreamRD?.hasSucceeded && formatsRD?.hasSucceeded"> <div class="row" *ngIf="bitstreamRD?.hasSucceeded && formatsRD?.hasSucceeded">
<div class="col-md-2"> <div class="col-md-2">
<ds-thumbnail [thumbnail]="bitstreamRD?.payload"></ds-thumbnail> <ds-themed-thumbnail [thumbnail]="bitstreamRD?.payload"></ds-themed-thumbnail>
</div> </div>
<div class="col-md-10"> <div class="col-md-10">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<h3>{{bitstreamRD?.payload?.name}} <span class="text-muted">({{bitstreamRD?.payload?.sizeBytes | dsFileSize}})</span></h3> <h3>{{dsoNameService.getName(bitstreamRD?.payload)}} <span class="text-muted">({{bitstreamRD?.payload?.sizeBytes | dsFileSize}})</span></h3>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -15,7 +15,7 @@ import { INotification, Notification } from '../../shared/notifications/models/n
import { BitstreamFormat } from '../../core/shared/bitstream-format.model'; import { BitstreamFormat } from '../../core/shared/bitstream-format.model';
import { BitstreamFormatSupportLevel } from '../../core/shared/bitstream-format-support-level'; import { BitstreamFormatSupportLevel } from '../../core/shared/bitstream-format-support-level';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { FormControl, FormGroup } from '@angular/forms'; import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { FileSizePipe } from '../../shared/utils/file-size-pipe'; import { FileSizePipe } from '../../shared/utils/file-size-pipe';
import { VarDirective } from '../../shared/utils/var.directive'; import { VarDirective } from '../../shared/utils/var.directive';
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
@@ -84,9 +84,9 @@ describe('EditBitstreamPageComponent', () => {
const controls = {}; const controls = {};
if (hasValue(fModel)) { if (hasValue(fModel)) {
fModel.forEach((controlModel) => { fModel.forEach((controlModel) => {
controls[controlModel.id] = new FormControl((controlModel as any).value); controls[controlModel.id] = new UntypedFormControl((controlModel as any).value);
}); });
return new FormGroup(controls); return new UntypedFormGroup(controls);
} }
return undefined; return undefined;
} }

View File

@@ -23,7 +23,7 @@ import {
DynamicInputModel, DynamicInputModel,
DynamicSelectModel DynamicSelectModel
} from '@ng-dynamic-forms/core'; } from '@ng-dynamic-forms/core';
import { FormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DynamicCustomSwitchModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.model'; import { DynamicCustomSwitchModel } from '../../shared/form/builder/ds-dynamic-form-ui/models/custom-switch/custom-switch.model';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
@@ -359,7 +359,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
/** /**
* The form group of this form * The form group of this form
*/ */
formGroup: FormGroup; formGroup: UntypedFormGroup;
/** /**
* The ID of the item the bitstream originates from * The ID of the item the bitstream originates from
@@ -395,7 +395,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
private formService: DynamicFormService, private formService: DynamicFormService,
private translate: TranslateService, private translate: TranslateService,
private bitstreamService: BitstreamDataService, private bitstreamService: BitstreamDataService,
private dsoNameService: DSONameService, public dsoNameService: DSONameService,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private bitstreamFormatService: BitstreamFormatDataService) { private bitstreamFormatService: BitstreamFormatDataService) {
} }

View File

@@ -18,6 +18,7 @@ import { isValidDate } from '../../shared/date.util';
import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { Item } from '../../core/shared/item.model'; import { Item } from '../../core/shared/item.model';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-browse-by-date-page', selector: 'ds-browse-by-date-page',
@@ -42,8 +43,10 @@ export class BrowseByDatePageComponent extends BrowseByMetadataPageComponent {
protected router: Router, protected router: Router,
protected paginationService: PaginationService, protected paginationService: PaginationService,
protected cdRef: ChangeDetectorRef, protected cdRef: ChangeDetectorRef,
@Inject(APP_CONFIG) public appConfig: AppConfig) { @Inject(APP_CONFIG) public appConfig: AppConfig,
super(route, browseService, dsoService, paginationService, router, appConfig); public dsoNameService: DSONameService,
) {
super(route, browseService, dsoService, paginationService, router, appConfig, dsoNameService);
} }
ngOnInit(): void { ngOnInit(): void {

View File

@@ -4,6 +4,8 @@ import { of as observableOf } from 'rxjs';
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
import { BrowseByDataType } from './browse-by-switcher/browse-by-decorator'; import { BrowseByDataType } from './browse-by-switcher/browse-by-decorator';
import { ValueListBrowseDefinition } from '../core/shared/value-list-browse-definition.model'; import { ValueListBrowseDefinition } from '../core/shared/value-list-browse-definition.model';
import { DSONameServiceMock } from '../shared/mocks/dso-name.service.mock';
import { DSONameService } from '../core/breadcrumbs/dso-name.service';
describe('BrowseByGuard', () => { describe('BrowseByGuard', () => {
describe('canActivate', () => { describe('canActivate', () => {
@@ -33,7 +35,7 @@ describe('BrowseByGuard', () => {
findById: () => createSuccessfulRemoteDataObject$(browseDefinition) findById: () => createSuccessfulRemoteDataObject$(browseDefinition)
}; };
guard = new BrowseByGuard(dsoService, translateService, browseDefinitionService); guard = new BrowseByGuard(dsoService, translateService, browseDefinitionService, new DSONameServiceMock() as DSONameService);
}); });
it('should return true, and sets up the data correctly, with a scope and value', () => { it('should return true, and sets up the data correctly, with a scope and value', () => {

View File

@@ -3,11 +3,13 @@ import { Injectable } from '@angular/core';
import { DSpaceObjectDataService } from '../core/data/dspace-object-data.service'; import { DSpaceObjectDataService } from '../core/data/dspace-object-data.service';
import { hasNoValue, hasValue } from '../shared/empty.util'; import { hasNoValue, hasValue } from '../shared/empty.util';
import { map, switchMap } from 'rxjs/operators'; import { map, switchMap } from 'rxjs/operators';
import { getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Observable, of as observableOf } from 'rxjs'; import { Observable, of as observableOf } from 'rxjs';
import { BrowseDefinitionDataService } from '../core/browse/browse-definition-data.service'; import { BrowseDefinitionDataService } from '../core/browse/browse-definition-data.service';
import { BrowseDefinition } from '../core/shared/browse-definition.model'; import { BrowseDefinition } from '../core/shared/browse-definition.model';
import { DSONameService } from '../core/breadcrumbs/dso-name.service';
import { DSpaceObject } from '../core/shared/dspace-object.model';
@Injectable() @Injectable()
/** /**
@@ -17,7 +19,9 @@ export class BrowseByGuard implements CanActivate {
constructor(protected dsoService: DSpaceObjectDataService, constructor(protected dsoService: DSpaceObjectDataService,
protected translate: TranslateService, protected translate: TranslateService,
protected browseDefinitionService: BrowseDefinitionDataService) { protected browseDefinitionService: BrowseDefinitionDataService,
protected dsoNameService: DSONameService,
) {
} }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
@@ -31,14 +35,14 @@ export class BrowseByGuard implements CanActivate {
} }
const scope = route.queryParams.scope; const scope = route.queryParams.scope;
const value = route.queryParams.value; const value = route.queryParams.value;
const metadataTranslated = this.translate.instant('browse.metadata.' + id); const metadataTranslated = this.translate.instant(`browse.metadata.${id}`);
return browseDefinition$.pipe( return browseDefinition$.pipe(
switchMap((browseDefinition) => { switchMap((browseDefinition: BrowseDefinition) => {
if (hasValue(scope)) { if (hasValue(scope)) {
const dsoAndMetadata$ = this.dsoService.findById(scope).pipe(getFirstSucceededRemoteData()); const dso$: Observable<DSpaceObject> = this.dsoService.findById(scope).pipe(getFirstSucceededRemoteDataPayload());
return dsoAndMetadata$.pipe( return dso$.pipe(
map((dsoRD) => { map((dso: DSpaceObject) => {
const name = dsoRD.payload.name; const name = this.dsoNameService.getName(dso);
route.data = this.createData(title, id, browseDefinition, name, metadataTranslated, value, route); route.data = this.createData(title, id, browseDefinition, name, metadataTranslated, value, route);
return true; return true;
}) })

View File

@@ -5,7 +5,7 @@
<header class="comcol-header mr-auto"> <header class="comcol-header mr-auto">
<!-- Parent Name --> <!-- Parent Name -->
<ds-comcol-page-header [name]="parentContext.name"> <ds-comcol-page-header [name]="dsoNameService.getName(parentContext)">
</ds-comcol-page-header> </ds-comcol-page-header>
<!-- Collection logo --> <!-- Collection logo -->
<ds-comcol-page-logo *ngIf="logo$" <ds-comcol-page-logo *ngIf="logo$"
@@ -35,12 +35,12 @@
<ds-browse-by *ngIf="startsWithOptions" class="col-xs-12 w-100" <ds-browse-by *ngIf="startsWithOptions" class="col-xs-12 w-100"
title="{{'browse.title' | translate: title="{{'browse.title' | translate:
{ {
collection: (parent$ | async)?.payload?.name || '', collection: dsoNameService.getName((parent$ | async)?.payload),
field: 'browse.metadata.' + browseId | translate, field: 'browse.metadata.' + browseId | translate,
startsWith: (startsWith)? ('browse.startsWith' | translate: { startsWith: '&quot;' + startsWith + '&quot;' }) : '', startsWith: (startsWith)? ('browse.startsWith' | translate: { startsWith: '&quot;' + startsWith + '&quot;' }) : '',
value: (value)? '&quot;' + value + '&quot;': '' value: (value)? '&quot;' + value + '&quot;': ''
} }}" } }}"
parentname="{{(parent$ | async)?.payload?.name || ''}}" parentname="{{dsoNameService.getName((parent$ | async)?.payload)}}"
[objects$]="(items$ !== undefined)? items$ : browseEntries$" [objects$]="(items$ !== undefined)? items$ : browseEntries$"
[paginationConfig]="(currentPagination$ |async)" [paginationConfig]="(currentPagination$ |async)"
[sortConfig]="(currentSort$ |async)" [sortConfig]="(currentSort$ |async)"

View File

@@ -21,6 +21,7 @@ import { Bitstream } from '../../core/shared/bitstream.model';
import { Collection } from '../../core/shared/collection.model'; import { Collection } from '../../core/shared/collection.model';
import { Community } from '../../core/shared/community.model'; import { Community } from '../../core/shared/community.model';
import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface'; import { APP_CONFIG, AppConfig } from '../../../config/app-config.interface';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
export const BBM_PAGINATION_ID = 'bbm'; export const BBM_PAGINATION_ID = 'bbm';
@@ -126,7 +127,9 @@ export class BrowseByMetadataPageComponent implements OnInit, OnDestroy {
protected dsoService: DSpaceObjectDataService, protected dsoService: DSpaceObjectDataService,
protected paginationService: PaginationService, protected paginationService: PaginationService,
protected router: Router, protected router: Router,
@Inject(APP_CONFIG) public appConfig: AppConfig) { @Inject(APP_CONFIG) public appConfig: AppConfig,
public dsoNameService: DSONameService,
) {
this.fetchThumbnails = this.appConfig.browseBy.showThumbnails; this.fetchThumbnails = this.appConfig.browseBy.showThumbnails;
this.paginationConfig = Object.assign(new PaginationComponentOptions(), { this.paginationConfig = Object.assign(new PaginationComponentOptions(), {

View File

@@ -13,6 +13,7 @@ import { PaginationService } from '../../core/pagination/pagination.service';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface'; import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-browse-by-title-page', selector: 'ds-browse-by-title-page',
@@ -29,8 +30,10 @@ export class BrowseByTitlePageComponent extends BrowseByMetadataPageComponent {
protected dsoService: DSpaceObjectDataService, protected dsoService: DSpaceObjectDataService,
protected paginationService: PaginationService, protected paginationService: PaginationService,
protected router: Router, protected router: Router,
@Inject(APP_CONFIG) public appConfig: AppConfig) { @Inject(APP_CONFIG) public appConfig: AppConfig,
super(route, browseService, dsoService, paginationService, router, appConfig); public dsoNameService: DSONameService,
) {
super(route, browseService, dsoService, paginationService, router, appConfig, dsoNameService);
} }
ngOnInit(): void { ngOnInit(): void {

View File

@@ -157,7 +157,7 @@ export class CollectionItemMapperComponent implements OnInit {
scope: undefined, scope: undefined,
dsoTypes: [DSpaceObjectType.ITEM], dsoTypes: [DSpaceObjectType.ITEM],
sort: this.defaultSortOptions sort: this.defaultSortOptions
}), 10000).pipe( }), 10000, undefined, undefined, followLink('owningCollection')).pipe(
toDSpaceObjectListRD(), toDSpaceObjectListRD(),
startWith(undefined) startWith(undefined)
); );

View File

@@ -8,7 +8,7 @@
<header class="comcol-header mr-auto"> <header class="comcol-header mr-auto">
<!-- Collection Name --> <!-- Collection Name -->
<ds-comcol-page-header <ds-comcol-page-header
[name]="collection.name"> [name]="dsoNameService.getName(collection)">
</ds-comcol-page-header> </ds-comcol-page-header>
<!-- Collection logo --> <!-- Collection logo -->
<ds-comcol-page-logo *ngIf="logoRD$" <ds-comcol-page-logo *ngIf="logoRD$"

View File

@@ -29,6 +29,7 @@ import { FeatureID } from '../core/data/feature-authorization/feature-id';
import { getCollectionPageRoute } from './collection-page-routing-paths'; import { getCollectionPageRoute } from './collection-page-routing-paths';
import { redirectOn4xx } from '../core/shared/authorized.operators'; import { redirectOn4xx } from '../core/shared/authorized.operators';
import { BROWSE_LINKS_TO_FOLLOW } from '../core/browse/browse.service'; import { BROWSE_LINKS_TO_FOLLOW } from '../core/browse/browse.service';
import { DSONameService } from '../core/breadcrumbs/dso-name.service';
import { APP_CONFIG, AppConfig } from '../../../src/config/app-config.interface'; import { APP_CONFIG, AppConfig } from '../../../src/config/app-config.interface';
@Component({ @Component({
@@ -70,6 +71,7 @@ export class CollectionPageComponent implements OnInit {
private authService: AuthService, private authService: AuthService,
private paginationService: PaginationService, private paginationService: PaginationService,
private authorizationDataService: AuthorizationDataService, private authorizationDataService: AuthorizationDataService,
public dsoNameService: DSONameService,
@Inject(APP_CONFIG) public appConfig: AppConfig, @Inject(APP_CONFIG) public appConfig: AppConfig,
) { ) {
this.paginationConfig = Object.assign(new PaginationComponentOptions(), { this.paginationConfig = Object.assign(new PaginationComponentOptions(), {

View File

@@ -1,7 +1,7 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-12 pb-4"> <div class="col-12 pb-4">
<h2 id="sub-header" class="border-bottom pb-2">{{'collection.create.sub-head' | translate:{ parent: (parentRD$| async)?.payload.name } }}</h2> <h2 id="sub-header" class="border-bottom pb-2">{{'collection.create.sub-head' | translate:{ parent: dsoNameService.getName((parentRD$| async)?.payload) } }}</h2>
</div> </div>
</div> </div>
<ds-collection-form (submitForm)="onSubmit($event)" <ds-collection-form (submitForm)="onSubmit($event)"

View File

@@ -13,16 +13,19 @@ import { CreateCollectionPageComponent } from './create-collection-page.componen
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub';
import { RequestService } from '../../core/data/request.service'; import { RequestService } from '../../core/data/request.service';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../../shared/mocks/dso-name.service.mock';
describe('CreateCollectionPageComponent', () => { describe('CreateCollectionPageComponent', () => {
let comp: CreateCollectionPageComponent; let comp: CreateCollectionPageComponent;
let fixture: ComponentFixture<CreateCollectionPageComponent>; let fixture: ComponentFixture<CreateCollectionPageComponent>;
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ return TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule], imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
declarations: [CreateCollectionPageComponent], declarations: [CreateCollectionPageComponent],
providers: [ providers: [
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: CollectionDataService, useValue: {} }, { provide: CollectionDataService, useValue: {} },
{ {
provide: CommunityDataService, provide: CommunityDataService,

View File

@@ -8,6 +8,7 @@ import { CollectionDataService } from '../../core/data/collection-data.service';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { RequestService } from '../../core/data/request.service'; 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 Collection * Component that represents the page where a user can create a new Collection
@@ -22,6 +23,7 @@ export class CreateCollectionPageComponent extends CreateComColPageComponent<Col
protected type = Collection.type; protected type = Collection.type;
public constructor( public constructor(
public dsoNameService: DSONameService,
protected communityDataService: CommunityDataService, protected communityDataService: CommunityDataService,
protected collectionDataService: CollectionDataService, protected collectionDataService: CollectionDataService,
protected routeService: RouteService, protected routeService: RouteService,
@@ -30,6 +32,6 @@ export class CreateCollectionPageComponent extends CreateComColPageComponent<Col
protected translate: TranslateService, protected translate: TranslateService,
protected requestService: RequestService protected requestService: RequestService
) { ) {
super(collectionDataService, communityDataService, routeService, router, notificationsService, translate, requestService); super(collectionDataService, dsoNameService, communityDataService, routeService, router, notificationsService, translate, requestService);
} }
} }

View File

@@ -3,7 +3,7 @@
<ng-container *ngVar="(dsoRD$ | async)?.payload as dso"> <ng-container *ngVar="(dsoRD$ | async)?.payload as dso">
<div class="col-12 pb-4"> <div class="col-12 pb-4">
<h2 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h2> <h2 id="header" class="border-bottom pb-2">{{ 'collection.delete.head' | translate}}</h2>
<p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dso.name } }}</p> <p class="pb-2">{{ 'collection.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
<div class="form-group row"> <div class="form-group row">
<div class="col text-right space-children-mr"> <div class="col text-right space-children-mr">
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)"> <button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)">

View File

@@ -10,6 +10,8 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
import { DeleteCollectionPageComponent } from './delete-collection-page.component'; import { DeleteCollectionPageComponent } from './delete-collection-page.component';
import { CollectionDataService } from '../../core/data/collection-data.service'; import { CollectionDataService } from '../../core/data/collection-data.service';
import { RequestService } from '../../core/data/request.service'; import { RequestService } from '../../core/data/request.service';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../../shared/mocks/dso-name.service.mock';
describe('DeleteCollectionPageComponent', () => { describe('DeleteCollectionPageComponent', () => {
let comp: DeleteCollectionPageComponent; let comp: DeleteCollectionPageComponent;
@@ -20,6 +22,7 @@ describe('DeleteCollectionPageComponent', () => {
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule], imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
declarations: [DeleteCollectionPageComponent], declarations: [DeleteCollectionPageComponent],
providers: [ providers: [
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: CollectionDataService, useValue: {} }, { provide: CollectionDataService, useValue: {} },
{ provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } }, { provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
{ provide: NotificationsService, useValue: {} }, { provide: NotificationsService, useValue: {} },

View File

@@ -5,6 +5,7 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
import { CollectionDataService } from '../../core/data/collection-data.service'; import { CollectionDataService } from '../../core/data/collection-data.service';
import { Collection } from '../../core/shared/collection.model'; import { Collection } from '../../core/shared/collection.model';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
/** /**
* Component that represents the page where a user can delete an existing Collection * Component that represents the page where a user can delete an existing Collection
@@ -19,11 +20,12 @@ export class DeleteCollectionPageComponent extends DeleteComColPageComponent<Col
public constructor( public constructor(
protected dsoDataService: CollectionDataService, protected dsoDataService: CollectionDataService,
public dsoNameService: DSONameService,
protected router: Router, protected router: Router,
protected route: ActivatedRoute, protected route: ActivatedRoute,
protected notifications: NotificationsService, protected notifications: NotificationsService,
protected translate: TranslateService, protected translate: TranslateService,
) { ) {
super(dsoDataService, router, route, notifications, translate); super(dsoDataService, dsoNameService, router, route, notifications, translate);
} }
} }

View File

@@ -15,6 +15,8 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ComcolModule } from '../../../shared/comcol/comcol.module'; import { ComcolModule } from '../../../shared/comcol/comcol.module';
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../../../shared/mocks/dso-name.service.mock';
describe('CollectionRolesComponent', () => { describe('CollectionRolesComponent', () => {
@@ -79,6 +81,7 @@ describe('CollectionRolesComponent', () => {
], ],
providers: [ providers: [
{ provide: ActivatedRoute, useValue: route }, { provide: ActivatedRoute, useValue: route },
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: RequestService, useValue: requestService }, { provide: RequestService, useValue: requestService },
{ provide: GroupDataService, useValue: groupDataService }, { provide: GroupDataService, useValue: groupDataService },
{ provide: NotificationsService, useClass: NotificationsServiceStub } { provide: NotificationsService, useClass: NotificationsServiceStub }

View File

@@ -12,7 +12,7 @@ import { NotificationType } from '../../../shared/notifications/models/notificat
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { DynamicFormControlModel, DynamicFormService } from '@ng-dynamic-forms/core'; import { DynamicFormControlModel, DynamicFormService } from '@ng-dynamic-forms/core';
import { hasValue } from '../../../shared/empty.util'; import { hasValue } from '../../../shared/empty.util';
import { FormControl, FormGroup } from '@angular/forms'; import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { RouterStub } from '../../../shared/testing/router.stub'; import { RouterStub } from '../../../shared/testing/router.stub';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { Collection } from '../../../core/shared/collection.model'; import { Collection } from '../../../core/shared/collection.model';
@@ -98,9 +98,9 @@ describe('CollectionSourceComponent', () => {
const controls = {}; const controls = {};
if (hasValue(fModel)) { if (hasValue(fModel)) {
fModel.forEach((controlModel) => { fModel.forEach((controlModel) => {
controls[controlModel.id] = new FormControl((controlModel as any).value); controls[controlModel.id] = new UntypedFormControl((controlModel as any).value);
}); });
return new FormGroup(controls); return new UntypedFormGroup(controls);
} }
return undefined; return undefined;
} }

View File

@@ -14,7 +14,7 @@ import { Location } from '@angular/common';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service'; import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { FormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util'; import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util';
import { ContentSource, ContentSourceHarvestType } from '../../../core/shared/content-source.model'; import { ContentSource, ContentSourceHarvestType } from '../../../core/shared/content-source.model';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
@@ -202,7 +202,7 @@ export class CollectionSourceComponent extends AbstractTrackableComponent implem
/** /**
* The form group of this form * The form group of this form
*/ */
formGroup: FormGroup; formGroup: UntypedFormGroup;
/** /**
* Subscription to update the current form * Subscription to update the current form

View File

@@ -2,7 +2,7 @@
<div class="row"> <div class="row">
<div class="col-12" *ngVar="(itemRD$ | async) as itemRD"> <div class="col-12" *ngVar="(itemRD$ | async) as itemRD">
<ng-container *ngIf="itemRD?.hasSucceeded"> <ng-container *ngIf="itemRD?.hasSucceeded">
<h2 class="border-bottom">{{ 'collection.edit.template.head' | translate:{ collection: collection?.name } }}</h2> <h2 class="border-bottom">{{ 'collection.edit.template.head' | translate:{ collection: dsoNameService.getName(collection) } }}</h2>
<ds-themed-dso-edit-metadata [updateDataService]="itemTemplateService" [dso]="itemRD?.payload"></ds-themed-dso-edit-metadata> <ds-themed-dso-edit-metadata [updateDataService]="itemTemplateService" [dso]="itemRD?.payload"></ds-themed-dso-edit-metadata>
<button [routerLink]="getCollectionEditUrl(collection)" class="btn btn-outline-secondary">{{ 'collection.edit.template.cancel' | translate }}</button> <button [routerLink]="getCollectionEditUrl(collection)" class="btn btn-outline-secondary">{{ 'collection.edit.template.cancel' | translate }}</button>
</ng-container> </ng-container>

View File

@@ -9,6 +9,7 @@ import { getCollectionEditRoute } from '../collection-page-routing-paths';
import { Item } from '../../core/shared/item.model'; import { Item } from '../../core/shared/item.model';
import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
import { AlertType } from '../../shared/alert/aletr-type'; import { AlertType } from '../../shared/alert/aletr-type';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-edit-item-template-page', selector: 'ds-edit-item-template-page',
@@ -35,8 +36,11 @@ export class EditItemTemplatePageComponent implements OnInit {
*/ */
AlertTypeEnum = AlertType; AlertTypeEnum = AlertType;
constructor(protected route: ActivatedRoute, constructor(
public itemTemplateService: ItemTemplateDataService) { protected route: ActivatedRoute,
public itemTemplateService: ItemTemplateDataService,
public dsoNameService: DSONameService,
) {
} }
ngOnInit(): void { ngOnInit(): void {

View File

@@ -2,18 +2,22 @@ import { first } from 'rxjs/operators';
import { ItemTemplatePageResolver } from './item-template-page.resolver'; import { ItemTemplatePageResolver } from './item-template-page.resolver';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; 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('ItemTemplatePageResolver', () => {
describe('resolve', () => { describe('resolve', () => {
let resolver: ItemTemplatePageResolver; let resolver: ItemTemplatePageResolver;
let itemTemplateService: any; let itemTemplateService: any;
let dsoNameService: DSONameServiceMock;
const uuid = '1234-65487-12354-1235'; const uuid = '1234-65487-12354-1235';
beforeEach(() => { beforeEach(() => {
itemTemplateService = { itemTemplateService = {
findByCollectionID: (id: string) => createSuccessfulRemoteDataObject$({ id }) 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) => { it('should resolve an item template with the correct id', (done) => {

View File

@@ -6,13 +6,17 @@ import { ItemTemplateDataService } from '../../core/data/item-template-data.serv
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { followLink } from '../../shared/utils/follow-link-config.model'; import { followLink } from '../../shared/utils/follow-link-config.model';
import { getFirstCompletedRemoteData } from '../../core/shared/operators'; 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 * This class represents a resolver that requests a specific collection's item template before the route is activated
*/ */
@Injectable() @Injectable()
export class ItemTemplatePageResolver implements Resolve<RemoteData<Item>> { export class ItemTemplatePageResolver implements Resolve<RemoteData<Item>> {
constructor(private itemTemplateService: ItemTemplateDataService) { constructor(
public dsoNameService: DSONameService,
private itemTemplateService: ItemTemplateDataService,
) {
} }
/** /**

View File

@@ -8,10 +8,10 @@
<span class="fa fa-chevron-right invisible" aria-hidden="true"></span> <span class="fa fa-chevron-right invisible" aria-hidden="true"></span>
</button> </button>
<div class="align-middle pt-2"> <div class="align-middle pt-2">
<a *ngIf="node!==loadingNode" [routerLink]="[]" (click)="getNextPage(node)" <button *ngIf="node!==loadingNode" (click)="getNextPage(node)"
class="btn btn-outline-primary btn-sm" role="button"> class="btn btn-outline-primary btn-sm" role="button">
<i class="fas fa-angle-down"></i> {{ 'communityList.showMore' | translate }} <i class="fas fa-angle-down"></i> {{ 'communityList.showMore' | translate }}
</a> </button>
<ds-themed-loading *ngIf="node===loadingNode && dataSource.loading$ | async" class="ds-themed-loading"></ds-themed-loading> <ds-themed-loading *ngIf="node===loadingNode && dataSource.loading$ | async" class="ds-themed-loading"></ds-themed-loading>
</div> </div>
</div> </div>
@@ -25,8 +25,8 @@
class="example-tree-node expandable-node"> class="example-tree-node expandable-node">
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-default" cdkTreeNodeToggle <button type="button" class="btn btn-default" cdkTreeNodeToggle
[title]="'toggle ' + node.name" [title]="'toggle ' + dsoNameService.getName(node.payload)"
[attr.aria-label]="'toggle ' + node.name" [attr.aria-label]="'toggle ' + dsoNameService.getName(node.payload)"
(click)="toggleExpanded(node)" (click)="toggleExpanded(node)"
[ngClass]="(hasChild(null, node)| async) ? 'visible' : 'invisible'" [ngClass]="(hasChild(null, node)| async) ? 'visible' : 'invisible'"
[attr.data-test]="(hasChild(null, node)| async) ? 'expand-button' : ''"> [attr.data-test]="(hasChild(null, node)| async) ? 'expand-button' : ''">
@@ -35,7 +35,7 @@
</button> </button>
<h5 class="align-middle pt-2"> <h5 class="align-middle pt-2">
<a [routerLink]="node.route" class="lead"> <a [routerLink]="node.route" class="lead">
{{node.name}} {{ dsoNameService.getName(node.payload) }}
</a> </a>
</h5> </h5>
</div> </div>
@@ -66,12 +66,11 @@
class="example-tree-node childless-node"> class="example-tree-node childless-node">
<div class="btn-group"> <div class="btn-group">
<button type="button" class="btn btn-default" cdkTreeNodeToggle> <button type="button" class="btn btn-default" cdkTreeNodeToggle>
<span class="fa fa-chevron-right invisible" <span class="fa fa-chevron-right invisible" aria-hidden="true"></span>
aria-hidden="true"></span>
</button> </button>
<h6 class="align-middle pt-2"> <h6 class="align-middle pt-2">
<a [routerLink]="node.route" class="lead"> <a [routerLink]="node.route" class="lead">
{{node.name}} {{ dsoNameService.getName(node.payload) }}
</a> </a>
</h6> </h6>
</div> </div>

View File

@@ -16,6 +16,7 @@ import { of as observableOf } from 'rxjs';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { isEmpty, isNotEmpty } from '../../shared/empty.util'; import { isEmpty, isNotEmpty } from '../../shared/empty.util';
import { FlatNode } from '../flat-node.model'; import { FlatNode } from '../flat-node.model';
import { RouterLinkWithHref } from '@angular/router';
describe('CommunityListComponent', () => { describe('CommunityListComponent', () => {
let component: CommunityListComponent; let component: CommunityListComponent;
@@ -193,7 +194,8 @@ describe('CommunityListComponent', () => {
}, },
}), }),
CdkTreeModule, CdkTreeModule,
RouterTestingModule], RouterTestingModule,
RouterLinkWithHref],
declarations: [CommunityListComponent], declarations: [CommunityListComponent],
providers: [CommunityListComponent, providers: [CommunityListComponent,
{ provide: CommunityListService, useValue: communityListServiceStub },], { provide: CommunityListService, useValue: communityListServiceStub },],
@@ -230,9 +232,14 @@ describe('CommunityListComponent', () => {
expect(showMoreEl).toBeTruthy(); 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', () => { describe('when show more of top communities is clicked', () => {
beforeEach(fakeAsync(() => { 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', { showMoreLink.triggerEventHandler('click', {
preventDefault: () => {/**/ preventDefault: () => {/**/
} }
@@ -240,6 +247,7 @@ describe('CommunityListComponent', () => {
tick(); tick();
fixture.detectChanges(); fixture.detectChanges();
})); }));
it('tree contains maximum of currentPage (2) * (2) elementsPerPage of first top communities, or less if there are less communities (3)', () => { 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 expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a'));
const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a')); const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a'));

View File

@@ -7,6 +7,7 @@ import { FlatTreeControl } from '@angular/cdk/tree';
import { isEmpty } from '../../shared/empty.util'; import { isEmpty } from '../../shared/empty.util';
import { FlatNode } from '../flat-node.model'; import { FlatNode } from '../flat-node.model';
import { FindListOptions } from '../../core/data/find-list-options.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. * 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; paginationConfig: FindListOptions;
constructor(private communityListService: CommunityListService) { constructor(
protected communityListService: CommunityListService,
public dsoNameService: DSONameService,
) {
this.paginationConfig = new FindListOptions(); this.paginationConfig = new FindListOptions();
this.paginationConfig.elementsPerPage = 2; this.paginationConfig.elementsPerPage = 2;
this.paginationConfig.currentPage = 1; this.paginationConfig.currentPage = 1;

View File

@@ -5,7 +5,7 @@
<div class="d-flex flex-row border-bottom mb-4 pb-4"> <div class="d-flex flex-row border-bottom mb-4 pb-4">
<header class="comcol-header mr-auto"> <header class="comcol-header mr-auto">
<!-- Community name --> <!-- Community name -->
<ds-comcol-page-header [name]="communityPayload.name"></ds-comcol-page-header> <ds-comcol-page-header [name]="dsoNameService.getName(communityPayload)"></ds-comcol-page-header>
<!-- Community logo --> <!-- Community logo -->
<ds-comcol-page-logo *ngIf="logoRD$" [logo]="(logoRD$ | async)?.payload" [alternateText]="'Community Logo'"> <ds-comcol-page-logo *ngIf="logoRD$" [logo]="(logoRD$ | async)?.payload" [alternateText]="'Community Logo'">
</ds-comcol-page-logo> </ds-comcol-page-logo>

View File

@@ -19,6 +19,7 @@ import { AuthorizationDataService } from '../core/data/feature-authorization/aut
import { FeatureID } from '../core/data/feature-authorization/feature-id'; import { FeatureID } from '../core/data/feature-authorization/feature-id';
import { getCommunityPageRoute } from './community-page-routing-paths'; import { getCommunityPageRoute } from './community-page-routing-paths';
import { redirectOn4xx } from '../core/shared/authorized.operators'; import { redirectOn4xx } from '../core/shared/authorized.operators';
import { DSONameService } from '../core/breadcrumbs/dso-name.service';
@Component({ @Component({
selector: 'ds-community-page', selector: 'ds-community-page',
@@ -57,7 +58,8 @@ export class CommunityPageComponent implements OnInit {
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private authService: AuthService, private authService: AuthService,
private authorizationDataService: AuthorizationDataService private authorizationDataService: AuthorizationDataService,
public dsoNameService: DSONameService,
) { ) {
} }

View File

@@ -3,7 +3,7 @@
<div class="col-12 pb-4"> <div class="col-12 pb-4">
<ng-container *ngVar="(parentRD$ | async)?.payload as parent"> <ng-container *ngVar="(parentRD$ | async)?.payload as parent">
<h2 *ngIf="!parent" id="header" class="border-bottom pb-2">{{ 'community.create.head' | translate }}</h2> <h2 *ngIf="!parent" id="header" class="border-bottom pb-2">{{ 'community.create.head' | translate }}</h2>
<h2 *ngIf="parent" id="sub-header" class="border-bottom pb-2">{{ 'community.create.sub-head' | translate:{ parent: parent.name } }}</h2> <h2 *ngIf="parent" id="sub-header" class="border-bottom pb-2">{{ 'community.create.sub-head' | translate:{ parent: dsoNameService.getName(parent) } }}</h2>
</ng-container> </ng-container>
</div> </div>
</div> </div>

View File

@@ -7,6 +7,7 @@ import { CreateComColPageComponent } from '../../shared/comcol/comcol-forms/crea
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { RequestService } from '../../core/data/request.service'; 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 * Component that represents the page where a user can create a new Community
@@ -22,12 +23,13 @@ export class CreateCommunityPageComponent extends CreateComColPageComponent<Comm
public constructor( public constructor(
protected communityDataService: CommunityDataService, protected communityDataService: CommunityDataService,
public dsoNameService: DSONameService,
protected routeService: RouteService, protected routeService: RouteService,
protected router: Router, protected router: Router,
protected notificationsService: NotificationsService, protected notificationsService: NotificationsService,
protected translate: TranslateService, protected translate: TranslateService,
protected requestService: RequestService protected requestService: RequestService
) { ) {
super(communityDataService, communityDataService, routeService, router, notificationsService, translate, requestService); super(communityDataService, dsoNameService, communityDataService, routeService, router, notificationsService, translate, requestService);
} }
} }

View File

@@ -3,7 +3,7 @@
<ng-container *ngVar="(dsoRD$ | async)?.payload as dso"> <ng-container *ngVar="(dsoRD$ | async)?.payload as dso">
<div class="col-12 pb-4"> <div class="col-12 pb-4">
<h2 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h2> <h2 id="header" class="border-bottom pb-2">{{ 'community.delete.head' | translate}}</h2>
<p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dso.name } }}</p> <p class="pb-2">{{ 'community.delete.text' | translate:{ dso: dsoNameService.getName(dso) } }}</p>
<div class="form-group row"> <div class="form-group row">
<div class="col text-right space-children-mr"> <div class="col text-right space-children-mr">
<button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)"> <button class="btn btn-outline-secondary" (click)="onCancel(dso)" [disabled]="(processing$ | async)">

View File

@@ -10,6 +10,8 @@ import { NotificationsService } from '../../shared/notifications/notifications.s
import { SharedModule } from '../../shared/shared.module'; import { SharedModule } from '../../shared/shared.module';
import { DeleteCommunityPageComponent } from './delete-community-page.component'; import { DeleteCommunityPageComponent } from './delete-community-page.component';
import { RequestService } from '../../core/data/request.service'; import { RequestService } from '../../core/data/request.service';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../../shared/mocks/dso-name.service.mock';
describe('DeleteCommunityPageComponent', () => { describe('DeleteCommunityPageComponent', () => {
let comp: DeleteCommunityPageComponent; let comp: DeleteCommunityPageComponent;
@@ -20,6 +22,7 @@ describe('DeleteCommunityPageComponent', () => {
imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule], imports: [TranslateModule.forRoot(), SharedModule, CommonModule, RouterTestingModule],
declarations: [DeleteCommunityPageComponent], declarations: [DeleteCommunityPageComponent],
providers: [ providers: [
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: CommunityDataService, useValue: {} }, { provide: CommunityDataService, useValue: {} },
{ provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } }, { provide: ActivatedRoute, useValue: { data: observableOf({ dso: { payload: {} } }) } },
{ provide: NotificationsService, useValue: {} }, { provide: NotificationsService, useValue: {} },

View File

@@ -5,6 +5,7 @@ import { ActivatedRoute, Router } from '@angular/router';
import { DeleteComColPageComponent } from '../../shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component'; import { DeleteComColPageComponent } from '../../shared/comcol/comcol-forms/delete-comcol-page/delete-comcol-page.component';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
/** /**
* Component that represents the page where a user can delete an existing Community * Component that represents the page where a user can delete an existing Community
@@ -19,12 +20,13 @@ export class DeleteCommunityPageComponent extends DeleteComColPageComponent<Comm
public constructor( public constructor(
protected dsoDataService: CommunityDataService, protected dsoDataService: CommunityDataService,
public dsoNameService: DSONameService,
protected router: Router, protected router: Router,
protected route: ActivatedRoute, protected route: ActivatedRoute,
protected notifications: NotificationsService, protected notifications: NotificationsService,
protected translate: TranslateService, protected translate: TranslateService,
) { ) {
super(dsoDataService, router, route, notifications, translate); super(dsoDataService, dsoNameService, router, route, notifications, translate);
} }
} }

View File

@@ -15,6 +15,8 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ComcolModule } from '../../../shared/comcol/comcol.module'; import { ComcolModule } from '../../../shared/comcol/comcol.module';
import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../../../shared/mocks/dso-name.service.mock';
describe('CommunityRolesComponent', () => { describe('CommunityRolesComponent', () => {
@@ -63,6 +65,7 @@ describe('CommunityRolesComponent', () => {
CommunityRolesComponent, CommunityRolesComponent,
], ],
providers: [ providers: [
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: ActivatedRoute, useValue: route }, { provide: ActivatedRoute, useValue: route },
{ provide: RequestService, useValue: requestService }, { provide: RequestService, useValue: requestService },
{ provide: GroupDataService, useValue: groupDataService }, { provide: GroupDataService, useValue: groupDataService },

View File

@@ -1,4 +1,5 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs'; import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs';
@@ -49,19 +50,23 @@ export class CommunityPageSubCollectionListComponent implements OnInit, OnDestro
*/ */
subCollectionsRDObs: BehaviorSubject<RemoteData<PaginatedList<Collection>>> = new BehaviorSubject<RemoteData<PaginatedList<Collection>>>({} as any); subCollectionsRDObs: BehaviorSubject<RemoteData<PaginatedList<Collection>>> = new BehaviorSubject<RemoteData<PaginatedList<Collection>>>({} as any);
constructor(private cds: CollectionDataService, constructor(
private paginationService: PaginationService, protected cds: CollectionDataService,
protected paginationService: PaginationService,
) {} protected route: ActivatedRoute,
) {
}
ngOnInit(): void { ngOnInit(): void {
this.config = new PaginationComponentOptions(); this.config = new PaginationComponentOptions();
this.config.id = this.pageId; this.config.id = this.pageId;
if (hasValue(this.pageSize)) { if (hasValue(this.pageSize)) {
this.config.pageSize = this.pageSize; this.config.pageSize = this.pageSize;
} else {
this.config.pageSize = this.route.snapshot.queryParams[this.pageId + '.rpp'] ?? this.config.pageSize;
} }
this.config.currentPage = 1; this.config.currentPage = this.route.snapshot.queryParams[this.pageId + '.page'] ?? 1;
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC); this.sortConfig = new SortOptions('dc.title', SortDirection[this.route.snapshot.queryParams[this.pageId + '.sd']] ?? SortDirection.ASC);
this.initPage(); this.initPage();
} }

View File

@@ -1,4 +1,5 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs'; import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs';
@@ -9,7 +10,6 @@ import { PaginatedList } from '../../core/data/paginated-list.model';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import { CommunityDataService } from '../../core/data/community-data.service'; import { CommunityDataService } from '../../core/data/community-data.service';
import { takeUntilCompletedRemoteData } from '../../core/shared/operators';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationService } from '../../core/pagination/pagination.service';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
@@ -52,8 +52,10 @@ export class CommunityPageSubCommunityListComponent implements OnInit, OnDestroy
*/ */
subCommunitiesRDObs: BehaviorSubject<RemoteData<PaginatedList<Community>>> = new BehaviorSubject<RemoteData<PaginatedList<Community>>>({} as any); subCommunitiesRDObs: BehaviorSubject<RemoteData<PaginatedList<Community>>> = new BehaviorSubject<RemoteData<PaginatedList<Community>>>({} as any);
constructor(private cds: CommunityDataService, constructor(
private paginationService: PaginationService protected cds: CommunityDataService,
protected paginationService: PaginationService,
protected route: ActivatedRoute,
) { ) {
} }
@@ -62,9 +64,11 @@ export class CommunityPageSubCommunityListComponent implements OnInit, OnDestroy
this.config.id = this.pageId; this.config.id = this.pageId;
if (hasValue(this.pageSize)) { if (hasValue(this.pageSize)) {
this.config.pageSize = this.pageSize; this.config.pageSize = this.pageSize;
} else {
this.config.pageSize = this.route.snapshot.queryParams[this.pageId + '.rpp'] ?? this.config.pageSize;
} }
this.config.currentPage = 1; this.config.currentPage = this.route.snapshot.queryParams[this.pageId + '.page'] ?? 1;
this.sortConfig = new SortOptions('dc.title', SortDirection.ASC); this.sortConfig = new SortOptions('dc.title', SortDirection[this.route.snapshot.queryParams[this.pageId + '.sd']] ?? SortDirection.ASC);
this.initPage(); this.initPage();
} }
@@ -86,15 +90,6 @@ export class CommunityPageSubCommunityListComponent implements OnInit, OnDestroy
).subscribe((results) => { ).subscribe((results) => {
this.subCommunitiesRDObs.next(results); this.subCommunitiesRDObs.next(results);
}); });
this.cds.findByParent(this.community.id, {
currentPage: this.config.currentPage,
elementsPerPage: this.config.pageSize,
sort: { field: this.sortConfig.field, direction: this.sortConfig.direction }
}).pipe(takeUntilCompletedRemoteData()).subscribe((results) => {
this.subCommunitiesRDObs.next(results);
});
} }
ngOnDestroy(): void { ngOnDestroy(): void {

View File

@@ -27,11 +27,22 @@ export class DSONameService {
* With only two exceptions those solutions seem overkill for now. * With only two exceptions those solutions seem overkill for now.
*/ */
private readonly factories = { private readonly factories = {
EPerson: (dso: DSpaceObject): string => {
const firstName = dso.firstMetadataValue('eperson.firstname');
const lastName = dso.firstMetadataValue('eperson.lastname');
if (isEmpty(firstName) && isEmpty(lastName)) {
return this.translateService.instant('dso.name.unnamed');
} else if (isEmpty(firstName) || isEmpty(lastName)) {
return firstName || lastName;
} else {
return `${firstName} ${lastName}`;
}
},
Person: (dso: DSpaceObject): string => { Person: (dso: DSpaceObject): string => {
const familyName = dso.firstMetadataValue('person.familyName'); const familyName = dso.firstMetadataValue('person.familyName');
const givenName = dso.firstMetadataValue('person.givenName'); const givenName = dso.firstMetadataValue('person.givenName');
if (isEmpty(familyName) && isEmpty(givenName)) { if (isEmpty(familyName) && isEmpty(givenName)) {
return dso.firstMetadataValue('dc.title') || dso.name; return dso.firstMetadataValue('dc.title') || this.translateService.instant('dso.name.unnamed');
} else if (isEmpty(familyName) || isEmpty(givenName)) { } else if (isEmpty(familyName) || isEmpty(givenName)) {
return familyName || givenName; return familyName || givenName;
} else { } else {
@@ -52,20 +63,24 @@ export class DSONameService {
* *
* @param dso The {@link DSpaceObject} you want a name for * @param dso The {@link DSpaceObject} you want a name for
*/ */
getName(dso: DSpaceObject): string { getName(dso: DSpaceObject | undefined): string {
const types = dso.getRenderTypes(); if (dso) {
const match = types const types = dso.getRenderTypes();
.filter((type) => typeof type === 'string') const match = types
.find((type: string) => Object.keys(this.factories).includes(type)) as string; .filter((type) => typeof type === 'string')
.find((type: string) => Object.keys(this.factories).includes(type)) as string;
let name; let name;
if (hasValue(match)) { if (hasValue(match)) {
name = this.factories[match](dso); name = this.factories[match](dso);
}
if (isEmpty(name)) {
name = this.factories.Default(dso);
}
return name;
} else {
return '';
} }
if (isEmpty(name)) {
name = this.factories.Default(dso);
}
return name;
} }
/** /**

View File

@@ -60,7 +60,7 @@ export class LinkService {
const provider = this.getDataServiceFor(matchingLinkDef.resourceType); const provider = this.getDataServiceFor(matchingLinkDef.resourceType);
if (hasNoValue(provider)) { if (hasNoValue(provider)) {
throw new Error(`The @link() for ${linkToFollow.name} on ${model.constructor.name} models uses the resource type ${matchingLinkDef.resourceType.value.toUpperCase()}, but there is no service with an @dataService(${matchingLinkDef.resourceType.value.toUpperCase()}) annotation in order to retrieve it`); throw new Error(`The @link() for ${String(linkToFollow.name)} on ${model.constructor.name} models uses the resource type ${matchingLinkDef.resourceType.value.toUpperCase()}, but there is no service with an @dataService(${matchingLinkDef.resourceType.value.toUpperCase()}) annotation in order to retrieve it`);
} }
const service: HALDataService<any> = Injector.create({ const service: HALDataService<any> = Injector.create({
@@ -79,12 +79,12 @@ export class LinkService {
return service.findByHref(href, linkToFollow.useCachedVersionIfAvailable, linkToFollow.reRequestOnStale, ...linkToFollow.linksToFollow); return service.findByHref(href, linkToFollow.useCachedVersionIfAvailable, linkToFollow.reRequestOnStale, ...linkToFollow.linksToFollow);
} }
} catch (e) { } catch (e) {
console.error(`Something went wrong when using @dataService(${matchingLinkDef.resourceType.value}) ${hasValue(service) ? '' : '(undefined) '}to resolve link ${linkToFollow.name} at ${href}`); console.error(`Something went wrong when using @dataService(${matchingLinkDef.resourceType.value}) ${hasValue(service) ? '' : '(undefined) '}to resolve link ${String(linkToFollow.name)} at ${href}`);
throw e; throw e;
} }
} }
} else if (!linkToFollow.isOptional) { } else if (!linkToFollow.isOptional) {
throw new Error(`followLink('${linkToFollow.name}') was used as a required link for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`); throw new Error(`followLink('${String(linkToFollow.name)}') was used as a required link for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${String(linkToFollow.name)}`);
} }
return EMPTY; return EMPTY;

View File

@@ -73,7 +73,7 @@ export class RemoteDataBuildService {
if (getResourceTypeValueFor((obj as any).type) === PAGINATED_LIST.value) { if (getResourceTypeValueFor((obj as any).type) === PAGINATED_LIST.value) {
return this.buildPaginatedList<T>(obj, ...linksToFollow); return this.buildPaginatedList<T>(obj, ...linksToFollow);
} else if (isNotEmpty(linksToFollow)) { } else if (isNotEmpty(linksToFollow)) {
return [this.linkService.resolveLinks(obj, ...linksToFollow)]; return [this.linkService.resolveLinks(obj as any, ...linksToFollow)];
} }
} }
return [obj]; return [obj];

View File

@@ -160,7 +160,7 @@ import { SubmissionAccessesModel } from './config/models/config-submission-acces
import { RatingAdvancedWorkflowInfo } from './tasks/models/rating-advanced-workflow-info.model'; import { RatingAdvancedWorkflowInfo } from './tasks/models/rating-advanced-workflow-info.model';
import { AdvancedWorkflowInfo } from './tasks/models/advanced-workflow-info.model'; import { AdvancedWorkflowInfo } from './tasks/models/advanced-workflow-info.model';
import { SelectReviewerAdvancedWorkflowInfo } from './tasks/models/select-reviewer-advanced-workflow-info.model'; import { SelectReviewerAdvancedWorkflowInfo } from './tasks/models/select-reviewer-advanced-workflow-info.model';
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 { AccessStatusDataService } from './data/access-status-data.service'; import { AccessStatusDataService } from './data/access-status-data.service';
import { LinkHeadService } from './services/link-head.service'; import { LinkHeadService } from './services/link-head.service';
import { ResearcherProfileDataService } from './profile/researcher-profile-data.service'; import { ResearcherProfileDataService } from './profile/researcher-profile-data.service';

View File

@@ -3,8 +3,8 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv
import { ObjectCacheService } from '../cache/object-cache.service'; import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service'; import { HALEndpointService } from '../shared/hal-endpoint.service';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model'; import { AccessStatusObject } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.model';
import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type'; import { ACCESS_STATUS } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.resource-type';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { RemoteData } from './remote-data'; import { RemoteData } from './remote-data';
import { Item } from '../shared/item.model'; import { Item } from '../shared/item.model';

View File

@@ -641,6 +641,62 @@ describe('BaseDataService', () => {
}); });
}); });
describe('hasCachedResponse', () => {
it('should return false when the request will be dispatched', (done) => {
const result = service.hasCachedResponse('test-href');
result.subscribe((hasCachedResponse) => {
expect(hasCachedResponse).toBeFalse();
done();
});
});
it('should return true when the request will not be dispatched', (done) => {
(requestService.shouldDispatchRequest as jasmine.Spy).and.returnValue(false);
const result = service.hasCachedResponse('test-href');
result.subscribe((hasCachedResponse) => {
expect(hasCachedResponse).toBeTrue();
done();
});
});
});
describe('hasCachedErrorResponse', () => {
it('should return false when no response is cached', (done) => {
spyOn(service,'hasCachedResponse').and.returnValue(observableOf(false));
const result = service.hasCachedErrorResponse('test-href');
result.subscribe((hasCachedErrorResponse) => {
expect(hasCachedErrorResponse).toBeFalse();
done();
});
});
it('should return false when no error response is cached', (done) => {
spyOn(service,'hasCachedResponse').and.returnValue(observableOf(true));
spyOn(rdbService,'buildSingle').and.returnValue(createSuccessfulRemoteDataObject$({}));
const result = service.hasCachedErrorResponse('test-href');
result.subscribe((hasCachedErrorResponse) => {
expect(hasCachedErrorResponse).toBeFalse();
done();
});
});
it('should return true when an error response is cached', (done) => {
spyOn(service,'hasCachedResponse').and.returnValue(observableOf(true));
spyOn(rdbService,'buildSingle').and.returnValue(createFailedRemoteDataObject$());
const result = service.hasCachedErrorResponse('test-href');
result.subscribe((hasCachedErrorResponse) => {
expect(hasCachedErrorResponse).toBeTrue();
done();
});
});
});
describe('addDependency', () => { describe('addDependency', () => {
let addDependencySpy; let addDependencySpy;

View File

@@ -341,6 +341,48 @@ export class BaseDataService<T extends CacheableObject> implements HALDataServic
} }
} }
/**
* Checks for the provided href whether a response is already cached
* @param href$ The url for which to check whether there is a cached response.
* Can be a string or an Observable<string>
*/
hasCachedResponse(href$: string | Observable<string>): Observable<boolean> {
if (isNotEmpty(href$)) {
if (typeof href$ === 'string') {
href$ = observableOf(href$);
}
return href$.pipe(
isNotEmptyOperator(),
take(1),
map((href: string) => {
const requestId = this.requestService.generateRequestId();
const request = new GetRequest(requestId, href);
return !this.requestService.shouldDispatchRequest(request, true);
}),
);
}
throw new Error(`Can't check whether there is a cached response for an empty href$`);
}
/**
* Checks for the provided href whether an ERROR response is currently cached
* @param href$ The url for which to check whether there is a cached ERROR response.
* Can be a string or an Observable<string>
*/
hasCachedErrorResponse(href$: string | Observable<string>): Observable<boolean> {
return this.hasCachedResponse(href$).pipe(
switchMap((hasCachedResponse) => {
if (hasCachedResponse) {
return this.rdbService.buildSingle(href$).pipe(
getFirstCompletedRemoteData(),
map((rd => rd.hasFailed))
);
}
return observableOf(false);
})
);
}
/** /**
* Return the links to traverse from the root of the api to the * Return the links to traverse from the root of the api to the
* endpoint this DataService represents * endpoint this DataService represents

View File

@@ -5,6 +5,7 @@ import { ExternalSourceEntry } from '../shared/external-source-entry.model';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { GetRequest } from './request.models'; import { GetRequest } from './request.models';
import { testSearchDataImplementation } from './base/search-data.spec'; import { testSearchDataImplementation } from './base/search-data.spec';
import { take } from 'rxjs/operators';
describe('ExternalSourceService', () => { describe('ExternalSourceService', () => {
let service: ExternalSourceDataService; let service: ExternalSourceDataService;
@@ -64,19 +65,42 @@ describe('ExternalSourceService', () => {
}); });
describe('getExternalSourceEntries', () => { describe('getExternalSourceEntries', () => {
let result;
beforeEach(() => { describe('when no error response is cached', () => {
result = service.getExternalSourceEntries('test'); let result;
beforeEach(() => {
spyOn(service, 'hasCachedErrorResponse').and.returnValue(observableOf(false));
result = service.getExternalSourceEntries('test');
});
it('should send a GetRequest', () => {
result.pipe(take(1)).subscribe();
expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), true);
});
it('should return the entries', () => {
result.subscribe((resultRD) => {
expect(resultRD.payload.page).toBe(entries);
});
});
}); });
it('should send a GetRequest', () => { describe('when an error response is cached', () => {
expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), true); let result;
}); beforeEach(() => {
spyOn(service, 'hasCachedErrorResponse').and.returnValue(observableOf(true));
result = service.getExternalSourceEntries('test');
});
it('should return the entries', () => { it('should send a GetRequest', () => {
result.subscribe((resultRD) => { result.pipe(take(1)).subscribe();
expect(resultRD.payload.page).toBe(entries); expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), false);
});
it('should return the entries', () => {
result.subscribe((resultRD) => {
expect(resultRD.payload.page).toBe(entries);
});
}); });
}); });
}); });

View File

@@ -74,7 +74,12 @@ export class ExternalSourceDataService extends IdentifiableDataService<ExternalS
); );
// TODO create a dedicated ExternalSourceEntryDataService and move this entire method to it. Then the "as any"s won't be necessary // TODO create a dedicated ExternalSourceEntryDataService and move this entire method to it. Then the "as any"s won't be necessary
return this.findListByHref(href$, undefined, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow as any) as any;
return this.hasCachedErrorResponse(href$).pipe(
switchMap((hasCachedErrorResponse) => {
return this.findListByHref(href$, undefined, !hasCachedErrorResponse, reRequestOnStale, ...linksToFollow as any);
})
) as any;
} }
/** /**

View File

@@ -9,6 +9,8 @@ import { HALLink } from '../../shared/hal-link.model';
import { EPERSON } from './eperson.resource-type'; import { EPERSON } from './eperson.resource-type';
import { Group } from './group.model'; import { Group } from './group.model';
import { GROUP } from './group.resource-type'; import { GROUP } from './group.resource-type';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
import { GenericConstructor } from '../../shared/generic-constructor';
@typedObject @typedObject
@inheritSerialization(DSpaceObject) @inheritSerialization(DSpaceObject)
@@ -82,4 +84,8 @@ export class EPerson extends DSpaceObject {
@link(GROUP, true) @link(GROUP, true)
public groups?: Observable<RemoteData<PaginatedList<Group>>>; public groups?: Observable<RemoteData<PaginatedList<Group>>>;
getRenderTypes(): (string | GenericConstructor<ListableObject>)[] {
return [this.constructor.name, ...super.getRenderTypes()];
}
} }

View File

@@ -28,7 +28,7 @@ describe('LocaleService test suite', () => {
isAuthenticationLoaded: jasmine.createSpy('isAuthenticationLoaded') isAuthenticationLoaded: jasmine.createSpy('isAuthenticationLoaded')
}); });
const langList = ['en', 'it', 'de']; const langList = ['en', 'xx', 'de'];
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
return TestBed.configureTestingModule({ return TestBed.configureTestingModule({
@@ -82,8 +82,8 @@ describe('LocaleService test suite', () => {
}); });
it('should return language from browser setting', () => { it('should return language from browser setting', () => {
spyOn(translateService, 'getBrowserLang').and.returnValue('it'); spyOn(translateService, 'getBrowserLang').and.returnValue('xx');
expect(service.getCurrentLanguageCode()).toBe('it'); expect(service.getCurrentLanguageCode()).toBe('xx');
}); });
it('should return default language from config', () => { it('should return default language from config', () => {
@@ -114,9 +114,9 @@ describe('LocaleService test suite', () => {
}); });
it('should set the given language', () => { it('should set the given language', () => {
service.setCurrentLanguageCode('it'); service.setCurrentLanguageCode('xx');
expect(translateService.use).toHaveBeenCalledWith('it'); expect(translateService.use).toHaveBeenCalledWith('xx');
expect(service.saveLanguageCodeToCookie).toHaveBeenCalledWith('it'); expect(service.saveLanguageCodeToCookie).toHaveBeenCalledWith('xx');
}); });
it('should set the current language', () => { it('should set the current language', () => {
@@ -135,7 +135,7 @@ describe('LocaleService test suite', () => {
describe('', () => { describe('', () => {
it('should set quality to current language list', () => { it('should set quality to current language list', () => {
const langListWithQuality = ['en;q=1', 'it;q=0.9', 'de;q=0.8']; const langListWithQuality = ['en;q=1', 'xx;q=0.9', 'de;q=0.8'];
spyOn(service, 'setQuality').and.returnValue(langListWithQuality); spyOn(service, 'setQuality').and.returnValue(langListWithQuality);
service.setQuality(langList, LANG_ORIGIN.BROWSER, false); service.setQuality(langList, LANG_ORIGIN.BROWSER, false);
expect(service.setQuality).toHaveBeenCalledWith(langList, LANG_ORIGIN.BROWSER, false); expect(service.setQuality).toHaveBeenCalledWith(langList, LANG_ORIGIN.BROWSER, false);

View File

@@ -3,17 +3,38 @@
*/ */
export enum Context { export enum Context {
/** Default context */
Any = 'undefined', Any = 'undefined',
/** General item page context */
ItemPage = 'itemPage', ItemPage = 'itemPage',
/** General search page context */
Search = 'search', Search = 'search',
Workflow = 'workflow', Workflow = 'workflow',
Workspace = 'workspace', Workspace = 'workspace',
SupervisedItems = 'supervisedWorkspace', SupervisedItems = 'supervisedWorkspace',
/** Administrative menu context */
AdminMenu = 'adminMenu', AdminMenu = 'adminMenu',
EntitySearchModalWithNameVariants = 'EntitySearchModalWithNameVariants', EntitySearchModalWithNameVariants = 'EntitySearchModalWithNameVariants',
EntitySearchModal = 'EntitySearchModal', EntitySearchModal = 'EntitySearchModal',
/** Administrative search page context */
AdminSearch = 'adminSearch', AdminSearch = 'adminSearch',
AdminWorkflowSearch = 'adminWorkflowSearch', AdminWorkflowSearch = 'adminWorkflowSearch',
SideBarSearchModal = 'sideBarSearchModal', SideBarSearchModal = 'sideBarSearchModal',
SideBarSearchModalCurrent = 'sideBarSearchModalCurrent', SideBarSearchModalCurrent = 'sideBarSearchModalCurrent',
/** The MyDSpace* Context values below are used for badge display in MyDSpace. */
MyDSpaceArchived = 'mydspaceArchived',
MyDSpaceWorkspace = 'mydspaceWorkspace',
MyDSpaceWorkflow = 'mydspaceWorkflow',
MyDSpaceDeclined = 'mydspaceDeclined',
MyDSpaceApproved = 'mydspaceApproved',
MyDSpaceWaitingController = 'mydspaceWaitingController',
MyDSpaceValidation = 'mydspaceValidation',
} }

View File

@@ -21,8 +21,8 @@ import { Version } from './version.model';
import { VERSION } from './version.resource-type'; import { VERSION } from './version.resource-type';
import { BITSTREAM } from './bitstream.resource-type'; import { BITSTREAM } from './bitstream.resource-type';
import { Bitstream } from './bitstream.model'; import { Bitstream } from './bitstream.model';
import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type'; import { ACCESS_STATUS } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.resource-type';
import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model'; import { AccessStatusObject } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.model';
import { HandleObject } from './handle-object.model'; import { HandleObject } from './handle-object.model';
import { IDENTIFIERS } from '../../shared/object-list/identifier-data/identifier-data.resource-type'; import { IDENTIFIERS } from '../../shared/object-list/identifier-data/identifier-data.resource-type';
import { IdentifierData } from '../../shared/object-list/identifier-data/identifier-data.model'; import { IdentifierData } from '../../shared/object-list/identifier-data/identifier-data.model';

View File

@@ -1,6 +1,6 @@
import { Component, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { ScriptDataService } from '../core/data/processes/script-data.service'; import { ScriptDataService } from '../core/data/processes/script-data.service';
import { FormControl, FormGroup } from '@angular/forms'; import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { getFirstCompletedRemoteData } from '../core/shared/operators'; import { getFirstCompletedRemoteData } from '../core/shared/operators';
import { find, map } from 'rxjs/operators'; import { find, map } from 'rxjs/operators';
import { NotificationsService } from '../shared/notifications/notifications.service'; import { NotificationsService } from '../shared/notifications/notifications.service';
@@ -28,7 +28,7 @@ export class CurationFormComponent implements OnInit {
config: Observable<RemoteData<ConfigurationProperty>>; config: Observable<RemoteData<ConfigurationProperty>>;
tasks: string[]; tasks: string[];
form: FormGroup; form: UntypedFormGroup;
@Input() @Input()
dsoHandle: string; dsoHandle: string;
@@ -40,14 +40,15 @@ export class CurationFormComponent implements OnInit {
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private translateService: TranslateService, private translateService: TranslateService,
private handleService: HandleService, private handleService: HandleService,
private router: Router private router: Router,
private cdr: ChangeDetectorRef
) { ) {
} }
ngOnInit(): void { ngOnInit(): void {
this.form = new FormGroup({ this.form = new UntypedFormGroup({
task: new FormControl(''), task: new UntypedFormControl(''),
handle: new FormControl('') handle: new UntypedFormControl('')
}); });
this.config = this.configurationDataService.findByPropertyName(CURATION_CFG); this.config = this.configurationDataService.findByPropertyName(CURATION_CFG);
@@ -59,6 +60,7 @@ export class CurationFormComponent implements OnInit {
.filter((value) => isNotEmpty(value) && value.includes('=')) .filter((value) => isNotEmpty(value) && value.includes('='))
.map((value) => value.split('=')[1].trim()); .map((value) => value.split('=')[1].trim());
this.form.get('task').patchValue(this.tasks[0]); this.form.get('task').patchValue(this.tasks[0]);
this.cdr.detectChanges();
}); });
} }

View File

@@ -7,7 +7,7 @@
[dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea> [dsDebounce]="300" (onDebounce)="confirm.emit(false)"></textarea>
<div class="d-flex" *ngIf="mdRepresentation"> <div class="d-flex" *ngIf="mdRepresentation">
<a class="mr-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a> <a class="mr-2" target="_blank" [routerLink]="mdRepresentationItemRoute$ | async">{{ mdRepresentationName$ | async }}</a>
<ds-type-badge [object]="mdRepresentation"></ds-type-badge> <ds-themed-type-badge [object]="mdRepresentation"></ds-themed-type-badge>
</div> </div>
</div> </div>
<div class="ds-flex-cell ds-lang-cell" role="cell"> <div class="ds-flex-cell ds-lang-cell" role="cell">

View File

@@ -68,7 +68,7 @@ describe('DsoEditMetadataValueComponent', () => {
}); });
it('should not show a badge', () => { 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', () => { describe('when no changes have been made', () => {
@@ -134,7 +134,7 @@ describe('DsoEditMetadataValueComponent', () => {
}); });
it('should show a badge', () => { 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); assertButton(EDIT_BTN, true, true);

View File

@@ -17,7 +17,7 @@ import {
} from '../../../core/shared/operators'; } from '../../../core/shared/operators';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { RegistryService } from '../../../core/registry/registry.service'; import { RegistryService } from '../../../core/registry/registry.service';
import { FormControl } from '@angular/forms'; import { UntypedFormControl } from '@angular/forms';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { hasValue } from '../../../shared/empty.util'; import { hasValue } from '../../../shared/empty.util';
import { Subscription } from 'rxjs/internal/Subscription'; import { Subscription } from 'rxjs/internal/Subscription';
@@ -70,7 +70,7 @@ export class MetadataFieldSelectorComponent implements OnInit, OnDestroy, AfterV
/** /**
* FormControl for the input * FormControl for the input
*/ */
public input: FormControl = new FormControl(); public input: UntypedFormControl = new UntypedFormControl();
/** /**
* The current query to update mdFieldOptions$ for * The current query to update mdFieldOptions$ for

View File

@@ -10,6 +10,8 @@ import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser'; 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(), { const mockItem = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
@@ -48,6 +50,7 @@ describe('JournalIssueGridElementComponent', () => {
imports: [NoopAnimationsModule], imports: [NoopAnimationsModule],
declarations: [JournalIssueGridElementComponent, TruncatePipe], declarations: [JournalIssueGridElementComponent, TruncatePipe],
providers: [ providers: [
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: TruncatableService, useValue: truncatableServiceStub }, { provide: TruncatableService, useValue: truncatableServiceStub },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]

View File

@@ -10,6 +10,8 @@ import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser'; 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(), { const mockItem = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
@@ -48,6 +50,7 @@ describe('JournalVolumeGridElementComponent', () => {
imports: [NoopAnimationsModule], imports: [NoopAnimationsModule],
declarations: [JournalVolumeGridElementComponent, TruncatePipe], declarations: [JournalVolumeGridElementComponent, TruncatePipe],
providers: [ providers: [
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: TruncatableService, useValue: truncatableServiceStub }, { provide: TruncatableService, useValue: truncatableServiceStub },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]

View File

@@ -10,6 +10,8 @@ import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser'; 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(), { const mockItem = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])),
@@ -54,6 +56,7 @@ describe('JournalGridElementComponent', () => {
imports: [NoopAnimationsModule], imports: [NoopAnimationsModule],
declarations: [JournalGridElementComponent, TruncatePipe], declarations: [JournalGridElementComponent, TruncatePipe],
providers: [ providers: [
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: TruncatableService, useValue: truncatableServiceStub }, { provide: TruncatableService, useValue: truncatableServiceStub },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]

View File

@@ -8,18 +8,18 @@
rel="noopener noreferrer" [routerLink]="[itemPageRoute]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate"> class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
<div> <div>
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false"> <ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
</ds-thumbnail> </ds-themed-thumbnail>
</div> </div>
</a> </a>
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width"> <span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
<div> <div>
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false"> <ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
</ds-thumbnail> </ds-themed-thumbnail>
</div> </div>
</span> </span>
<div class="card-body"> <div class="card-body">
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-themed-badges *ngIf="showLabel" [object]="dso" [context]="context"></ds-themed-badges>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4"> <ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="dsoTitle"></h4> <h4 class="card-title" [innerHTML]="dsoTitle"></h4>
</ds-truncatable-part> </ds-truncatable-part>

View File

@@ -8,18 +8,18 @@
rel="noopener noreferrer" [routerLink]="[itemPageRoute]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate"> class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
<div> <div>
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false"> <ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
</ds-thumbnail> </ds-themed-thumbnail>
</div> </div>
</a> </a>
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width"> <span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
<div> <div>
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false"> <ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
</ds-thumbnail> </ds-themed-thumbnail>
</div> </div>
</span> </span>
<div class="card-body"> <div class="card-body">
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-themed-badges *ngIf="showLabel" [object]="dso" [context]="context"></ds-themed-badges>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4"> <ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="dsoTitle"></h4> <h4 class="card-title" [innerHTML]="dsoTitle"></h4>
</ds-truncatable-part> </ds-truncatable-part>

View File

@@ -8,18 +8,18 @@
rel="noopener noreferrer" [routerLink]="[itemPageRoute]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate"> class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
<div> <div>
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false"> <ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
</ds-thumbnail> </ds-themed-thumbnail>
</div> </div>
</a> </a>
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width"> <span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
<div> <div>
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false"> <ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
</ds-thumbnail> </ds-themed-thumbnail>
</div> </div>
</span> </span>
<div class="card-body"> <div class="card-body">
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-themed-badges *ngIf="showLabel" [object]="dso" [context]="context"></ds-themed-badges>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4"> <ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="dsoTitle"></h4> <h4 class="card-title" [innerHTML]="dsoTitle"></h4>
</ds-truncatable-part> </ds-truncatable-part>

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