diff --git a/src/app/item-page/edit-item-page/edit-item-page.component.spec.ts b/src/app/item-page/edit-item-page/edit-item-page.component.spec.ts index 4b5c777ef3e..bdb4a92c5aa 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.component.spec.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.component.spec.ts @@ -16,6 +16,7 @@ import { RouterModule, RouterStateSnapshot, } from '@angular/router'; +import { ItemDataService } from '@dspace/core/data/item-data.service'; import { Item } from '@dspace/core/shared/item.model'; import { TranslateLoaderMock } from '@dspace/core/testing/translate-loader.mock'; import { createSuccessfulRemoteDataObject } from '@dspace/core/utilities/remote-data.utils'; @@ -92,6 +93,7 @@ describe('EditItemPageComponent', () => { ], providers: [ { provide: ActivatedRoute, useValue: mockRoute }, + { provide: ItemDataService, useValue: { findByHref: () => of(createSuccessfulRemoteDataObject(new Item())) } }, ], schemas: [NO_ERRORS_SCHEMA], }).overrideComponent(EditItemPageComponent, { diff --git a/src/app/item-page/edit-item-page/edit-item-page.component.ts b/src/app/item-page/edit-item-page/edit-item-page.component.ts index 08eebba7feb..95d568f8137 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.component.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.component.ts @@ -17,9 +17,10 @@ import { RouterLink, RouterOutlet, } from '@angular/router'; +import { ItemDataService } from '@dspace/core/data/item-data.service'; import { RemoteData } from '@dspace/core/data/remote-data'; -import { getItemPageRoute } from '@dspace/core/router/utils/dso-route.utils'; import { Item } from '@dspace/core/shared/item.model'; +import { getFirstCompletedRemoteData } from '@dspace/core/shared/operators'; import { isNotEmpty } from '@dspace/shared/utils/empty.util'; import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { TranslateModule } from '@ngx-translate/core'; @@ -28,7 +29,10 @@ import { Observable, of, } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { + map, + switchMap, +} from 'rxjs/operators'; import { fadeIn, @@ -72,7 +76,12 @@ export class EditItemPageComponent implements OnInit { */ pages: { page: string, enabled: Observable }[]; - constructor(private route: ActivatedRoute, private router: Router, private injector: Injector) { + constructor( + private route: ActivatedRoute, + private router: Router, + private injector: Injector, + private items: ItemDataService, + ) { this.router.events.subscribe(() => this.initPageParamsByRoute()); } @@ -93,8 +102,18 @@ export class EditItemPageComponent implements OnInit { ); } return { page: child.path, enabled: enabled }; - }); // ignore reroutes - this.itemRD$ = this.route.data.pipe(map((data) => data.dso)); + }); + + this.itemRD$ = this.route.data.pipe( + map((data) => data.dso), + switchMap((rd: RemoteData) => { + if (rd?.hasSucceeded && rd?.payload?._links?.self?.href) { + return this.items.findByHref(rd.payload._links.self.href, true, false); + } + return of(rd); + }), + getFirstCompletedRemoteData(), + ); } /** @@ -102,7 +121,24 @@ export class EditItemPageComponent implements OnInit { * @param item The item for which the url is requested */ getItemPage(item: Item): string { - return getItemPageRoute(item); + if (!item) {return null;} + // Extract UUID from current edit URL instead of using item metadata + // This avoids the resolver redirecting back to the (possibly deleted) custom URL + const currentUrl = this.router.url; + const uuidMatch = currentUrl.match(/\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\//); + if (uuidMatch) { + const uuid = uuidMatch[1]; + const type = item.firstMetadataValue('dspace.entity.type'); + if (type) { + return `/entities/${encodeURIComponent(type.toLowerCase())}/${uuid}`; + } + return `/items/${uuid}`; + } + // Fallback + const type = item.firstMetadataValue('dspace.entity.type'); + return type + ? `/entities/${encodeURIComponent(type.toLowerCase())}/${item.uuid}` + : `/items/${item.uuid}`; } /** diff --git a/src/app/item-page/item-page.resolver.spec.ts b/src/app/item-page/item-page.resolver.spec.ts index 0d2132239f6..c71dc8eb774 100644 --- a/src/app/item-page/item-page.resolver.spec.ts +++ b/src/app/item-page/item-page.resolver.spec.ts @@ -177,6 +177,21 @@ describe('itemPageResolver', () => { }); }); + it('should not navigate to custom URL if coming from edit page', (done) => { + spyOn(router, 'navigateByUrl').and.callThrough(); + Object.defineProperty(router, 'url', { get: () => `/entities/person/${uuid}/edit`, configurable: true }); + + const route = { params: { id: uuid } } as any; + const state = { url: `/entities/person/${uuid}` } as any; + + resolver(route, state, router, itemService, store, authService, platformId, hardRedirectService) + .pipe(first()) + .subscribe(() => { + expect(router.navigateByUrl).not.toHaveBeenCalled(); + done(); + }); + }); + it('should not navigate if dspace.customurl matches the current route id', (done) => { spyOn(router, 'navigateByUrl').and.callThrough(); diff --git a/src/app/item-page/item-page.resolver.ts b/src/app/item-page/item-page.resolver.ts index 58f690ae309..262f676cc2a 100644 --- a/src/app/item-page/item-page.resolver.ts +++ b/src/app/item-page/item-page.resolver.ts @@ -77,7 +77,12 @@ export const itemPageResolver: ResolveFn> = ( itemRoute = isSubPath ? state.url : router.parseUrl(getItemPageRoute(rd.payload)).toString(); let newUrl: string; if (route.params.id !== customUrl && !isSubPath) { - newUrl = itemRoute.replace(route.params.id,rd.payload.firstMetadataValue('dspace.customurl')); + // Only redirect to custom URL if navigating directly to item page (not from edit/administer) + const referer = router.url; + const isComingFromEdit = referer.includes('/edit'); + if (!isComingFromEdit) { + newUrl = itemRoute.replace(route.params.id, rd.payload.firstMetadataValue('dspace.customurl')); + } } else if (isSubPath && route.params.id === customUrl) { // In case of a sub path, we need to ensure we navigate to the edit page of the item ID, not the custom URL const itemId = rd.payload.uuid;