Skip to content

Commit ea0abcf

Browse files
[DURACOM-413] fix custom url issues with signposting, version and subPath handling
1 parent 49bd3c3 commit ea0abcf

15 files changed

Lines changed: 259 additions & 65 deletions

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ describe('ObjectAuditLogsComponent', () => {
6060
{ findOwningCollectionFor: createSuccessfulRemoteDataObject$(createPaginatedList([{ id : 'collectionId' }])) },
6161
);
6262
activatedRoute = new MockActivatedRoute({ objectId: mockItemId });
63-
activatedRoute.paramMap = of({
64-
get: () => mockItemId,
65-
});
63+
activatedRoute.data = of({ dso: {
64+
payload: mockItem,
65+
} });
6666
locationStub = jasmine.createSpyObj('location', {
6767
back: jasmine.createSpy('back'),
6868
});

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from '@angular/core';
99
import {
1010
ActivatedRoute,
11-
ParamMap,
11+
Data,
1212
Router,
1313
RouterLink,
1414
} from '@angular/router';
@@ -111,9 +111,8 @@ export class ObjectAuditLogsComponent implements OnInit {
111111
) {}
112112

113113
ngOnInit(): void {
114-
this.objectId$ = this.route.paramMap.pipe(
115-
map((paramMap: ParamMap) => paramMap.get('id')),
116-
switchMap((id: string) => this.dSpaceObjectDataService.findById(id, true, true)),
114+
this.objectId$ = this.route.data.pipe(
115+
switchMap((data: Data) => this.dSpaceObjectDataService.findById(data.dso.payload.id, true, true)),
117116
getFirstSucceededRemoteDataPayload(),
118117
tap((object) => {
119118
this.objectRoute = getDSORoute(object);

src/app/item-page/edit-item-page/item-page-delete.guard.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AuthorizationDataService } from '@dspace/core/data/feature-authorizatio
88
import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id';
99
import { ItemDataService } from '@dspace/core/data/item-data.service';
1010
import { APP_DATA_SERVICES_MAP } from '@dspace/core/data-services-map-type';
11+
import { HardRedirectService } from '@dspace/core/services/hard-redirect.service';
1112
import { Item } from '@dspace/core/shared/item.model';
1213
import { getMockTranslateService } from '@dspace/core/testing/translate.service.mock';
1314
import { createSuccessfulRemoteDataObject$ } from '@dspace/core/utilities/remote-data.utils';
@@ -72,6 +73,7 @@ describe('itemPageDeleteGuard', () => {
7273
{ provide: APP_DATA_SERVICES_MAP, useValue: {} },
7374
{ provide: TranslateService, useValue: getMockTranslateService() },
7475
{ provide: ItemDataService, useValue: itemService },
76+
{ provide: HardRedirectService, useValue: {} },
7577
],
7678
});
7779
});

src/app/item-page/edit-item-page/item-page-edit-authorizations.guard.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AuthorizationDataService } from '@dspace/core/data/feature-authorizatio
88
import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id';
99
import { ItemDataService } from '@dspace/core/data/item-data.service';
1010
import { APP_DATA_SERVICES_MAP } from '@dspace/core/data-services-map-type';
11+
import { HardRedirectService } from '@dspace/core/services/hard-redirect.service';
1112
import { Item } from '@dspace/core/shared/item.model';
1213
import { getMockTranslateService } from '@dspace/core/testing/translate.service.mock';
1314
import { createSuccessfulRemoteDataObject$ } from '@dspace/core/utilities/remote-data.utils';
@@ -72,6 +73,7 @@ describe('itemPageEditAuthorizationsGuard', () => {
7273
{ provide: APP_DATA_SERVICES_MAP, useValue: {} },
7374
{ provide: TranslateService, useValue: getMockTranslateService() },
7475
{ provide: ItemDataService, useValue: itemService },
76+
{ provide: HardRedirectService, useValue: {} },
7577
],
7678
});
7779
});

src/app/item-page/edit-item-page/item-page-move.guard.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AuthorizationDataService } from '@dspace/core/data/feature-authorizatio
88
import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id';
99
import { ItemDataService } from '@dspace/core/data/item-data.service';
1010
import { APP_DATA_SERVICES_MAP } from '@dspace/core/data-services-map-type';
11+
import { HardRedirectService } from '@dspace/core/services/hard-redirect.service';
1112
import { Item } from '@dspace/core/shared/item.model';
1213
import { getMockTranslateService } from '@dspace/core/testing/translate.service.mock';
1314
import { createSuccessfulRemoteDataObject$ } from '@dspace/core/utilities/remote-data.utils';
@@ -72,6 +73,7 @@ describe('itemPageMoveGuard', () => {
7273
{ provide: APP_DATA_SERVICES_MAP, useValue: {} },
7374
{ provide: TranslateService, useValue: getMockTranslateService() },
7475
{ provide: ItemDataService, useValue: itemService },
76+
{ provide: HardRedirectService, useValue: {} },
7577
],
7678
});
7779
});

src/app/item-page/edit-item-page/item-page-private.guard.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AuthorizationDataService } from '@dspace/core/data/feature-authorizatio
88
import { FeatureID } from '@dspace/core/data/feature-authorization/feature-id';
99
import { ItemDataService } from '@dspace/core/data/item-data.service';
1010
import { APP_DATA_SERVICES_MAP } from '@dspace/core/data-services-map-type';
11+
import { HardRedirectService } from '@dspace/core/services/hard-redirect.service';
1112
import { Item } from '@dspace/core/shared/item.model';
1213
import { getMockTranslateService } from '@dspace/core/testing/translate.service.mock';
1314
import { createSuccessfulRemoteDataObject$ } from '@dspace/core/utilities/remote-data.utils';
@@ -72,6 +73,7 @@ describe('itemPagePrivateGuard', () => {
7273
{ provide: APP_DATA_SERVICES_MAP, useValue: {} },
7374
{ provide: TranslateService, useValue: getMockTranslateService() },
7475
{ provide: ItemDataService, useValue: itemService },
76+
{ provide: HardRedirectService, useValue: {} },
7577
],
7678
});
7779
});

src/app/item-page/full/full-item-page.component.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { ItemDataService } from '@dspace/core/data/item-data.service';
1919
import { RemoteData } from '@dspace/core/data/remote-data';
2020
import { SignpostingDataService } from '@dspace/core/data/signposting-data.service';
2121
import { HeadTagService } from '@dspace/core/metadata/head-tag.service';
22+
import { HardRedirectService } from '@dspace/core/services/hard-redirect.service';
2223
import { LinkHeadService } from '@dspace/core/services/link-head.service';
2324
import { ServerResponseService } from '@dspace/core/services/server-response.service';
2425
import { Item } from '@dspace/core/shared/item.model';
@@ -101,6 +102,7 @@ describe('FullItemPageComponent', () => {
101102
beforeEach(waitForAsync(() => {
102103
routeData = {
103104
dso: createSuccessfulRemoteDataObject(mockItem),
105+
links: [mocklink, mocklink2],
104106
};
105107

106108
routeStub = Object.assign(new ActivatedRouteStub(), {
@@ -150,6 +152,7 @@ describe('FullItemPageComponent', () => {
150152
{ provide: NotifyInfoService, useValue: notifyInfoService },
151153
{ provide: PLATFORM_ID, useValue: 'server' },
152154
{ provide: ThemeService, useValue: getMockThemeService() },
155+
{ provide: HardRedirectService, useValue: {} },
153156
],
154157
schemas: [NO_ERRORS_SCHEMA],
155158
})

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

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,31 @@ import {
2121
} from './item-page-routing-paths';
2222
import { OrcidPageComponent } from './orcid-page/orcid-page.component';
2323
import { orcidPageGuard } from './orcid-page/orcid-page.guard';
24+
import { signpostingLinksResolver } from './simple/link-resolver/signposting-links.resolver';
2425
import { ThemedItemPageComponent } from './simple/themed-item-page.component';
2526
import { versionResolver } from './version-page/version.resolver';
2627
import { VersionPageComponent } from './version-page/version-page/version-page.component';
2728

2829
export const ROUTES: Route[] = [
30+
{
31+
path: 'version',
32+
children: [
33+
{
34+
path: ':id',
35+
component: VersionPageComponent,
36+
resolve: {
37+
dso: versionResolver,
38+
},
39+
},
40+
],
41+
},
2942
{
3043
path: ':id',
3144
resolve: {
3245
dso: itemPageResolver,
3346
itemRequest: accessTokenResolver,
3447
breadcrumb: itemBreadcrumbResolver,
48+
links: signpostingLinksResolver,
3549
},
3650
runGuardsAndResolvers: 'always',
3751
children: [
@@ -92,16 +106,4 @@ export const ROUTES: Route[] = [
92106
},
93107
],
94108
},
95-
{
96-
path: 'version',
97-
children: [
98-
{
99-
path: ':id',
100-
component: VersionPageComponent,
101-
resolve: {
102-
dso: versionResolver,
103-
},
104-
},
105-
],
106-
},
107109
];

src/app/item-page/item-page.resolver.spec.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import { PLATFORM_ID } from '@angular/core';
12
import { TestBed } from '@angular/core/testing';
23
import {
34
Router,
45
RouterModule,
56
} from '@angular/router';
7+
import { HardRedirectService } from '@dspace/core/services/hard-redirect.service';
68
import { DSpaceObject } from '@dspace/core/shared/dspace-object.model';
79
import { MetadataValueFilter } from '@dspace/core/shared/metadata.models';
810
import { AuthServiceStub } from '@dspace/core/testing/auth-service.stub';
@@ -14,6 +16,10 @@ import { itemPageResolver } from './item-page.resolver';
1416
describe('itemPageResolver', () => {
1517
beforeEach(() => {
1618
TestBed.configureTestingModule({
19+
providers: [
20+
{ provide: PLATFORM_ID, useValue: 'browser' },
21+
{ provide: HardRedirectService, useValue: {} },
22+
],
1723
imports: [RouterModule.forRoot([{
1824
path: 'entities/:entity-type/:id',
1925
component: {} as any,
@@ -27,13 +33,19 @@ describe('itemPageResolver', () => {
2733
let store: any;
2834
let router: Router;
2935
let authService: AuthServiceStub;
36+
let platformId: any;
37+
let hardRedirectService: any;
3038

3139
const uuid = '1234-65487-12354-1235';
3240
let item: DSpaceObject;
3341

3442
function runTestsWithEntityType(entityType: string) {
3543
beforeEach(() => {
3644
router = TestBed.inject(Router);
45+
platformId = TestBed.inject(PLATFORM_ID);
46+
hardRedirectService = jasmine.createSpyObj('hardRedirectService', {
47+
redirect: {},
48+
});
3749
item = Object.assign(new DSpaceObject(), {
3850
uuid: uuid,
3951
firstMetadataValue(_keyOrKeys: string | string[], _valueFilter?: MetadataValueFilter): string {
@@ -60,6 +72,8 @@ describe('itemPageResolver', () => {
6072
itemService,
6173
store,
6274
authService,
75+
platformId,
76+
hardRedirectService,
6377
).pipe(first())
6478
.subscribe(
6579
() => {
@@ -80,6 +94,8 @@ describe('itemPageResolver', () => {
8094
itemService,
8195
store,
8296
authService,
97+
platformId,
98+
hardRedirectService,
8399
).pipe(first())
84100
.subscribe(
85101
() => {
@@ -111,14 +127,21 @@ describe('itemPageResolver', () => {
111127
let store: any;
112128
let router: Router;
113129
let authService: AuthServiceStub;
130+
let platformId: any;
131+
let hardRedirectService: any;
114132

115133
const uuid = '1234-65487-12354-1235';
116134
let item: DSpaceObject;
117135

118136
beforeEach(() => {
119137
router = TestBed.inject(Router);
138+
platformId = TestBed.inject(PLATFORM_ID);
139+
hardRedirectService = jasmine.createSpyObj('hardRedirectService', {
140+
redirect: {},
141+
});
120142
item = Object.assign(new DSpaceObject(), {
121143
uuid: uuid,
144+
id: uuid,
122145
firstMetadataValue(_keyOrKeys: string | string[], _valueFilter?: MetadataValueFilter): string {
123146
return _keyOrKeys === 'dspace.entity.type' ? 'person' : customUrl;
124147
},
@@ -145,7 +168,7 @@ describe('itemPageResolver', () => {
145168
const route = { params: { id: uuid } } as any;
146169
const state = { url: `/entities/person/${uuid}` } as any;
147170

148-
resolver(route, state, router, itemService, store, authService)
171+
resolver(route, state, router, itemService, store, authService, platformId, hardRedirectService)
149172
.pipe(first())
150173
.subscribe((rd: any) => {
151174
const expectedUrl = `/entities/person/${customUrl}`;
@@ -160,27 +183,41 @@ describe('itemPageResolver', () => {
160183
const route = { params: { id: customUrl } } as any;
161184
const state = { url: `/entities/person/${customUrl}` } as any;
162185

163-
resolver(route, state, router, itemService, store, authService)
186+
resolver(route, state, router, itemService, store, authService, platformId, hardRedirectService)
164187
.pipe(first())
165188
.subscribe((rd: any) => {
166189
expect(router.navigateByUrl).not.toHaveBeenCalled();
167190
done();
168191
});
169192
});
170193

171-
it('should replace dspace.customurl if the current route is an administrative one', (done) => {
194+
it('should replace dspace.customurl if the current route is a sub path (/full excluded)', (done) => {
172195
spyOn(router, 'navigateByUrl').and.callThrough();
173196

174197
const route = { params: { id: customUrl } } as any;
175198
const state = { url: `/entities/person/${customUrl}/edit` } as any;
176199

177-
resolver(route, state, router, itemService, store, authService)
200+
resolver(route, state, router, itemService, store, authService, platformId, hardRedirectService)
178201
.pipe(first())
179202
.subscribe(() => {
180203
expect(router.navigateByUrl).toHaveBeenCalledWith(`/entities/person/${uuid}/edit`);
181204
done();
182205
});
183206
});
207+
208+
it('should not replace dspace.customurl if the current sub path is /full', (done) => {
209+
spyOn(router, 'navigateByUrl').and.callThrough();
210+
211+
const route = { params: { id: customUrl } } as any;
212+
const state = { url: `/entities/person/${customUrl}/full` } as any;
213+
214+
resolver(route, state, router, itemService, store, authService, platformId, hardRedirectService)
215+
.pipe(first())
216+
.subscribe(() => {
217+
expect(router.navigateByUrl).not.toHaveBeenCalled();;
218+
done();
219+
});
220+
});
184221
});
185222

186223
});

src/app/item-page/item-page.resolver.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { inject } from '@angular/core';
1+
import { isPlatformServer } from '@angular/common';
2+
import {
3+
inject,
4+
PLATFORM_ID,
5+
} from '@angular/core';
26
import {
37
ActivatedRouteSnapshot,
48
ResolveFn,
@@ -10,6 +14,7 @@ import { ItemDataService } from '@dspace/core/data/item-data.service';
1014
import { RemoteData } from '@dspace/core/data/remote-data';
1115
import { ResolvedAction } from '@dspace/core/resolving/resolver.actions';
1216
import { getItemPageRoute } from '@dspace/core/router/utils/dso-route.utils';
17+
import { HardRedirectService } from '@dspace/core/services/hard-redirect.service';
1318
import { redirectOn4xx } from '@dspace/core/shared/authorized.operators';
1419
import {
1520
getItemPageLinksToFollow,
@@ -41,6 +46,8 @@ export const itemPageResolver: ResolveFn<RemoteData<Item>> = (
4146
itemService: ItemDataService = inject(ItemDataService),
4247
store: Store<AppState> = inject(Store<AppState>),
4348
authService: AuthService = inject(AuthService),
49+
platformId: any = inject(PLATFORM_ID),
50+
hardRedirectService: HardRedirectService = inject(HardRedirectService),
4451
): Observable<RemoteData<Item>> => {
4552
const itemRD$ = itemService.findByIdOrCustomUrl(
4653
route.params.id,
@@ -59,15 +66,16 @@ export const itemPageResolver: ResolveFn<RemoteData<Item>> = (
5966
return itemRD$.pipe(
6067
map((rd: RemoteData<Item>) => {
6168
if (rd.hasSucceeded && hasValue(rd.payload)) {
62-
const isItemEditPage = state.url.includes('/edit') || state.url.includes('/bitstreams');
63-
let itemRoute = isItemEditPage ? state.url : router.parseUrl(getItemPageRoute(rd.payload)).toString();
69+
let itemRoute;
6470
if (hasValue(rd.payload.metadata) && rd.payload.hasMetadata('dspace.customurl')) {
6571
const customUrl = rd.payload.firstMetadataValue('dspace.customurl');
72+
const isSubPath = !(state.url.endsWith(customUrl) || state.url.endsWith(rd.payload.id) || state.url.endsWith('/full'));
73+
itemRoute = isSubPath ? state.url : router.parseUrl(getItemPageRoute(rd.payload)).toString();
6674
let newUrl: string;
67-
if (route.params.id !== customUrl && !isItemEditPage) {
75+
if (route.params.id !== customUrl && !isSubPath) {
6876
newUrl = itemRoute.replace(route.params.id,rd.payload.firstMetadataValue('dspace.customurl'));
69-
} else if (isItemEditPage && route.params.id === customUrl) {
70-
// In case of an edit page, we need to ensure we navigate to the edit page of the item ID, not the custom URL
77+
} else if (isSubPath && route.params.id === customUrl) {
78+
// In case of a sub path, we need to ensure we navigate to the edit page of the item ID, not the custom URL
7179
const itemId = rd.payload.uuid;
7280
newUrl = itemRoute.replace(rd.payload.firstMetadataValue('dspace.customurl'), itemId);
7381
}
@@ -87,7 +95,11 @@ export const itemPageResolver: ResolveFn<RemoteData<Item>> = (
8795
if (!thisRoute.startsWith(itemRoute)) {
8896
const itemId = rd.payload.uuid;
8997
const subRoute = thisRoute.substring(thisRoute.indexOf(itemId) + itemId.length, thisRoute.length);
90-
void router.navigateByUrl(itemRoute + subRoute);
98+
if (isPlatformServer(platformId)) {
99+
hardRedirectService.redirect(itemRoute + subRoute, 301);
100+
} else {
101+
router.navigateByUrl(itemRoute + subRoute);
102+
}
91103
}
92104
}
93105
}

0 commit comments

Comments
 (0)