Skip to content

Commit 6caf5a6

Browse files
committed
Merged in task/main-cris/DSC-2207 (pull request DSpace#2861)
Task/main cris/DSC-2207
2 parents 24ad9b0 + 01adc9d commit 6caf5a6

File tree

82 files changed

+1704
-149
lines changed

Some content is hidden

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

82 files changed

+1704
-149
lines changed

config/config.example.yml

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -342,15 +342,6 @@ item:
342342
# The maximum number of values for repeatable metadata to show in the full item
343343
metadataLimit: 20
344344

345-
# When the search results are retrieved, for each item type the metadata with a valid authority value are inspected.
346-
# Referenced items will be fetched with a find all by id strategy to avoid individual rest requests
347-
# to efficiently display the search results.
348-
followAuthorityMetadata:
349-
- type: Publication
350-
metadata: dc.contributor.author
351-
- type: Product
352-
metadata: dc.contributor.author
353-
354345
# Community Page Config
355346
community:
356347
# Search tab config
@@ -424,10 +415,6 @@ themes:
424415
attributes:
425416
rel: manifest
426417
href: assets/dspace/images/favicons/manifest.webmanifest
427-
- tagName: link
428-
attributes:
429-
rel: stylesheet
430-
href: "https://fonts.googleapis.com/icon?family=Material+Icons"
431418

432419
# The default bundles that should always be displayed as suggestions when you upload a new bundle
433420
bundle:
@@ -615,3 +602,19 @@ addToAnyPlugin:
615602
title: DSpace CRIS 7 demo
616603
# The link to be shown in the shared post, if different from document.location.origin (optional)
617604
# link: https://dspacecris7.4science.cloud/
605+
606+
# When the search results are retrieved, for each item type the metadata with a valid authority value are inspected.
607+
# Referenced items will be fetched with a find all by id strategy to avoid individual rest requests
608+
# to efficiently display the search results.
609+
followAuthorityMetadata:
610+
- type: Publication
611+
metadata: dc.contributor.author
612+
- type: Product
613+
metadata: dc.contributor.author
614+
615+
# The maximum number of item to process when following authority metadata values.
616+
followAuthorityMaxItemLimit: 100
617+
618+
# The maximum number of metadata values to process for each metadata key
619+
# when following authority metadata values.
620+
followAuthorityMetadataValuesLimit: 5

src/app/audit-page/object-audit-overview/object-audit-overview.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ <h4 class="mt-4 mb-4">{{ object.name }} (<em>{{object.type}}</em>)</h4>
6161

6262
</ng-container>
6363

64-
<h4 class="mt-4 mb-4" *ngIf="(auditsRD$ | async).statusCode === 404">{{'audit.object.overview.disabled.message' | translate}}</h4>
64+
<h4 class="mt-4 mb-4" *ngIf="(auditsRD$ | async)?.statusCode === 404">{{'audit.object.overview.disabled.message' | translate}}</h4>
6565

6666
</ng-container>
6767

src/app/audit-page/object-audit-overview/object-audit-overview.component.ts

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,21 @@ import { TranslateModule } from '@ngx-translate/core';
1818
import {
1919
combineLatest,
2020
Observable,
21+
of,
2122
} from 'rxjs';
22-
import { mergeMap } from 'rxjs/operators';
23+
import {
24+
map,
25+
mergeMap,
26+
switchMap,
27+
take,
28+
} from 'rxjs/operators';
2329

30+
import { COLLECTION_PAGE_LINKS_TO_FOLLOW } from '../../collection-page/collection-page.resolver';
2431
import { AuditDataService } from '../../core/audit/audit-data.service';
2532
import { Audit } from '../../core/audit/model/audit.model';
2633
import { AuthService } from '../../core/auth/auth.service';
2734
import { SortDirection } from '../../core/cache/models/sort-options.model';
35+
import { CollectionDataService } from '../../core/data/collection-data.service';
2836
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
2937
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
3038
import { FindListOptions } from '../../core/data/find-list-options.model';
@@ -33,6 +41,8 @@ import { PaginatedList } from '../../core/data/paginated-list.model';
3341
import { RemoteData } from '../../core/data/remote-data';
3442
import { PaginationService } from '../../core/pagination/pagination.service';
3543
import { redirectOn4xx } from '../../core/shared/authorized.operators';
44+
import { Collection } from '../../core/shared/collection.model';
45+
import { Item } from '../../core/shared/item.model';
3646
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
3747
import { PaginationComponent } from '../../shared/pagination/pagination.component';
3848
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
@@ -61,7 +71,7 @@ export class ObjectAuditOverviewComponent implements OnInit {
6171
/**
6272
* The object extracted from the route.
6373
*/
64-
object;
74+
object: Item;
6575

6676
/**
6777
* List of all audits
@@ -92,14 +102,17 @@ export class ObjectAuditOverviewComponent implements OnInit {
92102
*/
93103
dateFormat = 'yyyy-MM-dd HH:mm:ss';
94104

105+
owningCollection$: Observable<Collection>;
106+
95107
constructor(protected authService: AuthService,
96108
protected route: ActivatedRoute,
97109
protected router: Router,
98110
protected auditService: AuditDataService,
99111
protected itemService: ItemDataService,
100112
protected authorizationService: AuthorizationDataService,
101-
protected paginationService: PaginationService) {
102-
}
113+
protected paginationService: PaginationService,
114+
protected collectionDataService: CollectionDataService,
115+
) {}
103116

104117
ngOnInit(): void {
105118
this.route.paramMap.pipe(
@@ -108,6 +121,15 @@ export class ObjectAuditOverviewComponent implements OnInit {
108121
redirectOn4xx(this.router, this.authService),
109122
).subscribe((rd) => {
110123
this.object = rd.payload;
124+
this.owningCollection$ = this.collectionDataService.findOwningCollectionFor(
125+
this.object,
126+
true,
127+
false,
128+
...COLLECTION_PAGE_LINKS_TO_FOLLOW,
129+
).pipe(
130+
getFirstCompletedRemoteData(),
131+
map(data => data?.payload),
132+
);
111133
this.setAudits();
112134
});
113135
}
@@ -118,17 +140,35 @@ export class ObjectAuditOverviewComponent implements OnInit {
118140
setAudits() {
119141
const config$ = this.paginationService.getFindListOptions(this.pageConfig.id, this.config, this.pageConfig);
120142
const isAdmin$ = this.isCurrentUserAdmin();
121-
this.auditsRD$ = combineLatest([isAdmin$, config$]).pipe(
122-
mergeMap(([isAdmin, config]) => {
143+
const parentCommunity$ = this.owningCollection$.pipe(
144+
switchMap(collection => collection.parentCommunity),
145+
getFirstCompletedRemoteData(),
146+
map(data => data?.payload),
147+
);
148+
149+
150+
this.auditsRD$ = combineLatest([isAdmin$, config$, this.owningCollection$, parentCommunity$]).pipe(
151+
mergeMap(([isAdmin, config, owningCollection, parentCommunity]) => {
123152
if (isAdmin) {
124-
return this.auditService.findByObject(this.object.id, config);
153+
return this.auditService.findByObject(this.object.id, config, owningCollection.id, parentCommunity.id);
125154
}
155+
156+
return of(null);
126157
}),
127158
);
128159
}
129160

130161
isCurrentUserAdmin(): Observable<boolean> {
131-
return this.authorizationService.isAuthorized(FeatureID.AdministratorOf, undefined, undefined);
162+
return combineLatest([
163+
this.authorizationService.isAuthorized(FeatureID.IsCollectionAdmin),
164+
this.authorizationService.isAuthorized(FeatureID.IsCommunityAdmin),
165+
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
166+
]).pipe(
167+
map(([isCollectionAdmin, isCommunityAdmin, isSiteAdmin]) => {
168+
return isCollectionAdmin || isCommunityAdmin || isSiteAdmin;
169+
}),
170+
take(1),
171+
);
132172
}
133173

134174
/**

src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div class="container">
22
<div class="d-flex justify-content-between">
3-
<h1 class="h2">{{'bitstream.download.page' | translate:{ bitstream: (fileName$ | async) } }}</h1>
3+
<span class="h2">{{'bitstream.download.page' | translate:{ bitstream: (fileName$ | async) } }}</span>
44
<div class="pt-3">
55
<button *ngIf="hasHistory" (click)="back()" class="btn btn-outline-secondary">
66
<i class="fas fa-arrow-left"></i> {{'bitstream.download.page.back' | translate}}

src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import {
3333
NativeWindowRef,
3434
NativeWindowService,
3535
} from '../../core/services/window.service';
36-
import { redirectOn4xx } from '../../core/shared/authorized.operators';
3736
import { Bitstream } from '../../core/shared/bitstream.model';
3837
import { FileService } from '../../core/shared/file.service';
3938
import { getRemoteDataPayload } from '../../core/shared/operators';
@@ -91,7 +90,6 @@ export class BitstreamDownloadPageComponent implements OnInit {
9190
);
9291

9392
this.bitstream$ = this.bitstreamRD$.pipe(
94-
redirectOn4xx(this.router, this.auth),
9593
getRemoteDataPayload(),
9694
);
9795

src/app/breadcrumbs/breadcrumbs.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
</ng-template>
1515

1616
<ng-template #activeBreadcrumb let-text="text">
17-
<li class="breadcrumb-item active" aria-current="page"><div class="breadcrumb-item-limiter"><div class="text-truncate">{{text | translate}}</div></div></li>
17+
<li class="breadcrumb-item active" aria-current="page"><div class="breadcrumb-item-limiter"><span class="text-truncate" [innerHTML]="text | translate"></span></div></li>
1818
</ng-template>
1919
</ng-container>
2020

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export class CollectionPageComponent implements OnInit {
107107
collectionPageRoute$: Observable<string>;
108108

109109
constructor(
110-
@Inject(PLATFORM_ID) private platformId: any,
110+
@Inject(PLATFORM_ID) private platformId: string,
111111
@Inject(APP_CONFIG) public appConfig: AppConfig,
112112
protected route: ActivatedRoute,
113113
protected router: Router,

src/app/core/audit/audit-data.service.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
startWith,
99
} from 'rxjs/operators';
1010

11+
import { hasValue } from '../../shared/empty.util';
1112
import { NotificationsService } from '../../shared/notifications/notifications.service';
1213
import {
1314
followLink,
@@ -67,12 +68,23 @@ export class AuditDataService extends IdentifiableDataService<Audit>{
6768
*
6869
* @param objectId The objectId id
6970
* @param options The [[FindListOptions]] object
71+
* @param collUuid The Uuid of the collection
72+
* @param commUuid The Uuid of the community
7073
* @return Observable<RemoteData<PaginatedList<Audit>>>
7174
*/
72-
findByObject(objectId: string, options: FindListOptions = {}): Observable<RemoteData<PaginatedList<Audit>>> {
75+
findByObject(objectId: string, options: FindListOptions = {}, collUuid?: string, commUuid?: string): Observable<RemoteData<PaginatedList<Audit>>> {
7376
const searchMethod = AUDIT_FIND_BY_OBJECT_SEARCH_METHOD;
77+
const searchParams = [new RequestParam('object', objectId)];
78+
79+
if (hasValue(commUuid)) {
80+
searchParams.push(new RequestParam('commUuid', commUuid));
81+
}
82+
83+
if (hasValue(collUuid)) {
84+
searchParams.push(new RequestParam('collUuid', collUuid));
85+
}
7486
const optionsWithObject = Object.assign(new FindListOptions(), options, {
75-
searchParams: [new RequestParam('object', objectId)],
87+
searchParams,
7688
});
7789
return this.searchData.searchBy(searchMethod, optionsWithObject, true, true, followLink('eperson'));
7890
}

src/app/core/browse/search-manager.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ export class SearchManager {
123123
})
124124
.filter((item) => hasValue(item));
125125

126-
const uuidList = this.extractUUID(items, environment.followAuthorityMetadata);
126+
const uuidList = this.extractUUID(items, environment.followAuthorityMetadata, environment.followAuthorityMaxItemLimit);
127+
127128
return uuidList.length > 0 ? this.itemService.findAllById(uuidList).pipe(
128129
getFirstCompletedRemoteData(),
129130
map(data => {
@@ -136,27 +137,31 @@ export class SearchManager {
136137
) : of(null);
137138
}
138139

139-
protected extractUUID(items: Item[], metadataToFollow: FollowAuthorityMetadata[]): string[] {
140+
protected extractUUID(items: Item[], metadataToFollow: FollowAuthorityMetadata[], numberOfElementsToReturn?: number): string[] {
140141
const uuidMap = {};
141142

142143
items.forEach((item) => {
143144
metadataToFollow.forEach((followMetadata: FollowAuthorityMetadata) => {
144145
if (item.entityType === followMetadata.type) {
145146
if (isArray(followMetadata.metadata)) {
146147
followMetadata.metadata.forEach((metadata) => {
147-
Metadata.all(item.metadata, metadata)
148+
Metadata.all(item.metadata, metadata, null, environment.followAuthorityMetadataValuesLimit)
148149
.filter((metadataValue: MetadataValue) => Metadata.hasValidItemAuthority(metadataValue.authority))
149150
.forEach((metadataValue: MetadataValue) => uuidMap[metadataValue.authority] = metadataValue);
150151
});
151152
} else {
152-
Metadata.all(item.metadata, followMetadata.metadata)
153+
Metadata.all(item.metadata, followMetadata.metadata, null, environment.followAuthorityMetadataValuesLimit)
153154
.filter((metadataValue: MetadataValue) => Metadata.hasValidItemAuthority(metadataValue.authority))
154155
.forEach((metadataValue: MetadataValue) => uuidMap[metadataValue.authority] = metadataValue);
155156
}
156157
}
157158
});
158159
});
159160

161+
if (hasValue(numberOfElementsToReturn) && numberOfElementsToReturn > 0) {
162+
return Object.keys(uuidMap).slice(0, numberOfElementsToReturn);
163+
}
164+
160165
return Object.keys(uuidMap);
161166
}
162167
}

src/app/core/browse/search.manager.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,11 @@ describe('SearchManager', () => {
186186
const uuidList = (service as any).extractUUID([firstPublication, firstPublication], [{ type: 'Publication', metadata: ['dc.contributor.author'] }]);
187187
expect(uuidList).toEqual([validAuthority]);
188188
});
189+
190+
it('should limit the number of extracted uuids', () => {
191+
const uuidList = (service as any).extractUUID([firstPublication, secondPublication, invalidAuthorityPublication], [{ type: 'Publication', metadata: ['dc.contributor.author'] }], 2);
192+
expect(uuidList.length).toBe(2);
193+
});
189194
});
190195

191196
});

0 commit comments

Comments
 (0)