Skip to content

Commit d4c8ad0

Browse files
Refactored code to pass down whether they are injected in the code through an innerHTML attribute or not to properly escape them
1 parent 404ccd9 commit d4c8ad0

22 files changed

Lines changed: 224 additions & 173 deletions

File tree

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: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe(`DSONameService`, () => {
5454

5555
const result = service.getName(mockPerson);
5656

57-
expect((service as any).factories.Person).toHaveBeenCalledWith(mockPerson);
57+
expect((service as any).factories.Person).toHaveBeenCalledWith(mockPerson, undefined);
5858
expect(result).toBe('Bingo!');
5959
});
6060

@@ -63,7 +63,7 @@ describe(`DSONameService`, () => {
6363

6464
const result = service.getName(mockOrgUnit);
6565

66-
expect((service as any).factories.OrgUnit).toHaveBeenCalledWith(mockOrgUnit);
66+
expect((service as any).factories.OrgUnit).toHaveBeenCalledWith(mockOrgUnit, undefined);
6767
expect(result).toBe('Bingo!');
6868
});
6969

@@ -72,7 +72,7 @@ describe(`DSONameService`, () => {
7272

7373
const result = service.getName(mockDSO);
7474

75-
expect((service as any).factories.Default).toHaveBeenCalledWith(mockDSO);
75+
expect((service as any).factories.Default).toHaveBeenCalledWith(mockDSO, undefined);
7676
expect(result).toBe('Bingo!');
7777
});
7878
});
@@ -86,9 +86,9 @@ describe(`DSONameService`, () => {
8686
it(`should return 'person.familyName, person.givenName'`, () => {
8787
const result = (service as any).factories.Person(mockPerson);
8888
expect(result).toBe(mockPersonName);
89-
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName');
90-
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName');
91-
expect(mockPerson.firstMetadataValue).not.toHaveBeenCalledWith('dc.title');
89+
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName', undefined, undefined);
90+
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName', undefined, undefined);
91+
expect(mockPerson.firstMetadataValue).not.toHaveBeenCalledWith('dc.title', undefined, undefined);
9292
});
9393
});
9494

@@ -100,9 +100,9 @@ describe(`DSONameService`, () => {
100100
it(`should return dc.title`, () => {
101101
const result = (service as any).factories.Person(mockPerson);
102102
expect(result).toBe(mockPersonName);
103-
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName');
104-
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName');
105-
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('dc.title');
103+
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName', undefined, undefined);
104+
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName', undefined, undefined);
105+
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('dc.title', undefined, undefined);
106106
});
107107
});
108108
});
@@ -115,7 +115,7 @@ describe(`DSONameService`, () => {
115115
it(`should return 'organization.legalName'`, () => {
116116
const result = (service as any).factories.OrgUnit(mockOrgUnit);
117117
expect(result).toBe(mockOrgUnitName);
118-
expect(mockOrgUnit.firstMetadataValue).toHaveBeenCalledWith('organization.legalName');
118+
expect(mockOrgUnit.firstMetadataValue).toHaveBeenCalledWith('organization.legalName', undefined, undefined);
119119
});
120120
});
121121

@@ -127,7 +127,7 @@ describe(`DSONameService`, () => {
127127
it(`should return 'dc.title'`, () => {
128128
const result = (service as any).factories.Default(mockDSO);
129129
expect(result).toBe(mockDSOName);
130-
expect(mockDSO.firstMetadataValue).toHaveBeenCalledWith('dc.title');
130+
expect(mockDSO.firstMetadataValue).toHaveBeenCalledWith('dc.title', undefined, undefined);
131131
});
132132
});
133133
});

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

Lines changed: 25 additions & 22 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, injectedAsHTML?: boolean): string => {
31+
const firstName = dso.firstMetadataValue('eperson.firstname', undefined, injectedAsHTML);
32+
const lastName = dso.firstMetadataValue('eperson.lastname', undefined, injectedAsHTML);
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, injectedAsHTML?: boolean): string => {
42+
const familyName = dso.firstMetadataValue('person.familyName', undefined, injectedAsHTML);
43+
const givenName = dso.firstMetadataValue('person.givenName', undefined, injectedAsHTML);
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, injectedAsHTML) || 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');
52+
OrgUnit: (dso: DSpaceObject, injectedAsHTML?: boolean): string => {
53+
return dso.firstMetadataValue('organization.legalName', undefined, injectedAsHTML);
5454
},
55-
Default: (dso: DSpaceObject): string => {
55+
Default: (dso: DSpaceObject, injectedAsHTML?: 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');
57+
return dso.firstMetadataValue('dc.title', undefined, injectedAsHTML) || dso.name || this.translateService.instant('dso.name.untitled');
5858
}
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 injectedAsHTML Whether the HTML is used inside a `[innerHTML]` attribute
6566
*/
66-
getName(dso: DSpaceObject | undefined): string {
67+
getName(dso: DSpaceObject | undefined, injectedAsHTML?: 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, injectedAsHTML);
7677
}
7778
if (isEmpty(name)) {
78-
name = this.factories.Default(dso);
79+
name = this.factories.Default(dso, injectedAsHTML);
7980
}
8081
return name;
8182
} else {
@@ -88,27 +89,28 @@ export class DSONameService {
8889
*
8990
* @param object
9091
* @param dso
92+
* @param injectedAsHTML 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, injectedAsHTML?: 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', injectedAsHTML);
103+
const givenName = this.firstMetadataValue(object, dso, 'person.givenName', injectedAsHTML);
102104
if (isEmpty(familyName) && isEmpty(givenName)) {
103-
return this.firstMetadataValue(object, dso, 'dc.title') || dso.name;
105+
return this.firstMetadataValue(object, dso, 'dc.title', injectedAsHTML) || 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');
111+
return this.firstMetadataValue(object, dso, 'organization.legalName', injectedAsHTML);
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', injectedAsHTML) || 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 injectedAsHTML 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[], injectedAsHTML?: boolean): string {
127+
return Metadata.firstValue(dso.metadata, keyOrKeys, object.hitHighlights, undefined, injectedAsHTML);
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 injectedAsHTML 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, injectedAsHTML?: boolean): MetadataValue[] {
116+
return Metadata.all(this.metadata, keyOrKeys, undefined, valueFilter, injectedAsHTML);
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 injectedAsHTML 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, injectedAsHTML?: boolean): string[] {
128+
return Metadata.allValues(this.metadata, keyOrKeys, undefined, valueFilter, injectedAsHTML);
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 injectedAsHTML 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, injectedAsHTML?: boolean): MetadataValue {
140+
return Metadata.first(this.metadata, keyOrKeys, undefined, valueFilter, injectedAsHTML);
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 injectedAsHTML 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, injectedAsHTML?: boolean): string {
152+
return Metadata.firstValue(this.metadata, keyOrKeys, undefined, valueFilter, injectedAsHTML);
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)