Skip to content

Commit d9a6f6d

Browse files
Merge branch 'w2p-112970_added-missing-breadcrumbs_contribute-7.4' into main
2 parents 86ddd45 + 60d93e6 commit d9a6f6d

19 files changed

Lines changed: 328 additions & 35 deletions

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { BrowseByGuard } from '../browse-by/browse-by-guard';
44
import { BrowseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver';
55
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
66
import { CollectionBreadcrumbResolver } from '../core/breadcrumbs/collection-breadcrumb.resolver';
7+
import { CommunityBreadcrumbResolver } from '../core/breadcrumbs/community-breadcrumb.resolver';
78
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
89
import { ComcolBrowseByComponent } from '../shared/comcol/sections/comcol-browse-by/comcol-browse-by.component';
910
import { ComcolSearchSectionComponent } from '../shared/comcol/sections/comcol-search-section/comcol-search-section.component';
@@ -24,12 +25,28 @@ import { ItemTemplatePageResolver } from './edit-item-template-page/item-templat
2425
import { ThemedEditItemTemplatePageComponent } from './edit-item-template-page/themed-edit-item-template-page.component';
2526
import { ThemedCollectionPageComponent } from './themed-collection-page.component';
2627

27-
2828
export const ROUTES: Route[] = [
2929
{
3030
path: COLLECTION_CREATE_PATH,
31-
component: CreateCollectionPageComponent,
3231
canActivate: [AuthenticatedGuard, CreateCollectionPageGuard],
32+
children: [
33+
{
34+
path: '',
35+
component: CreateCollectionPageComponent,
36+
resolve: {
37+
breadcrumb: I18nBreadcrumbResolver,
38+
},
39+
data: {
40+
breadcrumbKey: 'collection.create',
41+
},
42+
},
43+
],
44+
data: {
45+
breadcrumbQueryParam: 'parent',
46+
},
47+
resolve: {
48+
breadcrumb: CommunityBreadcrumbResolver,
49+
},
3350
},
3451
{
3552
path: ':id',

src/app/community-page/community-page-routes.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,25 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component'
2525
export const ROUTES: Route[] = [
2626
{
2727
path: COMMUNITY_CREATE_PATH,
28-
component: CreateCommunityPageComponent,
28+
children: [
29+
{
30+
path: '',
31+
component: CreateCommunityPageComponent,
32+
resolve: {
33+
breadcrumb: I18nBreadcrumbResolver,
34+
},
35+
data: {
36+
breadcrumbKey: 'community.create',
37+
},
38+
},
39+
],
2940
canActivate: [AuthenticatedGuard, CreateCommunityPageGuard],
41+
data: {
42+
breadcrumbQueryParam: 'parent',
43+
},
44+
resolve: {
45+
breadcrumb: CommunityBreadcrumbResolver,
46+
},
3047
},
3148
{
3249
path: ':id',

src/app/core/breadcrumbs/community-breadcrumb.resolver.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import { Injectable } from '@angular/core';
2+
import {
3+
ActivatedRouteSnapshot,
4+
RouterStateSnapshot,
5+
} from '@angular/router';
6+
import { Observable } from 'rxjs';
27

8+
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
39
import { COMMUNITY_PAGE_LINKS_TO_FOLLOW } from '../../community-page/community-page.resolver';
10+
import { hasValue } from '../../shared/empty.util';
411
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
512
import { CommunityDataService } from '../data/community-data.service';
613
import { Community } from '../shared/community.model';
@@ -18,6 +25,23 @@ export class CommunityBreadcrumbResolver extends DSOBreadcrumbResolver<Community
1825
super(breadcrumbService, dataService);
1926
}
2027

28+
/**
29+
* Method to retrieve the breadcrumb config by the route id. It is also possible to retrieve the id through the
30+
* query parameters. This is done by defining the name of the query parameter in the data section under the property
31+
* breadcrumbQueryParam.
32+
*
33+
* @param route The current {@link ActivatedRouteSnapshot}
34+
* @param state The current {@link RouterStateSnapshot}
35+
* @returns BreadcrumbConfig object
36+
*/
37+
override resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<BreadcrumbConfig<Community>> {
38+
if (hasValue(route.data.breadcrumbQueryParam) && hasValue(route.queryParams[route.data.breadcrumbQueryParam])) {
39+
return this.resolveById(route.queryParams[route.data.breadcrumbQueryParam]);
40+
} else {
41+
return super.resolve(route, state);
42+
}
43+
}
44+
2145
/**
2246
* Method that returns the follow links to already resolve
2347
* The self links defined in this list are expected to be requested somewhere in the near future

src/app/core/breadcrumbs/dso-breadcrumb.resolver.spec.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ describe('DSOBreadcrumbResolver', () => {
1919
uuid = '1234-65487-12354-1235';
2020
breadcrumbUrl = '/collections/' + uuid;
2121
currentUrl = breadcrumbUrl + '/edit';
22-
testCollection = Object.assign(new Collection(), { uuid });
22+
testCollection = Object.assign(new Collection(), {
23+
uuid: uuid,
24+
type: 'collection',
25+
});
2326
dsoBreadcrumbService = {};
2427
collectionService = {
2528
findById: (id: string) => createSuccessfulRemoteDataObject$(testCollection),

src/app/core/breadcrumbs/dso-breadcrumb.resolver.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
import { Observable } from 'rxjs';
88
import { map } from 'rxjs/operators';
99

10+
import { getDSORoute } from '../../app-routing-paths';
1011
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
1112
import { hasValue } from '../../shared/empty.util';
1213
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
@@ -39,15 +40,22 @@ export abstract class DSOBreadcrumbResolver<T extends ChildHALResource & DSpaceO
3940
* @returns BreadcrumbConfig object
4041
*/
4142
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<BreadcrumbConfig<T>> {
42-
const uuid = route.params.id;
43+
return this.resolveById(route.params.id);
44+
}
45+
46+
/**
47+
* Method for resolving a breadcrumb by id
48+
*
49+
* @param uuid The uuid to resolve
50+
* @returns BreadcrumbConfig object
51+
*/
52+
resolveById(uuid: string): Observable<BreadcrumbConfig<T>> {
4353
return this.dataService.findById(uuid, true, false, ...this.followLinks).pipe(
4454
getFirstCompletedRemoteData(),
4555
getRemoteDataPayload(),
4656
map((object: T) => {
4757
if (hasValue(object)) {
48-
const fullPath = state.url;
49-
const url = fullPath.substr(0, fullPath.indexOf(uuid)) + uuid;
50-
return { provider: this.breadcrumbService, key: object, url: url };
58+
return { provider: this.breadcrumbService, key: object, url: getDSORoute(object) };
5159
} else {
5260
return undefined;
5361
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {
2+
followLink,
3+
FollowLinkConfig,
4+
} from '../../../shared/utils/follow-link-config.model';
5+
import { WorkflowItem } from '../models/workflowitem.model';
6+
import { WorkspaceItem } from '../models/workspaceitem.model';
7+
8+
/**
9+
* The self links defined in this list are expected to be requested somewhere in the near future
10+
* Requesting them as embeds will limit the number of requests
11+
*
12+
* Needs to be in a separate file to prevent circular dependencies in webpack.
13+
*/
14+
export const SUBMISSION_LINKS_TO_FOLLOW: FollowLinkConfig<WorkflowItem | WorkspaceItem>[] = [
15+
followLink('item'),
16+
followLink('collection'),
17+
];

src/app/core/submission/resolver/submission-object.resolver.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,36 @@ import {
44
Resolve,
55
RouterStateSnapshot,
66
} from '@angular/router';
7-
import { Store } from '@ngrx/store';
87
import { Observable } from 'rxjs';
98
import { switchMap } from 'rxjs/operators';
109

11-
import { followLink } from '../../../shared/utils/follow-link-config.model';
1210
import { IdentifiableDataService } from '../../data/base/identifiable-data.service';
1311
import { RemoteData } from '../../data/remote-data';
1412
import { getFirstCompletedRemoteData } from '../../shared/operators';
13+
import { SUBMISSION_LINKS_TO_FOLLOW } from './submission-links-to-follow';
1514

1615
/**
1716
* This class represents a resolver that requests a specific item before the route is activated
1817
*/
1918
@Injectable({ providedIn: 'root' })
2019
export class SubmissionObjectResolver<T> implements Resolve<RemoteData<T>> {
2120
constructor(
22-
protected dataService: IdentifiableDataService<any>,
23-
protected store: Store<any>,
21+
protected dataService: IdentifiableDataService<any>,
2422
) {
2523
}
2624

2725
/**
28-
* Method for resolving an item based on the parameters in the current route
29-
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
30-
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
31-
* @returns Observable<<RemoteData<Item>> Emits the found item based on the parameters in the current route,
32-
* or an error if something went wrong
33-
*/
26+
* Method for resolving an item based on the parameters in the current route
27+
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
28+
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
29+
* @returns Observable<<RemoteData<Item>> Emits the found item based on the parameters in the current route,
30+
* or an error if something went wrong
31+
*/
3432
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<RemoteData<T>> {
3533
const itemRD$ = this.dataService.findById(route.params.id,
3634
true,
3735
false,
38-
followLink('item'),
36+
...SUBMISSION_LINKS_TO_FOLLOW,
3937
).pipe(
4038
getFirstCompletedRemoteData(),
4139
switchMap((wfiRD: RemoteData<any>) => wfiRD.payload.item as Observable<RemoteData<T>>),
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {
2+
ActivatedRouteSnapshot,
3+
Resolve,
4+
RouterStateSnapshot,
5+
} from '@angular/router';
6+
import { Observable } from 'rxjs';
7+
import { map } from 'rxjs/operators';
8+
9+
import { BreadcrumbConfig } from '../../../breadcrumbs/breadcrumb/breadcrumb-config.model';
10+
import { IdentifiableDataService } from '../../data/base/identifiable-data.service';
11+
import {
12+
getFirstCompletedRemoteData,
13+
getRemoteDataPayload,
14+
} from '../../shared/operators';
15+
import { SubmissionObject } from '../models/submission-object.model';
16+
import { SubmissionParentBreadcrumbsService } from '../submission-parent-breadcrumb.service';
17+
import { SUBMISSION_LINKS_TO_FOLLOW } from './submission-links-to-follow';
18+
19+
/**
20+
* This class represents a resolver that requests a specific item before the route is activated
21+
*/
22+
export abstract class SubmissionParentBreadcrumbResolver implements Resolve<BreadcrumbConfig<SubmissionObject>> {
23+
24+
protected constructor(
25+
protected dataService: IdentifiableDataService<any>,
26+
protected breadcrumbService: SubmissionParentBreadcrumbsService,
27+
) {
28+
}
29+
30+
/**
31+
* Method for resolving an item based on the parameters in the current route
32+
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
33+
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
34+
* @returns Observable<<RemoteData<Item>> Emits the found item based on the parameters in the current route,
35+
* or an error if something went wrong
36+
*/
37+
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<BreadcrumbConfig<SubmissionObject>> {
38+
return this.dataService.findById(route.params.id,
39+
true,
40+
false,
41+
...SUBMISSION_LINKS_TO_FOLLOW,
42+
).pipe(
43+
getFirstCompletedRemoteData(),
44+
getRemoteDataPayload(),
45+
map((submissionObject: SubmissionObject) => ({
46+
provider: this.breadcrumbService,
47+
key: submissionObject,
48+
} as BreadcrumbConfig<SubmissionObject>)),
49+
);
50+
}
51+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Injectable } from '@angular/core';
2+
import {
3+
combineLatest,
4+
Observable,
5+
of as observableOf,
6+
switchMap,
7+
} from 'rxjs';
8+
9+
import { getDSORoute } from '../../app-routing-paths';
10+
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
11+
import { hasValue } from '../../shared/empty.util';
12+
import { SubmissionService } from '../../submission/submission.service';
13+
import { BreadcrumbsProviderService } from '../breadcrumbs/breadcrumbsProviderService';
14+
import { DSOBreadcrumbsService } from '../breadcrumbs/dso-breadcrumbs.service';
15+
import { DSONameService } from '../breadcrumbs/dso-name.service';
16+
import { CollectionDataService } from '../data/collection-data.service';
17+
import { RemoteData } from '../data/remote-data';
18+
import { Collection } from '../shared/collection.model';
19+
import {
20+
getFirstCompletedRemoteData,
21+
getRemoteDataPayload,
22+
} from '../shared/operators';
23+
import { SubmissionObject } from './models/submission-object.model';
24+
25+
/**
26+
* Service to calculate the parent {@link DSpaceObject} breadcrumbs for a {@link SubmissionObject}
27+
*/
28+
@Injectable({
29+
providedIn: 'root',
30+
})
31+
export class SubmissionParentBreadcrumbsService implements BreadcrumbsProviderService<SubmissionObject> {
32+
33+
constructor(
34+
protected dsoNameService: DSONameService,
35+
protected dsoBreadcrumbsService: DSOBreadcrumbsService,
36+
protected submissionService: SubmissionService,
37+
protected collectionService: CollectionDataService,
38+
) {
39+
}
40+
41+
/**
42+
* Creates the parent breadcrumb structure for {@link SubmissionObject}s. It also automatically recreates the
43+
* parent breadcrumb structure when you change a {@link SubmissionObject}'s by dispatching a
44+
* {@link ChangeSubmissionCollectionAction}.
45+
*
46+
* @param submissionObject The {@link SubmissionObject} for which the parent breadcrumb structure needs to be created
47+
*/
48+
getBreadcrumbs(submissionObject: SubmissionObject): Observable<Breadcrumb[]> {
49+
return combineLatest([
50+
(submissionObject.collection as Observable<RemoteData<Collection>>).pipe(
51+
getFirstCompletedRemoteData(),
52+
getRemoteDataPayload(),
53+
),
54+
this.submissionService.getSubmissionCollectionId(submissionObject.id),
55+
]).pipe(
56+
switchMap(([collection, latestCollectionId]: [Collection, string]) => {
57+
if (hasValue(latestCollectionId)) {
58+
return this.collectionService.findById(latestCollectionId).pipe(
59+
getFirstCompletedRemoteData(),
60+
getRemoteDataPayload(),
61+
);
62+
} else {
63+
return observableOf(collection);
64+
}
65+
}),
66+
switchMap((collection: Collection) => this.dsoBreadcrumbsService.getBreadcrumbs(collection, getDSORoute(collection))),
67+
);
68+
}
69+
70+
}

src/app/submission/submission.service.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { HttpHeaders } from '@angular/common/http';
22
import { Injectable } from '@angular/core';
33
import { Router } from '@angular/router';
4-
import { Store } from '@ngrx/store';
4+
import {
5+
createSelector,
6+
MemoizedSelector,
7+
select,
8+
Store,
9+
} from '@ngrx/store';
510
import { TranslateService } from '@ngx-translate/core';
611
import {
712
Observable,
@@ -71,6 +76,20 @@ import {
7176
SubmissionState,
7277
} from './submission.reducers';
7378

79+
function getSubmissionSelector(submissionId: string): MemoizedSelector<SubmissionState, SubmissionObjectEntry> {
80+
return createSelector(
81+
submissionSelector,
82+
(state: SubmissionState) => state.objects[submissionId],
83+
);
84+
}
85+
86+
function getSubmissionCollectionIdSelector(submissionId: string): MemoizedSelector<SubmissionState, string> {
87+
return createSelector(
88+
getSubmissionSelector(submissionId),
89+
(submission: SubmissionObjectEntry) => submission?.collection,
90+
);
91+
}
92+
7493
/**
7594
* A service that provides methods used in submission process.
7695
*/
@@ -120,10 +139,19 @@ export class SubmissionService {
120139
* @param collectionId
121140
* The collection id
122141
*/
123-
changeSubmissionCollection(submissionId, collectionId) {
142+
changeSubmissionCollection(submissionId: string, collectionId: string): void {
124143
this.store.dispatch(new ChangeSubmissionCollectionAction(submissionId, collectionId));
125144
}
126145

146+
/**
147+
* Listen to collection changes for a certain {@link SubmissionObject}
148+
*
149+
* @param submissionId The submission id
150+
*/
151+
getSubmissionCollectionId(submissionId: string): Observable<string> {
152+
return this.store.pipe(select(getSubmissionCollectionIdSelector(submissionId)));
153+
}
154+
127155
/**
128156
* Perform a REST call to create a new workspaceitem and return response
129157
*

0 commit comments

Comments
 (0)