Skip to content

Commit c23c075

Browse files
committed
Merge remote-tracking branch 'upstream/main' into issue-3170
2 parents a5721d2 + e0393ba commit c23c075

File tree

5 files changed

+139
-51
lines changed

5 files changed

+139
-51
lines changed
Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
1+
@let uri = (uri$ | async);
2+
@let name = (name$ | async);
13
@if (uri && name) {
24
<div class="item-page-field">
3-
<ds-metadata-field-wrapper [label]="'item.page.cc.license.title' | translate">
5+
<ds-metadata-field-wrapper [label]="'item.page.cc.license.title' | translate" [hideIfNoTextContent]="false">
46
<div [ngClass]="{'row': variant === 'full', 'col': variant === 'small'}">
57
<!-- 'img' tag is not rendered if any errors occurs when loading it -->
68
@if (showImage) {
79
<div [ngClass]="{'col-auto': variant === 'full', 'row': variant === 'small'}"
810
style="align-content: center;"
911
>
1012
<a [href]="uri" target="_blank" class="link-anchor dont-break-out ds-simple-metadata-link">
11-
<img (error)="showImage = false" [src]="imgSrc" [alt]="name" class="cc-image"
12-
[ngStyle]="{
13-
'width': 'var(--ds-thumbnail-max-width)',
14-
'margin-bottom': variant === 'small'? '1ch' : '0',
15-
}"
13+
<img (error)="showImage = false" [src]="imgSrc$ | async" [alt]="name" class="cc-image"
14+
[ngStyle]="{
15+
'width': 'var(--ds-thumbnail-max-width)',
16+
'margin-bottom': variant === 'small'? '1ch' : '0',
17+
}"
1618
/>
1719
</a>
1820
</div>
1921
}
2022
<!-- CC name is always displayed if the image fails to load -->
21-
<div [ngClass]="{ 'row': variant === 'small', 'col': variant === 'full' }">
22-
<span>
23-
{{ variant === 'full' && showDisclaimer ? ('item.page.cc.license.disclaimer' | translate) : '' }}
24-
@if (showName || !showImage) {
23+
@if (showName || !showImage) {
24+
<div [ngClass]="{ 'row': variant === 'small', 'col': variant === 'full' }">
25+
<span>
26+
{{ showDisclaimer ? ('item.page.cc.license.disclaimer' | translate) : '' }}
2527
<a [href]="uri" target="_blank" id="cc-name">{{ name }}</a>
26-
}
27-
</span>
28-
</div>
28+
</span>
29+
</div>
30+
}
2931
</div>
3032
</ds-metadata-field-wrapper>
3133
</div>
3234
}
35+

src/app/item-page/simple/field-components/specific-field/cc-license/item-page-cc-license-field.component.spec.ts

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ import {
1212
TranslateLoader,
1313
TranslateModule,
1414
} from '@ngx-translate/core';
15+
import { ConfigurationDataService } from 'src/app/core/data/configuration-data.service';
16+
import { ConfigurationProperty } from 'src/app/core/shared/configuration-property.model';
1517
import { Item } from 'src/app/core/shared/item.model';
1618
import {
1719
MetadataMap,
1820
MetadataValue,
1921
} from 'src/app/core/shared/metadata.models';
2022
import { createSuccessfulRemoteDataObject$ } from 'src/app/shared/remote-data.utils';
23+
import { ConfigurationDataServiceStub } from 'src/app/shared/testing/configuration-data.service.stub';
2124
import { createPaginatedList } from 'src/app/shared/testing/utils.test';
2225

2326
import { APP_CONFIG } from '../../../../../../config/app-config.interface';
@@ -57,6 +60,7 @@ const testCases: TestCase[] = [
5760
{
5861
testInstance: {
5962
metadata: { 'dc.rights.uri': undefined, 'dc.rights': undefined },
63+
componentInputs: { variant: 'small' },
6064
},
6165
expected: {
6266
render: false,
@@ -68,6 +72,7 @@ const testCases: TestCase[] = [
6872
{
6973
testInstance: {
7074
metadata: { 'dc.rights.uri': null, 'dc.rights': null },
75+
componentInputs: { variant: 'small' },
7176
},
7277
expected: {
7378
render: false,
@@ -79,6 +84,7 @@ const testCases: TestCase[] = [
7984
{
8085
testInstance: {
8186
metadata: { 'dc.rights.uri': 'https://creativecommons.org/licenses/by/4.0', 'dc.rights': null },
87+
componentInputs: { variant: 'small' },
8288
},
8389
expected: {
8490
render: false,
@@ -90,6 +96,7 @@ const testCases: TestCase[] = [
9096
{
9197
testInstance: {
9298
metadata: { 'dc.rights.uri': null, 'dc.rights': licenseNameMock },
99+
componentInputs: { variant: 'small' },
93100
},
94101
expected: {
95102
render: false,
@@ -101,23 +108,25 @@ const testCases: TestCase[] = [
101108
{
102109
testInstance: {
103110
metadata: { 'dc.rights.uri': 'https://creativecommons.org/licenses/by/4.0', 'dc.rights': licenseNameMock },
111+
componentInputs: { variant: 'small' },
104112
},
105113
expected: {
106114
render: true,
107115
showName: true,
108116
showImage: true,
109-
showDisclaimer: false,
117+
showDisclaimer: true,
110118
},
111119
},
112120
{
113121
testInstance: {
114122
metadata: { 'dc.rights.uri': 'https://creativecommons.org/', 'dc.rights': licenseNameMock },
123+
componentInputs: { variant: 'small' },
115124
},
116125
expected: {
117126
render: true,
118127
showName: true,
119128
showImage: false,
120-
showDisclaimer: false,
129+
showDisclaimer: true,
121130
},
122131
},
123132
{
@@ -135,19 +144,31 @@ const testCases: TestCase[] = [
135144
{
136145
testInstance: {
137146
metadata: { 'dc.rights.uri': 'https://creativecommons.org/', 'dc.rights': licenseNameMock },
138-
componentInputs: { showName: false },
147+
componentInputs: { variant: 'small', showName: false },
139148
},
140149
expected: {
141150
render: true,
142151
showName: true,
143152
showImage: false,
144-
showDisclaimer: false,
153+
showDisclaimer: true,
154+
},
155+
},
156+
{
157+
testInstance: {
158+
metadata: { 'dc.rights.uri': 'https://creativecommons.org/', 'dc.rights': licenseNameMock },
159+
componentInputs: { variant: 'full', showName: false },
160+
},
161+
expected: {
162+
render: true,
163+
showName: true,
164+
showImage: false,
165+
showDisclaimer: true,
145166
},
146167
},
147168
{
148169
testInstance: {
149170
metadata: { 'dc.rights.uri': 'https://creativecommons.org/licenses/by/4.0', 'dc.rights': licenseNameMock },
150-
componentInputs: { showName: false },
171+
componentInputs: { variant: 'small', showName: false },
151172
},
152173
expected: {
153174
render: true,
@@ -202,8 +223,22 @@ function configureFixture(
202223

203224
describe('ItemPageCcLicenseFieldComponent', () => {
204225
let fixture: ComponentFixture<ItemPageCcLicenseFieldComponent>;
226+
let configurationDataService = new ConfigurationDataServiceStub();
205227

206228
beforeEach(waitForAsync(() => {
229+
configurationDataService.findByPropertyName = jasmine.createSpy()
230+
.withArgs('cc.license.name').and.returnValue(createSuccessfulRemoteDataObject$({
231+
... new ConfigurationProperty(),
232+
name: 'cc.license.name',
233+
values: [ 'dc.rights' ],
234+
},
235+
))
236+
.withArgs('cc.license.uri').and.returnValue(createSuccessfulRemoteDataObject$({
237+
... new ConfigurationProperty(),
238+
name: 'cc.license.uri',
239+
values: [ 'dc.rights.uri' ],
240+
},
241+
));
207242
void TestBed.configureTestingModule({
208243
imports: [
209244
TranslateModule.forRoot({
@@ -214,7 +249,10 @@ describe('ItemPageCcLicenseFieldComponent', () => {
214249
}),
215250
ItemPageCcLicenseFieldComponent,
216251
],
217-
providers: [{ provide: APP_CONFIG, useValue: environment }],
252+
providers: [
253+
{ provide: APP_CONFIG, useValue: environment },
254+
{ provide: ConfigurationDataService, useValue: configurationDataService },
255+
],
218256
schemas: [NO_ERRORS_SCHEMA],
219257
})
220258
.overrideComponent(ItemPageCcLicenseFieldComponent, {
@@ -282,14 +320,13 @@ describe('ItemPageCcLicenseFieldComponent', () => {
282320
it('should show or not CC license disclaimer',
283321
() => {
284322
const disclaimerEl = fixture.debugElement.query(By.css('span'));
285-
if (testCase.expected.showDisclaimer) {
286-
expect(disclaimerEl).toBeTruthy();
287-
expect(disclaimerEl.nativeElement.innerHTML).toContain('item.page.cc.license.disclaimer');
288-
} else if (testCase.expected.render) {
289-
expect(disclaimerEl).toBeTruthy();
290-
expect(disclaimerEl.nativeElement.innerHTML).not.toContain('item.page.cc.license.disclaimer');
291-
} else {
292-
expect(disclaimerEl).toBeFalsy();
323+
expect(Boolean(disclaimerEl)).toBe(testCase.expected.showName);
324+
if (testCase.expected.showName) {
325+
if (testCase.expected.showDisclaimer) {
326+
expect(disclaimerEl.nativeElement.innerHTML).toContain('item.page.cc.license.disclaimer');
327+
} else {
328+
expect(disclaimerEl.nativeElement.innerHTML).not.toContain('item.page.cc.license.disclaimer');
329+
}
293330
}
294331
},
295332
);
Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
AsyncPipe,
23
NgClass,
34
NgStyle,
45
} from '@angular/common';
@@ -8,20 +9,34 @@ import {
89
OnInit,
910
} from '@angular/core';
1011
import { TranslateModule } from '@ngx-translate/core';
12+
import {
13+
map,
14+
Observable,
15+
of,
16+
} from 'rxjs';
17+
import { ConfigurationDataService } from 'src/app/core/data/configuration-data.service';
18+
import { ConfigurationProperty } from 'src/app/core/shared/configuration-property.model';
1119
import { Item } from 'src/app/core/shared/item.model';
20+
import {
21+
getFirstCompletedRemoteData,
22+
getRemoteDataPayload,
23+
} from 'src/app/core/shared/operators';
24+
import { hasValue } from 'src/app/shared/empty.util';
1225
import { MetadataFieldWrapperComponent } from 'src/app/shared/metadata-field-wrapper/metadata-field-wrapper.component';
1326
import { parseCcCode } from 'src/app/shared/utils/license.utils';
1427

28+
1529
@Component({
1630
selector: 'ds-item-page-cc-license-field',
1731
templateUrl: './item-page-cc-license-field.component.html',
1832
standalone: true,
19-
imports: [NgClass, NgStyle, TranslateModule, MetadataFieldWrapperComponent],
33+
imports: [AsyncPipe, NgClass, NgStyle, TranslateModule, MetadataFieldWrapperComponent],
2034
})
2135
/**
2236
* Displays the item's Creative Commons license image in it's simple item page
2337
*/
2438
export class ItemPageCcLicenseFieldComponent implements OnInit {
39+
2540
/**
2641
* The item to display the CC license image for
2742
*/
@@ -34,16 +49,14 @@ export class ItemPageCcLicenseFieldComponent implements OnInit {
3449
@Input() variant?: 'small' | 'full' = 'small';
3550

3651
/**
37-
* Filed name containing the CC license URI, as configured in the back-end, in the 'dspace.cfg' file, property
38-
* 'cc.license.uri'
52+
* Field name containing the CC license URI
3953
*/
40-
@Input() ccLicenseUriField? = 'dc.rights.uri';
54+
@Input() ccLicenseUriField?: string;
4155

4256
/**
43-
* Filed name containing the CC license name, as configured in the back-end, in the 'dspace.cfg' file, property
44-
* 'cc.license.name'
57+
* Field name containing the CC license name
4558
*/
46-
@Input() ccLicenseNameField? = 'dc.rights';
59+
@Input() ccLicenseNameField?: string;
4760

4861
/**
4962
* Shows the CC license name with the image. Always show if image fails to load
@@ -55,16 +68,47 @@ export class ItemPageCcLicenseFieldComponent implements OnInit {
5568
*/
5669
@Input() showDisclaimer? = true;
5770

58-
uri: string;
59-
name: string;
71+
6072
showImage = true;
61-
imgSrc: string;
6273

63-
ngOnInit() {
64-
this.uri = this.item.firstMetadataValue(this.ccLicenseUriField);
65-
this.name = this.item.firstMetadataValue(this.ccLicenseNameField);
74+
name$: Observable<string>;
75+
uri$: Observable<string>;
76+
imgSrc$: Observable<string>;
77+
78+
79+
constructor(
80+
protected configService: ConfigurationDataService,
81+
) {
82+
}
83+
84+
ngOnInit(): void {
85+
if (hasValue(this.ccLicenseNameField)) {
86+
this.name$ = of(this.item.firstMetadataValue(this.ccLicenseNameField));
87+
} else {
88+
this.name$ = this.configService.findByPropertyName('cc.license.name').pipe(
89+
getFirstCompletedRemoteData(),
90+
getRemoteDataPayload(),
91+
map((configurationProperty: ConfigurationProperty) => configurationProperty?.values?.[0]),
92+
map((metadataField: string) => hasValue(metadataField) ? metadataField : 'dc.rights'),
93+
map((metadataField: string) => this.item.firstMetadataValue(metadataField)),
94+
);
95+
}
96+
97+
if (hasValue(this.ccLicenseUriField)) {
98+
this.uri$ = of(this.item.firstMetadataValue(this.ccLicenseUriField));
99+
} else {
100+
this.uri$ = this.configService.findByPropertyName('cc.license.uri').pipe(
101+
getFirstCompletedRemoteData(),
102+
getRemoteDataPayload(),
103+
map((configurationProperty: ConfigurationProperty) => configurationProperty?.values?.[0]),
104+
map((metadataField: string) => hasValue(metadataField) ? metadataField : 'dc.rights.uri'),
105+
map((metadataField: string) => this.item.firstMetadataValue(metadataField)),
106+
);
107+
}
66108

67-
const ccCode = parseCcCode(this.uri);
68-
this.imgSrc = ccCode ? `assets/images/cc-licenses/${ccCode}.png` : null;
109+
this.imgSrc$ = this.uri$.pipe(
110+
map((uri: string) => parseCcCode(uri)),
111+
map((ccCode: string) => ccCode ? `assets/images/cc-licenses/${ccCode}.png` : null),
112+
);
69113
}
70114
}

src/app/item-page/simple/item-types/untyped-item/untyped-item.component.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ function getItem(metadata: MetadataMap) {
9090
describe('UntypedItemComponent', () => {
9191
let comp: UntypedItemComponent;
9292
let fixture: ComponentFixture<UntypedItemComponent>;
93+
let configurationDataService = new ConfigurationDataServiceStub();
9394

9495
beforeEach(waitForAsync(() => {
9596
const mockBitstreamDataService = {
@@ -133,6 +134,7 @@ describe('UntypedItemComponent', () => {
133134
{ provide: RouteService, useValue: mockRouteService },
134135
{ provide: BrowseDefinitionDataService, useValue: BrowseDefinitionDataServiceStub },
135136
{ provide: ConfigurationDataService, useValue: new ConfigurationDataServiceStub() },
137+
{ provide: ConfigurationDataService, useValue: configurationDataService },
136138
{ provide: APP_CONFIG, useValue: environment },
137139
],
138140
schemas: [NO_ERRORS_SCHEMA],
Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1+
import { hasValue } from 'src/app/shared/empty.util';
2+
13
/**
2-
* Parse a URI an return its CC code. URIs pointing to non-CC licenses will return null.
3-
* @param uri
4-
* @returns the CC code or null if uri is not a valid CC URI
5-
*/
4+
* Parse a URI an return its CC code. URIs pointing to non-CC licenses will return null.
5+
* @param uri
6+
* @returns the CC code or null if uri is not a valid CC URI
7+
*/
68
export function parseCcCode(uri: string): string {
79
const regex = /.*creativecommons.org\/(licenses|publicdomain)\/([^/]+)/gm;
810
const matches = regex.exec(uri ?? '') ?? [];
911
return matches.length > 2 ? matches[2] : null;
1012
}
1113

1214
/**
13-
* Returns whether a URI denotes a valid URI for CC licenses.
14-
* @param uri
15-
* @returns true if the URI corresponds to a reconigzable CC license URI or false otherwise
16-
*/
15+
* Returns whether a URI denotes a valid URI for CC licenses.
16+
* @param uri
17+
* @returns true if the URI corresponds to a reconigzable CC license URI or false otherwise
18+
*/
1719
export function isCcLicense(uri: string): boolean {
18-
return !!parseCcCode(uri);
20+
return hasValue(parseCcCode(uri));
1921
}

0 commit comments

Comments
 (0)