Skip to content

Commit ae585c3

Browse files
authored
Merge pull request #4881 from atmire/backport-4737-to-dspace-7_x
[Port dspace-7_x] Escape html tags in innerHTML #4737
2 parents 2fe4d40 + ac1a6e5 commit ae585c3

File tree

22 files changed

+235
-184
lines changed

22 files changed

+235
-184
lines changed

src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export class CollectionItemMapperComponent implements OnInit {
120120

121121
this.collectionName$ = this.collectionRD$.pipe(
122122
map((rd: RemoteData<Collection>) => {
123-
return this.dsoNameService.getName(rd.payload);
123+
return this.dsoNameService.getName(rd.payload, true);
124124
})
125125
);
126126
this.searchOptions$ = this.searchConfigService.paginatedSearchOptions;

src/app/core/breadcrumbs/dso-name.service.spec.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ describe(`DSONameService`, () => {
7878

7979
const result = service.getName(mockPerson);
8080

81-
expect((service as any).factories.Person).toHaveBeenCalledWith(mockPerson);
81+
expect((service as any).factories.Person).toHaveBeenCalledWith(mockPerson, undefined);
8282
expect(result).toBe('Bingo!');
8383
});
8484

@@ -87,7 +87,7 @@ describe(`DSONameService`, () => {
8787

8888
const result = service.getName(mockOrgUnit);
8989

90-
expect((service as any).factories.OrgUnit).toHaveBeenCalledWith(mockOrgUnit);
90+
expect((service as any).factories.OrgUnit).toHaveBeenCalledWith(mockOrgUnit, undefined);
9191
expect(result).toBe('Bingo!');
9292
});
9393

@@ -96,7 +96,7 @@ describe(`DSONameService`, () => {
9696

9797
const result = service.getName(mockEPerson);
9898

99-
expect((service as any).factories.EPerson).toHaveBeenCalledWith(mockEPerson);
99+
expect((service as any).factories.EPerson).toHaveBeenCalledWith(mockEPerson, undefined);
100100
expect(result).toBe('Bingo!');
101101
});
102102

@@ -105,7 +105,7 @@ describe(`DSONameService`, () => {
105105

106106
const result = service.getName(mockDSO);
107107

108-
expect((service as any).factories.Default).toHaveBeenCalledWith(mockDSO);
108+
expect((service as any).factories.Default).toHaveBeenCalledWith(mockDSO, undefined);
109109
expect(result).toBe('Bingo!');
110110
});
111111
});
@@ -119,9 +119,9 @@ describe(`DSONameService`, () => {
119119
it(`should return 'person.familyName, person.givenName'`, () => {
120120
const result = (service as any).factories.Person(mockPerson);
121121
expect(result).toBe(mockPersonName);
122-
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName');
123-
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName');
124-
expect(mockPerson.firstMetadataValue).not.toHaveBeenCalledWith('dc.title');
122+
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName', undefined, undefined);
123+
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName', undefined, undefined);
124+
expect(mockPerson.firstMetadataValue).not.toHaveBeenCalledWith('dc.title', undefined, undefined);
125125
});
126126
});
127127

@@ -133,9 +133,9 @@ describe(`DSONameService`, () => {
133133
it(`should return dc.title`, () => {
134134
const result = (service as any).factories.Person(mockPerson);
135135
expect(result).toBe(mockPersonName);
136-
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName');
137-
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName');
138-
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('dc.title');
136+
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName', undefined, undefined);
137+
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName', undefined, undefined);
138+
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('dc.title', undefined, undefined);
139139
});
140140
});
141141
});
@@ -149,8 +149,8 @@ describe(`DSONameService`, () => {
149149
it(`should return 'eperson.firstname' and 'eperson.lastname'`, () => {
150150
const result = (service as any).factories.EPerson(mockEPerson);
151151
expect(result).toBe(mockEPersonName);
152-
expect(mockEPerson.firstMetadataValue).toHaveBeenCalledWith('eperson.firstname');
153-
expect(mockEPerson.firstMetadataValue).toHaveBeenCalledWith('eperson.lastname');
152+
expect(mockEPerson.firstMetadataValue).toHaveBeenCalledWith('eperson.firstname', undefined, undefined);
153+
expect(mockEPerson.firstMetadataValue).toHaveBeenCalledWith('eperson.lastname', undefined, undefined);
154154
});
155155
});
156156

@@ -162,8 +162,8 @@ describe(`DSONameService`, () => {
162162
it(`should return 'eperson.firstname'`, () => {
163163
const result = (service as any).factories.EPerson(mockEPersonFirst);
164164
expect(result).toBe(mockEPersonNameFirst);
165-
expect(mockEPersonFirst.firstMetadataValue).toHaveBeenCalledWith('eperson.firstname');
166-
expect(mockEPersonFirst.firstMetadataValue).toHaveBeenCalledWith('eperson.lastname');
165+
expect(mockEPersonFirst.firstMetadataValue).toHaveBeenCalledWith('eperson.firstname', undefined, undefined);
166+
expect(mockEPersonFirst.firstMetadataValue).toHaveBeenCalledWith('eperson.lastname', undefined, undefined);
167167
});
168168
});
169169
});
@@ -177,7 +177,7 @@ describe(`DSONameService`, () => {
177177
it(`should return 'organization.legalName'`, () => {
178178
const result = (service as any).factories.OrgUnit(mockOrgUnit);
179179
expect(result).toBe(mockOrgUnitName);
180-
expect(mockOrgUnit.firstMetadataValue).toHaveBeenCalledWith('organization.legalName');
180+
expect(mockOrgUnit.firstMetadataValue).toHaveBeenCalledWith('organization.legalName', undefined, undefined);
181181
});
182182
});
183183

@@ -189,7 +189,7 @@ describe(`DSONameService`, () => {
189189
it(`should return 'dc.title'`, () => {
190190
const result = (service as any).factories.Default(mockDSO);
191191
expect(result).toBe(mockDSOName);
192-
expect(mockDSO.firstMetadataValue).toHaveBeenCalledWith('dc.title');
192+
expect(mockDSO.firstMetadataValue).toHaveBeenCalledWith('dc.title', undefined, undefined);
193193
});
194194
});
195195
});

src/app/core/breadcrumbs/dso-name.service.ts

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ export class DSONameService {
2727
* With only two exceptions those solutions seem overkill for now.
2828
*/
2929
private readonly factories = {
30-
EPerson: (dso: DSpaceObject): string => {
31-
const firstName = dso.firstMetadataValue('eperson.firstname');
32-
const lastName = dso.firstMetadataValue('eperson.lastname');
30+
EPerson: (dso: DSpaceObject, escapeHTML?: boolean): string => {
31+
const firstName = dso.firstMetadataValue('eperson.firstname', undefined, escapeHTML);
32+
const lastName = dso.firstMetadataValue('eperson.lastname', undefined, escapeHTML);
3333
if (isEmpty(firstName) && isEmpty(lastName)) {
3434
return this.translateService.instant('dso.name.unnamed');
3535
} else if (isEmpty(firstName) || isEmpty(lastName)) {
@@ -38,32 +38,33 @@ export class DSONameService {
3838
return `${firstName} ${lastName}`;
3939
}
4040
},
41-
Person: (dso: DSpaceObject): string => {
42-
const familyName = dso.firstMetadataValue('person.familyName');
43-
const givenName = dso.firstMetadataValue('person.givenName');
41+
Person: (dso: DSpaceObject, escapeHTML?: boolean): string => {
42+
const familyName = dso.firstMetadataValue('person.familyName', undefined, escapeHTML);
43+
const givenName = dso.firstMetadataValue('person.givenName', undefined, escapeHTML);
4444
if (isEmpty(familyName) && isEmpty(givenName)) {
45-
return dso.firstMetadataValue('dc.title') || this.translateService.instant('dso.name.unnamed');
45+
return dso.firstMetadataValue('dc.title', undefined, escapeHTML) || this.translateService.instant('dso.name.unnamed');
4646
} else if (isEmpty(familyName) || isEmpty(givenName)) {
4747
return familyName || givenName;
4848
} else {
4949
return `${familyName}, ${givenName}`;
5050
}
5151
},
52-
OrgUnit: (dso: DSpaceObject): string => {
53-
return dso.firstMetadataValue('organization.legalName') || this.translateService.instant('dso.name.untitled');
52+
OrgUnit: (dso: DSpaceObject, escapeHTML?: boolean): string => {
53+
return dso.firstMetadataValue('organization.legalName', undefined, escapeHTML);
5454
},
55-
Default: (dso: DSpaceObject): string => {
55+
Default: (dso: DSpaceObject, escapeHTML?: boolean): string => {
5656
// If object doesn't have dc.title metadata use name property
57-
return dso.firstMetadataValue('dc.title') || dso.name || this.translateService.instant('dso.name.untitled');
58-
}
57+
return dso.firstMetadataValue('dc.title', undefined, escapeHTML) || dso.name || this.translateService.instant('dso.name.untitled');
58+
},
5959
};
6060

6161
/**
6262
* Get the name for the given {@link DSpaceObject}
6363
*
6464
* @param dso The {@link DSpaceObject} you want a name for
65+
* @param escapeHTML Whether the HTML is used inside a `[innerHTML]` attribute
6566
*/
66-
getName(dso: DSpaceObject | undefined): string {
67+
getName(dso: DSpaceObject | undefined, escapeHTML?: boolean): string {
6768
if (dso) {
6869
const types = dso.getRenderTypes();
6970
const match = types
@@ -72,10 +73,10 @@ export class DSONameService {
7273

7374
let name;
7475
if (hasValue(match)) {
75-
name = this.factories[match](dso);
76+
name = this.factories[match](dso, escapeHTML);
7677
}
7778
if (isEmpty(name)) {
78-
name = this.factories.Default(dso);
79+
name = this.factories.Default(dso, escapeHTML);
7980
}
8081
return name;
8182
} else {
@@ -88,27 +89,28 @@ export class DSONameService {
8889
*
8990
* @param object
9091
* @param dso
92+
* @param escapeHTML Whether the HTML is used inside a `[innerHTML]` attribute
9193
*
9294
* @returns {string} html embedded hit highlight.
9395
*/
94-
getHitHighlights(object: any, dso: DSpaceObject): string {
96+
getHitHighlights(object: any, dso: DSpaceObject, escapeHTML?: boolean): string {
9597
const types = dso.getRenderTypes();
9698
const entityType = types
9799
.filter((type) => typeof type === 'string')
98100
.find((type: string) => (['Person', 'OrgUnit']).includes(type)) as string;
99101
if (entityType === 'Person') {
100-
const familyName = this.firstMetadataValue(object, dso, 'person.familyName');
101-
const givenName = this.firstMetadataValue(object, dso, 'person.givenName');
102+
const familyName = this.firstMetadataValue(object, dso, 'person.familyName', escapeHTML);
103+
const givenName = this.firstMetadataValue(object, dso, 'person.givenName', escapeHTML);
102104
if (isEmpty(familyName) && isEmpty(givenName)) {
103-
return this.firstMetadataValue(object, dso, 'dc.title') || dso.name;
105+
return this.firstMetadataValue(object, dso, 'dc.title', escapeHTML) || dso.name;
104106
} else if (isEmpty(familyName) || isEmpty(givenName)) {
105107
return familyName || givenName;
106108
}
107109
return `${familyName}, ${givenName}`;
108110
} else if (entityType === 'OrgUnit') {
109-
return this.firstMetadataValue(object, dso, 'organization.legalName') || this.translateService.instant('dso.name.untitled');
111+
return this.firstMetadataValue(object, dso, 'organization.legalName', escapeHTML);
110112
}
111-
return this.firstMetadataValue(object, dso, 'dc.title') || dso.name || this.translateService.instant('dso.name.untitled');
113+
return this.firstMetadataValue(object, dso, 'dc.title', escapeHTML) || dso.name || this.translateService.instant('dso.name.untitled');
112114
}
113115

114116
/**
@@ -117,11 +119,12 @@ export class DSONameService {
117119
* @param object
118120
* @param dso
119121
* @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
122+
* @param escapeHTML Whether the HTML is used inside a `[innerHTML]` attribute
120123
*
121124
* @returns {string} the first matching string value, or `undefined`.
122125
*/
123-
firstMetadataValue(object: any, dso: DSpaceObject, keyOrKeys: string | string[]): string {
124-
return Metadata.firstValue([object.hitHighlights, dso.metadata], keyOrKeys);
126+
firstMetadataValue(object: any, dso: DSpaceObject, keyOrKeys: string | string[], escapeHTML?: boolean): string {
127+
return Metadata.firstValue(dso.metadata, keyOrKeys, object.hitHighlights, undefined, escapeHTML);
125128
}
126129

127130
}

src/app/core/shared/dspace-object.model.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -108,60 +108,64 @@ export class DSpaceObject extends ListableObject implements CacheableObject {
108108
* Gets all matching metadata in this DSpaceObject.
109109
*
110110
* @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
111-
* @param {MetadataValueFilter} filter The value filter to use. If unspecified, no filtering will be done.
111+
* @param {MetadataValueFilter} valueFilter The value filter to use. If unspecified, no filtering will be done.
112+
* @param escapeHTML Whether the HTML is used inside a `[innerHTML]` attribute
112113
* @returns {MetadataValue[]} the matching values or an empty array.
113114
*/
114-
allMetadata(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): MetadataValue[] {
115-
return Metadata.all(this.metadata, keyOrKeys, valueFilter);
115+
allMetadata(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter, escapeHTML?: boolean): MetadataValue[] {
116+
return Metadata.all(this.metadata, keyOrKeys, undefined, valueFilter, escapeHTML);
116117
}
117118

118119
/**
119120
* Like [[allMetadata]], but only returns string values.
120121
*
121122
* @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
122-
* @param {MetadataValueFilter} filter The value filter to use. If unspecified, no filtering will be done.
123+
* @param {MetadataValueFilter} valueFilter The value filter to use. If unspecified, no filtering will be done.
124+
* @param escapeHTML Whether the HTML is used inside a `[innerHTML]` attribute
123125
* @returns {string[]} the matching string values or an empty array.
124126
*/
125-
allMetadataValues(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string[] {
126-
return Metadata.allValues(this.metadata, keyOrKeys, valueFilter);
127+
allMetadataValues(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter, escapeHTML?: boolean): string[] {
128+
return Metadata.allValues(this.metadata, keyOrKeys, undefined, valueFilter, escapeHTML);
127129
}
128130

129131
/**
130132
* Gets the first matching MetadataValue object in this DSpaceObject, or `undefined`.
131133
*
132134
* @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
133-
* @param {MetadataValueFilter} filter The value filter to use. If unspecified, no filtering will be done.
135+
* @param {MetadataValueFilter} valueFilter The value filter to use. If unspecified, no filtering will be done.
136+
* @param escapeHTML Whether the HTML is used inside a `[innerHTML]` attribute
134137
* @returns {MetadataValue} the first matching value, or `undefined`.
135138
*/
136-
firstMetadata(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): MetadataValue {
137-
return Metadata.first(this.metadata, keyOrKeys, valueFilter);
139+
firstMetadata(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter, escapeHTML?: boolean): MetadataValue {
140+
return Metadata.first(this.metadata, keyOrKeys, undefined, valueFilter, escapeHTML);
138141
}
139142

140143
/**
141144
* Like [[firstMetadata]], but only returns a string value, or `undefined`.
142145
*
143146
* @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
144147
* @param {MetadataValueFilter} valueFilter The value filter to use. If unspecified, no filtering will be done.
148+
* @param escapeHTML Whether the HTML is used inside a `[innerHTML]` attribute
145149
* @returns {string} the first matching string value, or `undefined`.
146150
*/
147-
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
148-
return Metadata.firstValue(this.metadata, keyOrKeys, valueFilter);
151+
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter, escapeHTML?: boolean): string {
152+
return Metadata.firstValue(this.metadata, keyOrKeys, undefined, valueFilter, escapeHTML);
149153
}
150154

151155
/**
152156
* Checks for a matching metadata value in this DSpaceObject.
153157
*
154158
* @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
155-
* @param {MetadataValueFilter} filter The value filter to use. If unspecified, no filtering will be done.
159+
* @param {MetadataValueFilter} valueFilter The value filter to use. If unspecified, no filtering will be done.
156160
* @returns {boolean} whether a match is found.
157161
*/
158162
hasMetadata(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): boolean {
159-
return Metadata.has(this.metadata, keyOrKeys, valueFilter);
163+
return Metadata.has(this.metadata, keyOrKeys, undefined, valueFilter);
160164
}
161165

162166
/**
163167
* Find metadata on a specific field and order all of them using their "place" property.
164-
* @param key
168+
* @param keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
165169
*/
166170
findMetadataSortedByPlace(keyOrKeys: string | string[]): MetadataValue[] {
167171
return this.allMetadata(keyOrKeys).sort((a: MetadataValue, b: MetadataValue) => {

0 commit comments

Comments
 (0)