Skip to content

Commit 3da7b15

Browse files
author
Andrea Barbasso
committed
Merged dspace-cris-2023_02_x into task/dspace-cris-2023_02_x/DSC-1745
2 parents a805607 + bebc88c commit 3da7b15

55 files changed

Lines changed: 96168 additions & 28699 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/metadataGroup/metadata-group.component.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ export abstract class MetadataGroupComponent extends RenderingTypeStructuredMode
3232
/**
3333
* The prefix used for box field label's i18n key
3434
*/
35-
fieldI18nPrefix = 'layout.field.label.';
35+
readonly fieldI18nPrefix = 'layout.field.label';
36+
37+
/**
38+
* The prefix used for box field label's
39+
*/
40+
readonly nestedMetadataPrefix = 'NESTED';
3641

3742
/**
3843
* A boolean representing if component is initialized
@@ -80,17 +85,23 @@ export abstract class MetadataGroupComponent extends RenderingTypeStructuredMode
8085
}
8186

8287
/**
83-
* Returns a string representing the label of field if exists
88+
* Returns the translated label, if exists, otherwiuse returns a fallback value
8489
*/
8590
getLabel(field: LayoutField): string {
86-
const fieldLabelI18nKey = this.fieldI18nPrefix + field.label;
87-
const header: string = this.translateService.instant(fieldLabelI18nKey);
88-
if (header === fieldLabelI18nKey) {
89-
// if translation does not exist return the value present in the header property
90-
return this.translateService.instant(field.label);
91-
} else {
92-
return header;
93-
}
91+
return this.getTranslationIfExists(`${this.fieldI18nPrefix}.${this.item.entityType}.${this.nestedMetadataPrefix}[${field.metadata}]}`) ??
92+
this.getTranslationIfExists(`${this.fieldI18nPrefix}.${this.item.entityType}.[${field.metadata}]`) ??
93+
this.getTranslationIfExists(`${this.fieldI18nPrefix}.${this.item.entityType}.${field.metadata}`) ?? // old syntax - do not use
94+
this.getTranslationIfExists(`${this.fieldI18nPrefix}.[${field.metadata}]`) ??
95+
this.getTranslationIfExists(`${this.fieldI18nPrefix}.${field.label}`) ?? // old syntax - do not use
96+
field.label; // the untranslated value from the CRIS layout
97+
}
98+
99+
/**
100+
* Return the translated label, if exists, otherwise returns null
101+
*/
102+
getTranslationIfExists(key: string): string {
103+
const translation: string = this.translateService.instant(key);
104+
return translation !== key ? translation : null;
94105
}
95106

96107
ngOnDestroy(): void {

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-container.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div *ngIf="metadataFieldRenderOptions" class="{{containerStyle}}">
22
<div class="d-flex" [class.flex-column]="field.labelAsHeading">
33
<span *ngIf="hasLabel"
4-
class="{{labelStyle}}" [innerHTML]="label">
4+
class="{{labelStyle}}" [innerHTML]="getLabel()">
55
</span>
66

77
<div *ngIf="!isStructured" class="d-flex col flex-wrap" [class.flex-column]="!field.valuesInline">

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/row/metadata-container/metadata-container.component.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { CrisLayoutBox, LayoutField, LayoutFieldType } from '../../../../../../.
44
import {
55
FieldRenderingType,
66
getMetadataBoxFieldRendering,
7-
MetadataBoxFieldRenderOptions
7+
MetadataBoxFieldRenderOptions,
88
} from '../../rendering-types/metadata-box.decorator';
99
import { hasValue, isEmpty, isNotEmpty } from '../../../../../../../shared/empty.util';
1010
import { TranslateService } from '@ngx-translate/core';
@@ -13,11 +13,11 @@ import { MetadataValue } from '../../../../../../../core/shared/metadata.models'
1313
import { Bitstream } from '../../../../../../../core/shared/bitstream.model';
1414
import { getFirstCompletedRemoteData } from '../../../../../../../core/shared/operators';
1515
import { map, take } from 'rxjs/operators';
16-
import { BitstreamDataService } from '../../../../../../../core/data/bitstream-data.service';
17-
import { MetadataFilter } from '../../../../../../../core/data/bitstream-data.service';
16+
import { BitstreamDataService, MetadataFilter } from '../../../../../../../core/data/bitstream-data.service';
1817
import { RemoteData } from '../../../../../../../core/data/remote-data';
1918
import { PaginatedList } from '../../../../../../../core/data/paginated-list.model';
2019
import { Observable } from 'rxjs';
20+
import { inject } from '@angular/core';
2121

2222
@Component({
2323
selector: 'ds-metadata-container',
@@ -46,7 +46,7 @@ export class MetadataContainerComponent implements OnInit {
4646
/**
4747
* The prefix used for box field label's i18n key
4848
*/
49-
fieldI18nPrefix = 'layout.field.label.';
49+
readonly fieldI18nPrefix = 'layout.field.label';
5050

5151
/**
5252
* A boolean representing if metadata rendering type is structured or not
@@ -58,12 +58,9 @@ export class MetadataContainerComponent implements OnInit {
5858
*/
5959
metadataFieldRenderOptions: MetadataBoxFieldRenderOptions;
6060

61-
constructor(
62-
protected bitstreamDataService: BitstreamDataService,
63-
protected translateService: TranslateService,
64-
protected cd: ChangeDetectorRef
65-
) {
66-
}
61+
protected readonly bitstreamDataService = inject(BitstreamDataService);
62+
protected readonly translateService = inject(TranslateService);
63+
protected readonly cd = inject(ChangeDetectorRef);
6764

6865
/**
6966
* Returns all metadata values in the item
@@ -82,17 +79,29 @@ export class MetadataContainerComponent implements OnInit {
8279
/**
8380
* Returns a string representing the label of field if exists
8481
*/
85-
get label(): string {
86-
const fieldLabelI18nKey = this.fieldI18nPrefix + this.item.entityType + '.' + this.field.metadata;
87-
const header: string = this.translateService.instant(fieldLabelI18nKey);
88-
if (header === fieldLabelI18nKey) {
89-
// if translation does not exist return the value present in the header property
90-
return this.translateService.instant(this.field.label);
82+
getLabel(): string {
83+
if (this.field.fieldType === LayoutFieldType.BITSTREAM) {
84+
return (hasValue(this.field.bitstream.metadataValue) ?
85+
this.getTranslationIfExists(`${this.fieldI18nPrefix}.${this.item.entityType}.BITSTREAM[${this.field.bitstream.metadataValue}]`) :
86+
this.getTranslationIfExists(`${this.fieldI18nPrefix}.${this.item.entityType}.BITSTREAM`)
87+
) ?? this.field.label;
9188
} else {
92-
return header;
89+
return this.getTranslationIfExists(`${this.fieldI18nPrefix}.${this.item.entityType}.[${this.field.metadata}]`) ??
90+
this.getTranslationIfExists(`${this.fieldI18nPrefix}.${this.item.entityType}.${this.field.metadata}`) ?? // old syntax - do not use
91+
this.getTranslationIfExists(`${this.fieldI18nPrefix}.[${this.field.metadata}]`) ??
92+
this.getTranslationIfExists(`${this.fieldI18nPrefix}.${this.field.label}`) ?? // old syntax - do not use
93+
this.field.label; // the untranslated value from the CRIS layout
9394
}
9495
}
9596

97+
/**
98+
* Return the translated label, if exists, otherwise returns null
99+
*/
100+
getTranslationIfExists(key: string): string {
101+
const translation: string = this.translateService.instant(key);
102+
return translation !== key ? translation : null;
103+
}
104+
96105
/**
97106
* Returns a string representing the style of field container if exists
98107
*/
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<div class="thumbnail" [class.limit-width]="limitWidth" *ngVar="(isLoading$ | async) as isLoading">
2+
<div *ngIf="isLoading" class="thumbnail-content outer">
3+
<div class="inner">
4+
<div class="centered">
5+
<ds-themed-loading [spinner]="true"></ds-themed-loading>
6+
</div>
7+
</div>
8+
</div>
9+
<ng-container *ngVar="(src$ | async) as src">
10+
<!-- don't use *ngIf="!isLoading" so the thumbnail can load in while the animation is playing -->
11+
<img *ngIf="src !== null" class="thumbnail-content img-fluid" [ngClass]="{'d-none': isLoading}"
12+
[src]="src | dsSafeUrl" [alt]="alt | translate" (error)="errorHandler()" (load)="successHandler()">
13+
<div *ngIf="src === null && !isLoading" class="thumbnail-content outer" #thumbnailBox>
14+
<img [src]="fallbackImage" [alt]="placeholder | translate">
15+
</div>
16+
</ng-container>
17+
</div>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
:host{
2+
img {
3+
height: 80px;
4+
width: 80px;
5+
border: 1px solid #ccc;
6+
border-radius: 50%;
7+
object-fit: cover;
8+
}
9+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* tslint:disable:no-unused-variable */
2+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3+
4+
import { MetadataLinkViewAvatarPopoverComponent } from './metadata-link-view-avatar-popover.component';
5+
import { of as observableOf } from 'rxjs';
6+
import { AuthService } from '../../../core/auth/auth.service';
7+
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
8+
import { FileService } from '../../../core/shared/file.service';
9+
10+
describe('MetadataLinkViewAvatarPopoverComponent', () => {
11+
let component: MetadataLinkViewAvatarPopoverComponent;
12+
let fixture: ComponentFixture<MetadataLinkViewAvatarPopoverComponent>;
13+
let authService;
14+
let authorizationService;
15+
let fileService;
16+
17+
beforeEach(async(() => {
18+
authService = jasmine.createSpyObj('AuthService', {
19+
isAuthenticated: observableOf(true),
20+
});
21+
authorizationService = jasmine.createSpyObj('AuthorizationService', {
22+
isAuthorized: observableOf(true),
23+
});
24+
fileService = jasmine.createSpyObj('FileService', {
25+
retrieveFileDownloadLink: null
26+
});
27+
TestBed.configureTestingModule({
28+
declarations: [ MetadataLinkViewAvatarPopoverComponent ],
29+
providers: [
30+
{ provide: AuthService, useValue: authService },
31+
{ provide: AuthorizationDataService, useValue: authorizationService },
32+
{ provide: FileService, useValue: fileService }
33+
]
34+
})
35+
.compileComponents();
36+
}));
37+
38+
beforeEach(() => {
39+
fixture = TestBed.createComponent(MetadataLinkViewAvatarPopoverComponent);
40+
component = fixture.componentInstance;
41+
fixture.detectChanges();
42+
});
43+
44+
it('should create', () => {
45+
expect(component).toBeTruthy();
46+
});
47+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Component } from '@angular/core';
2+
import { ThumbnailComponent } from 'src/app/thumbnail/thumbnail.component';
3+
4+
@Component({
5+
selector: 'ds-metadata-link-view-avatar-popover',
6+
templateUrl: './metadata-link-view-avatar-popover.component.html',
7+
styleUrls: ['./metadata-link-view-avatar-popover.component.scss']
8+
})
9+
export class MetadataLinkViewAvatarPopoverComponent extends ThumbnailComponent {
10+
11+
/**
12+
* The fallback image to use when the thumbnail is not available
13+
*/
14+
fallbackImage = 'assets/images/person-placeholder.svg';
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<span class="d-flex align-items-center txt-value">
2+
<a *ngIf="(orcidUrl$ | async); else noOrcidUrl" href="{{(orcidUrl$ | async)}}/{{metadataValue}}" target="_blank">
3+
{{ metadataValue }}
4+
</a>
5+
6+
<ng-template #noOrcidUrl>{{ metadataValue }}</ng-template>
7+
8+
<img *ngIf="hasOrcidBadge()"
9+
placement="top"
10+
ngbTooltip="{{ 'orcid.badge.tooltip' | translate }}"
11+
class="orcid-icon"
12+
alt="orcid-logo"
13+
src="assets/images/orcid.logo.icon.svg"/>
14+
15+
</span>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.orcid-icon {
2+
height: 1.2rem;
3+
padding-left: 0.3rem;
4+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { ConfigurationDataService } from './../../../core/data/configuration-data.service';
2+
import { Item } from 'src/app/core/shared/item.model';
3+
import { TranslateLoaderMock } from './../../testing/translate-loader.mock';
4+
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
5+
import { ComponentFixture, TestBed } from '@angular/core/testing';
6+
7+
import { MetadataLinkViewOrcidComponent } from './metadata-link-view-orcid.component';
8+
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
9+
import { MetadataValue } from 'src/app/core/shared/metadata.models';
10+
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
11+
12+
describe('MetadataLinkViewOrcidComponent', () => {
13+
let component: MetadataLinkViewOrcidComponent;
14+
let fixture: ComponentFixture<MetadataLinkViewOrcidComponent>;
15+
16+
const configurationDataService = jasmine.createSpyObj('configurationDataService', {
17+
findByPropertyName: createSuccessfulRemoteDataObject$({ values: ['https://sandbox.orcid.org'] })
18+
});
19+
20+
21+
const metadataValue = Object.assign(new MetadataValue(), {
22+
'value': '0000-0001-8918-3592',
23+
'language': 'en_US',
24+
'authority': null,
25+
'confidence': -1,
26+
'place': 0
27+
});
28+
29+
const testItem = Object.assign(new Item(),
30+
{
31+
type: 'item',
32+
metadata: {
33+
'person.identifier.orcid': [metadataValue],
34+
'dspace.orcid.authenticated': [
35+
{
36+
language: null,
37+
value: 'authenticated'
38+
}
39+
]
40+
},
41+
uuid: 'test-item-uuid',
42+
}
43+
);
44+
45+
beforeEach(async () => {
46+
await TestBed.configureTestingModule({
47+
declarations: [ MetadataLinkViewOrcidComponent ],
48+
imports: [TranslateModule.forRoot({
49+
loader: {
50+
provide: TranslateLoader,
51+
useClass: TranslateLoaderMock
52+
}
53+
}), BrowserAnimationsModule],
54+
providers: [
55+
{ provide: ConfigurationDataService, useValue: configurationDataService}
56+
],
57+
})
58+
.compileComponents();
59+
60+
fixture = TestBed.createComponent(MetadataLinkViewOrcidComponent);
61+
component = fixture.componentInstance;
62+
component.itemValue = testItem;
63+
fixture.detectChanges();
64+
});
65+
66+
it('should create', () => {
67+
expect(component).toBeTruthy();
68+
});
69+
});

0 commit comments

Comments
 (0)