Merge pull request #1001 from atmire/Item-page-redirects

DS-4283: (Google Scholar) Provide Entity Type Label/Name as part of URL path
This commit is contained in:
Tim Donohue
2021-03-12 13:13:46 -06:00
committed by GitHub
110 changed files with 880 additions and 228 deletions

View File

@@ -7,10 +7,11 @@ import {
of as observableOf, of as observableOf,
Subscription, Subscription,
BehaviorSubject, BehaviorSubject,
combineLatest as observableCombineLatest, ObservedValueOf, combineLatest as observableCombineLatest,
ObservedValueOf,
} from 'rxjs'; } from 'rxjs';
import { map, mergeMap, switchMap, take } from 'rxjs/operators'; import { map, mergeMap, switchMap, take } from 'rxjs/operators';
import {buildPaginatedList, PaginatedList} from '../../../../../core/data/paginated-list.model'; import { buildPaginatedList, PaginatedList } from '../../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../../core/data/remote-data'; import { RemoteData } from '../../../../../core/data/remote-data';
import { EPersonDataService } from '../../../../../core/eperson/eperson-data.service'; import { EPersonDataService } from '../../../../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../../../../core/eperson/group-data.service'; import { GroupDataService } from '../../../../../core/eperson/group-data.service';
@@ -19,11 +20,12 @@ import { Group } from '../../../../../core/eperson/models/group.model';
import { import {
getRemoteDataPayload, getRemoteDataPayload,
getFirstSucceededRemoteData, getFirstSucceededRemoteData,
getFirstCompletedRemoteData, getAllCompletedRemoteData getFirstCompletedRemoteData,
getAllCompletedRemoteData
} from '../../../../../core/shared/operators'; } from '../../../../../core/shared/operators';
import { NotificationsService } from '../../../../../shared/notifications/notifications.service'; import { NotificationsService } from '../../../../../shared/notifications/notifications.service';
import { PaginationComponentOptions } from '../../../../../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../../../../shared/pagination/pagination-component-options.model';
import {EpersonDtoModel} from '../../../../../core/eperson/models/eperson-dto.model'; import { EpersonDtoModel } from '../../../../../core/eperson/models/eperson-dto.model';
/** /**
* Keys to keep track of specific subscriptions * Keys to keep track of specific subscriptions

View File

@@ -56,19 +56,19 @@ describe('ItemAdminSearchResultActionsComponent', () => {
it('should render an edit button with the correct link', () => { it('should render an edit button with the correct link', () => {
const button = fixture.debugElement.query(By.css('a.edit-link')); const button = fixture.debugElement.query(By.css('a.edit-link'));
const link = button.nativeElement.href; const link = button.nativeElement.href;
expect(link).toContain(getItemEditRoute(id)); expect(link).toContain(getItemEditRoute(item));
}); });
it('should render a delete button with the correct link', () => { it('should render a delete button with the correct link', () => {
const button = fixture.debugElement.query(By.css('a.delete-link')); const button = fixture.debugElement.query(By.css('a.delete-link'));
const link = button.nativeElement.href; const link = button.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_DELETE_PATH).toString()); expect(link).toContain(new URLCombiner(getItemEditRoute(item), ITEM_EDIT_DELETE_PATH).toString());
}); });
it('should render a move button with the correct link', () => { it('should render a move button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.move-link')); const a = fixture.debugElement.query(By.css('a.move-link'));
const link = a.nativeElement.href; const link = a.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_MOVE_PATH).toString()); expect(link).toContain(new URLCombiner(getItemEditRoute(item), ITEM_EDIT_MOVE_PATH).toString());
}); });
describe('when the item is not withdrawn', () => { describe('when the item is not withdrawn', () => {
@@ -80,7 +80,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
it('should render a withdraw button with the correct link', () => { it('should render a withdraw button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.withdraw-link')); const a = fixture.debugElement.query(By.css('a.withdraw-link'));
const link = a.nativeElement.href; const link = a.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_WITHDRAW_PATH).toString()); expect(link).toContain(new URLCombiner(getItemEditRoute(item), ITEM_EDIT_WITHDRAW_PATH).toString());
}); });
it('should not render a reinstate button with the correct link', () => { it('should not render a reinstate button with the correct link', () => {
@@ -103,7 +103,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
it('should render a reinstate button with the correct link', () => { it('should render a reinstate button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.reinstate-link')); const a = fixture.debugElement.query(By.css('a.reinstate-link'));
const link = a.nativeElement.href; const link = a.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_REINSTATE_PATH).toString()); expect(link).toContain(new URLCombiner(getItemEditRoute(item), ITEM_EDIT_REINSTATE_PATH).toString());
}); });
}); });
@@ -116,7 +116,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
it('should render a make private button with the correct link', () => { it('should render a make private button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.private-link')); const a = fixture.debugElement.query(By.css('a.private-link'));
const link = a.nativeElement.href; const link = a.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_PRIVATE_PATH).toString()); expect(link).toContain(new URLCombiner(getItemEditRoute(item), ITEM_EDIT_PRIVATE_PATH).toString());
}); });
it('should not render a make public button with the correct link', () => { it('should not render a make public button with the correct link', () => {
@@ -139,7 +139,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
it('should render a make private button with the correct link', () => { it('should render a make private button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.public-link')); const a = fixture.debugElement.query(By.css('a.public-link'));
const link = a.nativeElement.href; const link = a.nativeElement.href;
expect(link).toContain(new URLCombiner(getItemEditRoute(id), ITEM_EDIT_PUBLIC_PATH).toString()); expect(link).toContain(new URLCombiner(getItemEditRoute(item), ITEM_EDIT_PUBLIC_PATH).toString());
}); });
}); });
}); });

View File

@@ -34,7 +34,7 @@ export class ItemAdminSearchResultActionsComponent {
* Returns the path to the edit page of this item * Returns the path to the edit page of this item
*/ */
getEditRoute(): string { getEditRoute(): string {
return getItemEditRoute(this.item.uuid); return getItemEditRoute(this.item);
} }
/** /**

View File

@@ -18,10 +18,14 @@ import { hasValue } from '../../shared/empty.util';
import { FormControl, FormGroup } from '@angular/forms'; import { FormControl, FormGroup } from '@angular/forms';
import { FileSizePipe } from '../../shared/utils/file-size-pipe'; import { FileSizePipe } from '../../shared/utils/file-size-pipe';
import { VarDirective } from '../../shared/utils/var.directive'; import { VarDirective } from '../../shared/utils/var.directive';
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import {
createSuccessfulRemoteDataObject,
createSuccessfulRemoteDataObject$
} from '../../shared/remote-data.utils';
import { RouterStub } from '../../shared/testing/router.stub'; import { RouterStub } from '../../shared/testing/router.stub';
import { getItemEditRoute } from '../../+item-page/item-page-routing-paths'; import { getEntityEditRoute, getItemEditRoute } from '../../+item-page/item-page-routing-paths';
import { createPaginatedList } from '../../shared/testing/utils.test'; import { createPaginatedList } from '../../shared/testing/utils.test';
import { Item } from '../../core/shared/item.model';
const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info'); const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info');
const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning'); const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning');
@@ -109,9 +113,9 @@ describe('EditBitstreamPageComponent', () => {
self: 'bitstream-selflink' self: 'bitstream-selflink'
}, },
bundle: createSuccessfulRemoteDataObject$({ bundle: createSuccessfulRemoteDataObject$({
item: createSuccessfulRemoteDataObject$({ item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), {
uuid: 'some-uuid' uuid: 'some-uuid'
}) }))
}) })
}); });
bitstreamService = jasmine.createSpyObj('bitstreamService', { bitstreamService = jasmine.createSpyObj('bitstreamService', {
@@ -237,14 +241,14 @@ describe('EditBitstreamPageComponent', () => {
it('should redirect to the item edit page on the bitstreams tab with the itemId from the component', () => { it('should redirect to the item edit page on the bitstreams tab with the itemId from the component', () => {
comp.itemId = 'some-uuid1'; comp.itemId = 'some-uuid1';
comp.navigateToItemEditBitstreams(); comp.navigateToItemEditBitstreams();
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute('some-uuid1'), 'bitstreams']); expect(routerStub.navigate).toHaveBeenCalledWith([getEntityEditRoute(null, 'some-uuid1'), 'bitstreams']);
}); });
}); });
describe('when navigateToItemEditBitstreams is called, and the component does not have an itemId', () => { describe('when navigateToItemEditBitstreams is called, and the component does not have an itemId', () => {
it('should redirect to the item edit page on the bitstreams tab with the itemId from the bundle links ', () => { it('should redirect to the item edit page on the bitstreams tab with the itemId from the bundle links ', () => {
comp.itemId = undefined; comp.itemId = undefined;
comp.navigateToItemEditBitstreams(); comp.navigateToItemEditBitstreams();
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute('some-uuid'), 'bitstreams']); expect(routerStub.navigate).toHaveBeenCalledWith([getEntityEditRoute(null, 'some-uuid'), 'bitstreams']);
}); });
}); });
}); });

View File

@@ -33,9 +33,8 @@ import { Metadata } from '../../core/shared/metadata.utils';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { PaginatedList } from '../../core/data/paginated-list.model'; import { PaginatedList } from '../../core/data/paginated-list.model';
import { getItemEditRoute } from '../../+item-page/item-page-routing-paths'; import { getEntityEditRoute, getItemEditRoute } from '../../+item-page/item-page-routing-paths';
import { Bundle } from '../../core/shared/bundle.model'; import { Bundle } from '../../core/shared/bundle.model';
import { Item } from '../../core/shared/item.model';
@Component({ @Component({
selector: 'ds-edit-bitstream-page', selector: 'ds-edit-bitstream-page',
@@ -264,9 +263,17 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
/** /**
* The ID of the item the bitstream originates from * The ID of the item the bitstream originates from
* Taken from the current query parameters when present * Taken from the current query parameters when present
* This will determine the route of the item edit page to return to
*/ */
itemId: string; itemId: string;
/**
* The entity type of the item the bitstream originates from
* Taken from the current query parameters when present
* This will determine the route of the item edit page to return to
*/
entityType: string;
/** /**
* Array to track all subscriptions and unsubscribe them onDestroy * Array to track all subscriptions and unsubscribe them onDestroy
* @type {Array} * @type {Array}
@@ -293,6 +300,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
this.formGroup = this.formService.createFormGroup(this.formModel); this.formGroup = this.formService.createFormGroup(this.formModel);
this.itemId = this.route.snapshot.queryParams.itemId; this.itemId = this.route.snapshot.queryParams.itemId;
this.entityType = this.route.snapshot.queryParams.entityType;
this.bitstreamRD$ = this.route.data.pipe(map((data) => data.bitstream)); this.bitstreamRD$ = this.route.data.pipe(map((data) => data.bitstream));
this.bitstreamFormatsRD$ = this.bitstreamFormatService.findAll(this.findAllOptions); this.bitstreamFormatsRD$ = this.bitstreamFormatService.findAll(this.findAllOptions);
@@ -499,10 +507,10 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
*/ */
navigateToItemEditBitstreams() { navigateToItemEditBitstreams() {
if (hasValue(this.itemId)) { if (hasValue(this.itemId)) {
this.router.navigate([getItemEditRoute(this.itemId), 'bitstreams']); this.router.navigate([getEntityEditRoute(this.entityType, this.itemId), 'bitstreams']);
} else { } else {
this.bitstream.bundle.pipe(getFirstSucceededRemoteDataPayload(), this.bitstream.bundle.pipe(getFirstSucceededRemoteDataPayload(),
mergeMap((bundle: Bundle) => bundle.item.pipe(getFirstSucceededRemoteDataPayload(), map((item: Item) => item.uuid)))) mergeMap((bundle: Bundle) => bundle.item.pipe(getFirstSucceededRemoteDataPayload())))
.subscribe((item) => { .subscribe((item) => {
this.router.navigate(([getItemEditRoute(item), 'bitstreams'])); this.router.navigate(([getItemEditRoute(item), 'bitstreams']));
}); });

View File

@@ -35,7 +35,7 @@
</ds-comcol-page-content> </ds-comcol-page-content>
</header> </header>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'collections'" [dso]="collection" [tooltipMsg]="'collection.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="collectionPageRoute$ | async" [dso]="collection" [tooltipMsg]="'collection.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<section class="comcol-page-browse-section"> <section class="comcol-page-browse-section">

View File

@@ -15,13 +15,19 @@ import { Bitstream } from '../core/shared/bitstream.model';
import { Collection } from '../core/shared/collection.model'; import { Collection } from '../core/shared/collection.model';
import { DSpaceObjectType } from '../core/shared/dspace-object-type.model'; import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
import { Item } from '../core/shared/item.model'; import { Item } from '../core/shared/item.model';
import { getFirstSucceededRemoteData, redirectOn4xx, toDSpaceObjectListRD } from '../core/shared/operators'; import {
getAllSucceededRemoteDataPayload,
getFirstSucceededRemoteData,
redirectOn4xx,
toDSpaceObjectListRD
} from '../core/shared/operators';
import { fadeIn, fadeInOut } from '../shared/animations/fade'; import { fadeIn, fadeInOut } from '../shared/animations/fade';
import { hasValue, isNotEmpty } from '../shared/empty.util'; import { hasValue, isNotEmpty } from '../shared/empty.util';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { AuthService } from '../core/auth/auth.service'; import { AuthService } from '../core/auth/auth.service';
import {PaginationChangeEvent} from '../shared/pagination/paginationChangeEvent.interface'; import {PaginationChangeEvent} from '../shared/pagination/paginationChangeEvent.interface';
import { getCollectionPageRoute } from './collection-page-routing-paths';
@Component({ @Component({
selector: 'ds-collection-page', selector: 'ds-collection-page',
@@ -44,6 +50,11 @@ export class CollectionPageComponent implements OnInit {
sortConfig: SortOptions sortConfig: SortOptions
}>; }>;
/**
* Route to the community page
*/
collectionPageRoute$: Observable<string>;
constructor( constructor(
private collectionDataService: CollectionDataService, private collectionDataService: CollectionDataService,
private searchService: SearchService, private searchService: SearchService,
@@ -94,6 +105,11 @@ export class CollectionPageComponent implements OnInit {
) )
); );
this.collectionPageRoute$ = this.collectionRD$.pipe(
getAllSucceededRemoteDataPayload(),
map((collection) => getCollectionPageRoute(collection.id))
);
this.route.queryParams.pipe(take(1)).subscribe((params) => { this.route.queryParams.pipe(take(1)).subscribe((params) => {
this.metadata.processRemoteData(this.collectionRD$); this.metadata.processRemoteData(this.collectionRD$);
}); });

View File

@@ -21,7 +21,7 @@
</ds-comcol-page-content> </ds-comcol-page-content>
</header> </header>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'communities'" [dso]="communityPayload" [tooltipMsg]="'community.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="communityPageRoute$ | async" [dso]="communityPayload" [tooltipMsg]="'community.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<section class="comcol-page-browse-section"> <section class="comcol-page-browse-section">

View File

@@ -13,8 +13,9 @@ import { MetadataService } from '../core/metadata/metadata.service';
import { fadeInOut } from '../shared/animations/fade'; import { fadeInOut } from '../shared/animations/fade';
import { hasValue } from '../shared/empty.util'; import { hasValue } from '../shared/empty.util';
import { redirectOn4xx } from '../core/shared/operators'; import { getAllSucceededRemoteDataPayload, redirectOn4xx } from '../core/shared/operators';
import { AuthService } from '../core/auth/auth.service'; import { AuthService } from '../core/auth/auth.service';
import { getCommunityPageRoute } from './community-page-routing-paths';
@Component({ @Component({
selector: 'ds-community-page', selector: 'ds-community-page',
@@ -36,6 +37,12 @@ export class CommunityPageComponent implements OnInit {
* The logo of this community * The logo of this community
*/ */
logoRD$: Observable<RemoteData<Bitstream>>; logoRD$: Observable<RemoteData<Bitstream>>;
/**
* Route to the community page
*/
communityPageRoute$: Observable<string>;
constructor( constructor(
private communityDataService: CommunityDataService, private communityDataService: CommunityDataService,
private metadata: MetadataService, private metadata: MetadataService,
@@ -55,6 +62,10 @@ export class CommunityPageComponent implements OnInit {
map((rd: RemoteData<Community>) => rd.payload), map((rd: RemoteData<Community>) => rd.payload),
filter((community: Community) => hasValue(community)), filter((community: Community) => hasValue(community)),
mergeMap((community: Community) => community.logo)); mergeMap((community: Community) => community.logo));
this.communityPageRoute$ = this.communityRD$.pipe(
getAllSucceededRemoteDataPayload(),
map((community) => getCommunityPageRoute(community.id))
);
} }
} }

View File

@@ -17,7 +17,7 @@ import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operato
import { UploaderComponent } from '../../../shared/uploader/uploader.component'; import { UploaderComponent } from '../../../shared/uploader/uploader.component';
import { RequestService } from '../../../core/data/request.service'; import { RequestService } from '../../../core/data/request.service';
import { getBitstreamModuleRoute } from '../../../app-routing-paths'; import { getBitstreamModuleRoute } from '../../../app-routing-paths';
import { getItemEditRoute } from '../../item-page-routing-paths'; import { getEntityEditRoute } from '../../item-page-routing-paths';
@Component({ @Component({
selector: 'ds-upload-bitstream', selector: 'ds-upload-bitstream',
@@ -37,6 +37,12 @@ export class UploadBitstreamComponent implements OnInit, OnDestroy {
*/ */
itemId: string; itemId: string;
/**
* The entity type of the item
* This is fetched from the current URL and will determine the item's page route
*/
entityType: string;
/** /**
* The item to upload a bitstream to * The item to upload a bitstream to
*/ */
@@ -100,6 +106,7 @@ export class UploadBitstreamComponent implements OnInit, OnDestroy {
*/ */
ngOnInit(): void { ngOnInit(): void {
this.itemId = this.route.snapshot.params.id; this.itemId = this.route.snapshot.params.id;
this.entityType = this.route.snapshot.params['entity-type'];
this.itemRD$ = this.route.data.pipe(map((data) => data.dso)); this.itemRD$ = this.route.data.pipe(map((data) => data.dso));
this.bundlesRD$ = this.itemRD$.pipe( this.bundlesRD$ = this.itemRD$.pipe(
switchMap((itemRD: RemoteData<Item>) => itemRD.payload.bundles) switchMap((itemRD: RemoteData<Item>) => itemRD.payload.bundles)
@@ -167,7 +174,7 @@ export class UploadBitstreamComponent implements OnInit, OnDestroy {
}); });
// Bring over the item ID as a query parameter // Bring over the item ID as a query parameter
const queryParams = { itemId: this.itemId }; const queryParams = { itemId: this.itemId, entityType: this.entityType };
this.router.navigate([getBitstreamModuleRoute(), bitstream.id, 'edit'], { queryParams: queryParams }); this.router.navigate([getBitstreamModuleRoute(), bitstream.id, 'edit'], { queryParams: queryParams });
} }
@@ -193,7 +200,7 @@ export class UploadBitstreamComponent implements OnInit, OnDestroy {
* When cancel is clicked, navigate back to the item's edit bitstreams page * When cancel is clicked, navigate back to the item's edit bitstreams page
*/ */
onCancel() { onCancel() {
this.router.navigate([getItemEditRoute(this.itemId), 'bitstreams']); this.router.navigate([getEntityEditRoute(this.entityType, this.itemId), 'bitstreams']);
} }
/** /**

View File

@@ -14,6 +14,7 @@ import { first, map, switchMap, tap } from 'rxjs/operators';
import { RemoteData } from '../../../core/data/remote-data'; import { RemoteData } from '../../../core/data/remote-data';
import { AbstractTrackableComponent } from '../../../shared/trackable/abstract-trackable.component'; import { AbstractTrackableComponent } from '../../../shared/trackable/abstract-trackable.component';
import { environment } from '../../../../environments/environment'; import { environment } from '../../../../environments/environment';
import { getItemPageRoute } from '../../item-page-routing-paths';
import { ITEM_PAGE_LINKS_TO_FOLLOW } from '../../item-page.resolver'; import { ITEM_PAGE_LINKS_TO_FOLLOW } from '../../item-page.resolver';
import { getAllSucceededRemoteData } from '../../../core/shared/operators'; import { getAllSucceededRemoteData } from '../../../core/shared/operators';
import { hasValue } from '../../../shared/empty.util'; import { hasValue } from '../../../shared/empty.util';
@@ -36,6 +37,11 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
*/ */
updates$: Observable<FieldUpdates>; updates$: Observable<FieldUpdates>;
/**
* Route to the item's page
*/
itemPageRoute: string;
/** /**
* A subscription that checks when the item is deleted in cache and reloads the item by sending a new request * A subscription that checks when the item is deleted in cache and reloads the item by sending a new request
* This is used to update the item in cache after bitstreams are deleted * This is used to update the item in cache after bitstreams are deleted
@@ -69,6 +75,7 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
getAllSucceededRemoteData() getAllSucceededRemoteData()
).subscribe((rd: RemoteData<Item>) => { ).subscribe((rd: RemoteData<Item>) => {
this.item = rd.payload; this.item = rd.payload;
this.itemPageRoute = getItemPageRoute(this.item);
this.postItemInit(); this.postItemInit();
this.initializeUpdates(); this.initializeUpdates();
}); });

View File

@@ -55,6 +55,6 @@ export class EditItemPageComponent implements OnInit {
* @param item The item for which the url is requested * @param item The item for which the url is requested
*/ */
getItemPage(item: Item): string { getItemPage(item: Item): string {
return getItemPageRoute(item.id); return getItemPageRoute(item);
} }
} }

View File

@@ -1,7 +1,7 @@
<div class="item-bitstreams" *ngVar="(bundles$ | async) as bundles"> <div class="item-bitstreams" *ngVar="(bundles$ | async) as bundles">
<div class="button-row top d-flex mt-2"> <div class="button-row top d-flex mt-2">
<button class="mr-auto btn btn-success" <button class="mr-auto btn btn-success"
[routerLink]="['/items/', item.id, 'bitstreams', 'new']"><i [routerLink]="[itemPageRoute, 'bitstreams', 'new']"><i
class="fas fa-upload"></i> class="fas fa-upload"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.upload-button" | translate}}</span> <span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.upload-button" | translate}}</span>
</button> </button>

View File

@@ -8,7 +8,7 @@
</div> </div>
<div class="{{columnSizes.columns[3].buildClasses()}} text-center row-element"> <div class="{{columnSizes.columns[3].buildClasses()}} text-center row-element">
<div class="btn-group bundle-action-buttons"> <div class="btn-group bundle-action-buttons">
<button [routerLink]="['/items/', item.id, 'bitstreams', 'new']" <button [routerLink]="[itemPageRoute, 'bitstreams', 'new']"
[queryParams]="{bundle: bundle.id}" [queryParams]="{bundle: bundle.id}"
class="btn btn-outline-success btn-sm" class="btn btn-outline-success btn-sm"
title="{{'item.edit.bitstreams.bundle.edit.buttons.upload' | translate}}"> title="{{'item.edit.bitstreams.bundle.edit.buttons.upload' | translate}}">

View File

@@ -3,6 +3,7 @@ import { Bundle } from '../../../../core/shared/bundle.model';
import { Item } from '../../../../core/shared/item.model'; import { Item } from '../../../../core/shared/item.model';
import { ResponsiveColumnSizes } from '../../../../shared/responsive-table-sizes/responsive-column-sizes'; import { ResponsiveColumnSizes } from '../../../../shared/responsive-table-sizes/responsive-column-sizes';
import { ResponsiveTableSizes } from '../../../../shared/responsive-table-sizes/responsive-table-sizes'; import { ResponsiveTableSizes } from '../../../../shared/responsive-table-sizes/responsive-table-sizes';
import { getItemPageRoute } from '../../../item-page-routing-paths';
@Component({ @Component({
selector: 'ds-item-edit-bitstream-bundle', selector: 'ds-item-edit-bitstream-bundle',
@@ -49,11 +50,17 @@ export class ItemEditBitstreamBundleComponent implements OnInit {
*/ */
bundleNameColumn: ResponsiveColumnSizes; bundleNameColumn: ResponsiveColumnSizes;
/**
* Route to the item's page
*/
itemPageRoute: string;
constructor(private viewContainerRef: ViewContainerRef) { constructor(private viewContainerRef: ViewContainerRef) {
} }
ngOnInit(): void { ngOnInit(): void {
this.bundleNameColumn = this.columnSizes.combineColumns(0, 2); this.bundleNameColumn = this.columnSizes.combineColumns(0, 2);
this.viewContainerRef.createEmbeddedView(this.bundleView); this.viewContainerRef.createEmbeddedView(this.bundleView);
this.itemPageRoute = getItemPageRoute(this.item);
} }
} }

View File

@@ -55,6 +55,7 @@ describe('ItemCollectionMapperComponent', () => {
const mockCollection = Object.assign(new Collection(), { id: 'collection1' }); const mockCollection = Object.assign(new Collection(), { id: 'collection1' });
const mockItem: Item = Object.assign(new Item(), { const mockItem: Item = Object.assign(new Item(), {
id: '932c7d50-d85a-44cb-b9dc-b427b12877bd', id: '932c7d50-d85a-44cb-b9dc-b427b12877bd',
uuid: '932c7d50-d85a-44cb-b9dc-b427b12877bd',
name: 'test-item' name: 'test-item'
}); });
const mockItemRD: RemoteData<Item> = createSuccessfulRemoteDataObject(mockItem); const mockItemRD: RemoteData<Item> = createSuccessfulRemoteDataObject(mockItem);
@@ -212,7 +213,7 @@ describe('ItemCollectionMapperComponent', () => {
}); });
it('should navigate to the item page', () => { it('should navigate to the item page', () => {
expect(router.navigate).toHaveBeenCalledWith(['/items/', mockItem.id]); expect(router.navigate).toHaveBeenCalledWith(['/items/' + mockItem.uuid]);
}); });
}); });

View File

@@ -26,6 +26,7 @@ import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
import { SearchService } from '../../../core/shared/search/search.service'; import { SearchService } from '../../../core/shared/search/search.service';
import { NoContent } from '../../../core/shared/NoContent.model'; import { NoContent } from '../../../core/shared/NoContent.model';
import { getItemPageRoute } from '../../item-page-routing-paths';
@Component({ @Component({
selector: 'ds-item-collection-mapper', selector: 'ds-item-collection-mapper',
@@ -312,7 +313,7 @@ export class ItemCollectionMapperComponent implements OnInit {
getRemoteDataPayload(), getRemoteDataPayload(),
take(1) take(1)
).subscribe((item: Item) => { ).subscribe((item: Item) => {
this.router.navigate(['/items/', item.id]); this.router.navigate([getItemPageRoute(item)]);
}); });
} }

View File

@@ -89,7 +89,7 @@
<button (click)="performAction()" <button (click)="performAction()"
class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}} class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}}
</button> </button>
<button [routerLink]="['/items/', item.id, 'edit']" class="btn btn-outline-secondary cancel"> <button [routerLink]="[itemPageRoute, 'edit']" class="btn btn-outline-secondary cancel">
{{cancelMessage| translate}} {{cancelMessage| translate}}
</button> </button>

View File

@@ -183,7 +183,7 @@ describe('ItemDeleteComponent', () => {
describe('notify', () => { describe('notify', () => {
it('should navigate to the item edit page on failed deletion of the item', () => { it('should navigate to the item edit page on failed deletion of the item', () => {
comp.notify(false); comp.notify(false);
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute('fake-id')]); expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute(mockItem)]);
}); });
}); });
}); });

View File

@@ -355,7 +355,7 @@ export class ItemDeleteComponent
this.router.navigate(['']); this.router.navigate(['']);
} else { } else {
this.notificationsService.error(this.translateService.get('item.edit.' + this.messageKey + '.error')); this.notificationsService.error(this.translateService.get('item.edit.' + this.messageKey + '.error'));
this.router.navigate([getItemEditRoute(this.item.id)]); this.router.navigate([getItemEditRoute(this.item)]);
} }
} }
} }

View File

@@ -19,7 +19,7 @@
</ds-dso-input-suggestions> </ds-dso-input-suggestions>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<p> <p>
@@ -39,7 +39,7 @@
{{'item.edit.move.processing' | translate}} {{'item.edit.move.processing' | translate}}
</span> </span>
</button> </button>
<button [routerLink]="['/items/', (itemRD$ | async)?.payload?.id, 'edit']" <button [routerLink]="[(itemPageRoute$ | async), 'edit']"
class="btn btn-outline-secondary"> class="btn btn-outline-secondary">
{{'item.edit.move.cancel' | translate}} {{'item.edit.move.cancel' | translate}}
</button> </button>

View File

@@ -57,9 +57,9 @@ describe('ItemMoveComponent', () => {
const routeStub = { const routeStub = {
data: observableOf({ data: observableOf({
dso: createSuccessfulRemoteDataObject({ dso: createSuccessfulRemoteDataObject(Object.assign(new Item(), {
id: 'item1' id: 'item1'
}) }))
}) })
}; };
@@ -122,7 +122,10 @@ describe('ItemMoveComponent', () => {
}); });
describe('moveCollection', () => { describe('moveCollection', () => {
it('should call itemDataService.moveToCollection', () => { it('should call itemDataService.moveToCollection', () => {
comp.itemId = 'item-id'; comp.item = Object.assign(new Item(), {
id: 'item-id',
uuid: 'item-id',
});
comp.selectedCollectionName = 'selected-collection-id'; comp.selectedCollectionName = 'selected-collection-id';
comp.selectedCollection = collection1; comp.selectedCollection = collection1;
comp.moveCollection(); comp.moveCollection();

View File

@@ -10,7 +10,7 @@ import { NotificationsService } from '../../../shared/notifications/notification
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { import {
getFirstSucceededRemoteData, getFirstSucceededRemoteData,
getFirstCompletedRemoteData getFirstCompletedRemoteData, getAllSucceededRemoteDataPayload
} from '../../../core/shared/operators'; } from '../../../core/shared/operators';
import { ItemDataService } from '../../../core/data/item-data.service'; import { ItemDataService } from '../../../core/data/item-data.service';
import { Observable, of as observableOf } from 'rxjs'; import { Observable, of as observableOf } from 'rxjs';
@@ -19,7 +19,7 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio
import { SearchService } from '../../../core/shared/search/search.service'; import { SearchService } from '../../../core/shared/search/search.service';
import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model'; import { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.model';
import { SearchResult } from '../../../shared/search/search-result.model'; import { SearchResult } from '../../../shared/search/search-result.model';
import { getItemEditRoute } from '../../item-page-routing-paths'; import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths';
@Component({ @Component({
selector: 'ds-item-move', selector: 'ds-item-move',
@@ -43,11 +43,16 @@ export class ItemMoveComponent implements OnInit {
selectedCollection: Collection; selectedCollection: Collection;
canSubmit = false; canSubmit = false;
itemId: string; item: Item;
processing = false; processing = false;
pagination = new PaginationComponentOptions(); pagination = new PaginationComponentOptions();
/**
* Route to the item's page
*/
itemPageRoute$: Observable<string>;
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private router: Router, private router: Router,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
@@ -58,8 +63,12 @@ export class ItemMoveComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.itemRD$ = this.route.data.pipe(map((data) => data.dso), getFirstSucceededRemoteData()) as Observable<RemoteData<Item>>; this.itemRD$ = this.route.data.pipe(map((data) => data.dso), getFirstSucceededRemoteData()) as Observable<RemoteData<Item>>;
this.itemPageRoute$ = this.itemRD$.pipe(
getAllSucceededRemoteDataPayload(),
map((item) => getItemPageRoute(item))
);
this.itemRD$.subscribe((rd) => { this.itemRD$.subscribe((rd) => {
this.itemId = rd.payload.id; this.item = rd.payload;
} }
); );
this.pagination.pageSize = 5; this.pagination.pageSize = 5;
@@ -116,9 +125,9 @@ export class ItemMoveComponent implements OnInit {
*/ */
moveCollection() { moveCollection() {
this.processing = true; this.processing = true;
this.itemDataService.moveToCollection(this.itemId, this.selectedCollection).pipe(getFirstCompletedRemoteData()).subscribe( this.itemDataService.moveToCollection(this.item.id, this.selectedCollection).pipe(getFirstCompletedRemoteData()).subscribe(
(response: RemoteData<Collection>) => { (response: RemoteData<Collection>) => {
this.router.navigate([getItemEditRoute(this.itemId)]); this.router.navigate([getItemEditRoute(this.item)]);
if (response.hasSucceeded) { if (response.hasSucceeded) {
this.notificationsService.success(this.translateService.get('item.edit.move.success')); this.notificationsService.success(this.translateService.get('item.edit.move.success'));
} else { } else {

View File

@@ -47,9 +47,9 @@ describe('ItemReinstateComponent', () => {
routeStub = { routeStub = {
data: observableOf({ data: observableOf({
dso: createSuccessfulRemoteDataObject({ dso: createSuccessfulRemoteDataObject(Object.assign(new Item(), {
id: 'fake-id' id: 'fake-id'
}) }))
}) })
}; };

View File

@@ -12,7 +12,7 @@
{{'item.edit.tabs.status.labels.itemPage' | translate}}: {{'item.edit.tabs.status.labels.itemPage' | translate}}:
</div> </div>
<div class="col-9 float-left status-data" id="status-itemPage"> <div class="col-9 float-left status-data" id="status-itemPage">
<a [routerLink]="getItemPage((itemRD$ | async)?.payload)">{{getItemPage((itemRD$ | async)?.payload)}}</a> <a [routerLink]="itemPageRoute$ | async">{{itemPageRoute$ | async}}</a>
</div> </div>
<div *ngFor="let operation of (operations$ | async)" class="w-100" [ngClass]="{'pt-3': operation}"> <div *ngFor="let operation of (operations$ | async)" class="w-100" [ngClass]="{'pt-3': operation}">

View File

@@ -20,6 +20,7 @@ describe('ItemStatusComponent', () => {
const mockItem = Object.assign(new Item(), { const mockItem = Object.assign(new Item(), {
id: 'fake-id', id: 'fake-id',
uuid: 'fake-id',
handle: 'fake/handle', handle: 'fake/handle',
lastModified: '2018', lastModified: '2018',
_links: { _links: {
@@ -27,7 +28,7 @@ describe('ItemStatusComponent', () => {
} }
}); });
const itemPageUrl = `items/${mockItem.id}`; const itemPageUrl = `/items/${mockItem.uuid}`;
const routeStub = { const routeStub = {
parent: { parent: {

View File

@@ -10,6 +10,7 @@ import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-path
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { hasValue } from '../../../shared/empty.util'; import { hasValue } from '../../../shared/empty.util';
import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators';
@Component({ @Component({
selector: 'ds-item-status', selector: 'ds-item-status',
@@ -50,6 +51,11 @@ export class ItemStatusComponent implements OnInit {
*/ */
actionsKeys; actionsKeys;
/**
* Route to the item's page
*/
itemPageRoute$: Observable<string>;
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private authorizationService: AuthorizationDataService) { private authorizationService: AuthorizationDataService) {
} }
@@ -109,15 +115,10 @@ export class ItemStatusComponent implements OnInit {
}); });
} }
}); });
this.itemPageRoute$ = this.itemRD$.pipe(
} getAllSucceededRemoteDataPayload(),
map((item) => getItemPageRoute(item))
/** );
* Get the url to the simple item page
* @returns {string} url
*/
getItemPage(item: Item): string {
return getItemPageRoute(item.id);
} }
/** /**
@@ -125,7 +126,7 @@ export class ItemStatusComponent implements OnInit {
* @returns {string} url * @returns {string} url
*/ */
getCurrentUrl(item: Item): string { getCurrentUrl(item: Item): string {
return getItemEditRoute(item.id); return getItemEditRoute(item);
} }
trackOperation(index: number, operation: ItemOperation) { trackOperation(index: number, operation: ItemOperation) {

View File

@@ -6,11 +6,11 @@
<ds-modify-item-overview [item]="item"></ds-modify-item-overview> <ds-modify-item-overview [item]="item"></ds-modify-item-overview>
<button (click)="performAction()" class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}} <button (click)="performAction()" class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}}
</button> </button>
<button [routerLink]="['/items/', item.id, 'edit']" class="btn btn-outline-secondary cancel"> <button [routerLink]="[itemPageRoute, 'edit']" class="btn btn-outline-secondary cancel">
{{cancelMessage| translate}} {{cancelMessage| translate}}
</button> </button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -74,9 +74,9 @@ describe('AbstractSimpleItemActionComponent', () => {
routeStub = { routeStub = {
data: observableOf({ data: observableOf({
dso: createSuccessfulRemoteDataObject({ dso: createSuccessfulRemoteDataObject(Object.assign(new Item(), {
id: 'fake-id' id: 'fake-id'
}) }))
}) })
}; };
@@ -136,14 +136,14 @@ describe('AbstractSimpleItemActionComponent', () => {
comp.processRestResponse(successfulRemoteData); comp.processRestResponse(successfulRemoteData);
expect(notificationsServiceStub.success).toHaveBeenCalled(); expect(notificationsServiceStub.success).toHaveBeenCalled();
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute(mockItem.id)]); expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute(mockItem)]);
}); });
it('should process a RemoteData to navigate and display success notification', () => { it('should process a RemoteData to navigate and display success notification', () => {
comp.processRestResponse(failedRemoteData); comp.processRestResponse(failedRemoteData);
expect(notificationsServiceStub.error).toHaveBeenCalled(); expect(notificationsServiceStub.error).toHaveBeenCalled();
expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute(mockItem.id)]); expect(routerStub.navigate).toHaveBeenCalledWith([getItemEditRoute(mockItem)]);
}); });
}); });

View File

@@ -9,7 +9,7 @@ import { Observable } from 'rxjs';
import { getFirstSucceededRemoteData } from '../../../core/shared/operators'; import { getFirstSucceededRemoteData } from '../../../core/shared/operators';
import { first, map } from 'rxjs/operators'; import { first, map } from 'rxjs/operators';
import { findSuccessfulAccordingTo } from '../edit-item-operators'; import { findSuccessfulAccordingTo } from '../edit-item-operators';
import { getItemEditRoute } from '../../item-page-routing-paths'; import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths';
/** /**
* Component to render and handle simple item edit actions such as withdrawal and reinstatement. * Component to render and handle simple item edit actions such as withdrawal and reinstatement.
@@ -30,6 +30,11 @@ export class AbstractSimpleItemActionComponent implements OnInit {
headerMessage: string; headerMessage: string;
descriptionMessage: string; descriptionMessage: string;
/**
* Route to the item's page
*/
itemPageRoute: string;
protected predicate: Predicate<RemoteData<Item>>; protected predicate: Predicate<RemoteData<Item>>;
constructor(protected route: ActivatedRoute, constructor(protected route: ActivatedRoute,
@@ -47,6 +52,7 @@ export class AbstractSimpleItemActionComponent implements OnInit {
this.itemRD$.pipe(first()).subscribe((rd) => { this.itemRD$.pipe(first()).subscribe((rd) => {
this.item = rd.payload; this.item = rd.payload;
this.itemPageRoute = getItemPageRoute(this.item);
} }
); );
@@ -71,11 +77,11 @@ export class AbstractSimpleItemActionComponent implements OnInit {
this.itemDataService.findById(this.item.id).pipe( this.itemDataService.findById(this.item.id).pipe(
findSuccessfulAccordingTo(this.predicate)).subscribe(() => { findSuccessfulAccordingTo(this.predicate)).subscribe(() => {
this.notificationsService.success(this.translateService.get('item.edit.' + this.messageKey + '.success')); this.notificationsService.success(this.translateService.get('item.edit.' + this.messageKey + '.success'));
this.router.navigate([getItemEditRoute(this.item.id)]); this.router.navigate([getItemEditRoute(this.item)]);
}); });
} else { } else {
this.notificationsService.error(this.translateService.get('item.edit.' + this.messageKey + '.error')); this.notificationsService.error(this.translateService.get('item.edit.' + this.messageKey + '.error'));
this.router.navigate([getItemEditRoute(this.item.id)]); this.router.navigate([getItemEditRoute(this.item)]);
} }
} }

View File

@@ -7,11 +7,11 @@
<div class="d-flex flex-row"> <div class="d-flex flex-row">
<ds-item-page-title-field class="mr-auto" [item]="item"></ds-item-page-title-field> <ds-item-page-title-field class="mr-auto" [item]="item"></ds-item-page-title-field>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'items'" [dso]="item" [tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="itemPageRoute$ | async" [dso]="item" [tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<div class="simple-view-link my-3"> <div class="simple-view-link my-3">
<a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id]"> <a class="btn btn-outline-primary" [routerLink]="[(itemPageRoute$ | async)]">
{{"item.page.link.simple" | translate}} {{"item.page.link.simple" | translate}}
</a> </a>
</div> </div>

View File

@@ -1,4 +1,6 @@
import { URLCombiner } from '../core/url-combiner/url-combiner'; import { URLCombiner } from '../core/url-combiner/url-combiner';
import { Item } from '../core/shared/item.model';
import { isNotEmpty } from '../shared/empty.util';
export const ITEM_MODULE_PATH = 'items'; export const ITEM_MODULE_PATH = 'items';
@@ -6,12 +8,30 @@ export function getItemModuleRoute() {
return `/${ITEM_MODULE_PATH}`; return `/${ITEM_MODULE_PATH}`;
} }
export function getItemPageRoute(itemId: string) { /**
return new URLCombiner(getItemModuleRoute(), itemId).toString(); * Get the route to an item's page
* Depending on the item's relationship type, the route will either start with /items or /entities
* @param item The item to retrieve the route for
*/
export function getItemPageRoute(item: Item) {
const type = item.firstMetadataValue('relationship.type');
return getEntityPageRoute(type, item.uuid);
} }
export function getItemEditRoute(id: string) { export function getItemEditRoute(item: Item) {
return new URLCombiner(getItemModuleRoute(), id, ITEM_EDIT_PATH).toString(); return new URLCombiner(getItemPageRoute(item), ITEM_EDIT_PATH).toString();
}
export function getEntityPageRoute(entityType: string, itemId: string) {
if (isNotEmpty(entityType)) {
return new URLCombiner('/entities', encodeURIComponent(entityType.toLowerCase()), itemId).toString();
} else {
return new URLCombiner(getItemModuleRoute(), itemId).toString();
}
}
export function getEntityEditRoute(entityType: string, itemId: string) {
return new URLCombiner(getEntityPageRoute(entityType, itemId), ITEM_EDIT_PATH).toString();
} }
export const ITEM_EDIT_PATH = 'edit'; export const ITEM_EDIT_PATH = 'edit';

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { RemoteData } from '../core/data/remote-data'; import { RemoteData } from '../core/data/remote-data';
import { ItemDataService } from '../core/data/item-data.service'; import { ItemDataService } from '../core/data/item-data.service';
@@ -9,6 +9,9 @@ import { FindListOptions } from '../core/data/request.models';
import { getFirstCompletedRemoteData } from '../core/shared/operators'; import { getFirstCompletedRemoteData } from '../core/shared/operators';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { ResolvedAction } from '../core/resolving/resolver.actions'; import { ResolvedAction } from '../core/resolving/resolver.actions';
import { map } from 'rxjs/operators';
import { hasValue } from '../shared/empty.util';
import { getItemPageRoute } from './item-page-routing-paths';
/** /**
* The self links defined in this list are expected to be requested somewhere in the near future * The self links defined in this list are expected to be requested somewhere in the near future
@@ -31,7 +34,8 @@ export const ITEM_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Item>[] = [
export class ItemPageResolver implements Resolve<RemoteData<Item>> { export class ItemPageResolver implements Resolve<RemoteData<Item>> {
constructor( constructor(
private itemService: ItemDataService, private itemService: ItemDataService,
private store: Store<any> private store: Store<any>,
private router: Router
) { ) {
} }
@@ -49,6 +53,18 @@ export class ItemPageResolver implements Resolve<RemoteData<Item>> {
...ITEM_PAGE_LINKS_TO_FOLLOW ...ITEM_PAGE_LINKS_TO_FOLLOW
).pipe( ).pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
map((rd: RemoteData<Item>) => {
if (rd.hasSucceeded && hasValue(rd.payload)) {
const itemRoute = getItemPageRoute(rd.payload);
const thisRoute = state.url;
if (!thisRoute.startsWith(itemRoute)) {
const itemId = rd.payload.uuid;
const subRoute = thisRoute.substring(thisRoute.indexOf(itemId) + itemId.length, thisRoute.length);
this.router.navigateByUrl(itemRoute + subRoute);
}
}
return rd;
})
); );
itemRD$.subscribe((itemRD: RemoteData<Item>) => { itemRD$.subscribe((itemRD: RemoteData<Item>) => {

View File

@@ -11,9 +11,10 @@ import { Item } from '../../core/shared/item.model';
import { MetadataService } from '../../core/metadata/metadata.service'; import { MetadataService } from '../../core/metadata/metadata.service';
import { fadeInOut } from '../../shared/animations/fade'; import { fadeInOut } from '../../shared/animations/fade';
import { redirectOn4xx } from '../../core/shared/operators'; import { getAllSucceededRemoteDataPayload, redirectOn4xx } from '../../core/shared/operators';
import { ViewMode } from '../../core/shared/view-mode.model'; import { ViewMode } from '../../core/shared/view-mode.model';
import { AuthService } from '../../core/auth/auth.service'; import { AuthService } from '../../core/auth/auth.service';
import { getItemPageRoute } from '../item-page-routing-paths';
/** /**
* This component renders a simple item page. * This component renders a simple item page.
@@ -44,6 +45,11 @@ export class ItemPageComponent implements OnInit {
*/ */
viewMode = ViewMode.StandalonePage; viewMode = ViewMode.StandalonePage;
/**
* Route to the item's page
*/
itemPageRoute$: Observable<string>;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
@@ -61,5 +67,9 @@ export class ItemPageComponent implements OnInit {
redirectOn4xx(this.router, this.authService) redirectOn4xx(this.router, this.authService)
); );
this.metadataService.processRemoteData(this.itemRD$); this.metadataService.processRemoteData(this.itemRD$);
this.itemPageRoute$ = this.itemRD$.pipe(
getAllSucceededRemoteDataPayload(),
map((item) => getItemPageRoute(item))
);
} }
} }

View File

@@ -3,7 +3,7 @@
{{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values> {{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2> </h2>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'items'" [dso]="object" [tooltipMsg]="'publication.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'publication.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -74,7 +74,7 @@
</ds-item-page-uri-field> </ds-item-page-uri-field>
<ds-item-page-collections [item]="object"></ds-item-page-collections> <ds-item-page-collections [item]="object"></ds-item-page-collections>
<div> <div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']"> <a class="btn btn-outline-primary" [routerLink]="[itemPageRoute + '/full']">
{{"item.page.link.full" | translate}} {{"item.page.link.full" | translate}}
</a> </a>
</div> </div>

View File

@@ -1,9 +1,10 @@
import { Component, Input } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service'; import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { Bitstream } from '../../../../core/shared/bitstream.model'; import { Bitstream } from '../../../../core/shared/bitstream.model';
import { Item } from '../../../../core/shared/item.model'; import { Item } from '../../../../core/shared/item.model';
import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
import { getItemPageRoute } from '../../../item-page-routing-paths';
@Component({ @Component({
selector: 'ds-item', selector: 'ds-item',
@@ -12,12 +13,21 @@ import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/oper
/** /**
* A generic component for displaying metadata and relations of an item * A generic component for displaying metadata and relations of an item
*/ */
export class ItemComponent { export class ItemComponent implements OnInit {
@Input() object: Item; @Input() object: Item;
/**
* Route to the item page
*/
itemPageRoute: string;
constructor(protected bitstreamDataService: BitstreamDataService) { constructor(protected bitstreamDataService: BitstreamDataService) {
} }
ngOnInit(): void {
this.itemPageRoute = getItemPageRoute(this.object);
}
// TODO refactor to return RemoteData, and thumbnail template to deal with loading // TODO refactor to return RemoteData, and thumbnail template to deal with loading
getThumbnail(): Observable<Bitstream> { getThumbnail(): Observable<Bitstream> {
return this.bitstreamDataService.getThumbnailFor(this.object).pipe( return this.bitstreamDataService.getThumbnailFor(this.object).pipe(

View File

@@ -3,7 +3,7 @@
<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values> <ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2> </h2>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'items'" [dso]="object" [tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -59,7 +59,7 @@
</ds-item-page-uri-field> </ds-item-page-uri-field>
<ds-item-page-collections [item]="object"></ds-item-page-collections> <ds-item-page-collections [item]="object"></ds-item-page-collections>
<div> <div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']"> <a class="btn btn-outline-primary" [routerLink]="[itemPageRoute + '/full']">
{{"item.page.link.full" | translate}} {{"item.page.link.full" | translate}}
</a> </a>
</div> </div>

View File

@@ -1,7 +1,14 @@
import { HostWindowService } from '../shared/host-window.service'; import { HostWindowService } from '../shared/host-window.service';
import { SidebarService } from '../shared/sidebar/sidebar.service'; import { SidebarService } from '../shared/sidebar/sidebar.service';
import { SearchComponent } from './search.component'; import { SearchComponent } from './search.component';
import { ChangeDetectionStrategy, Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; import {
ChangeDetectionStrategy,
Component,
Inject,
Input,
OnDestroy,
OnInit
} from '@angular/core';
import { pushInOut } from '../shared/animations/push'; import { pushInOut } from '../shared/animations/push';
import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component'; import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';

View File

@@ -53,7 +53,7 @@ export function getDSORoute(dso: DSpaceObject): string {
case Collection.type.value: case Collection.type.value:
return getCollectionPageRoute(dso.uuid); return getCollectionPageRoute(dso.uuid);
case Item.type.value: case Item.type.value:
return getItemPageRoute(dso.uuid); return getItemPageRoute(dso as Item);
} }
} }
} }

View File

@@ -86,6 +86,11 @@ import { ForbiddenComponent } from './forbidden/forbidden.component';
.then((m) => m.ItemPageModule), .then((m) => m.ItemPageModule),
canActivate: [EndUserAgreementCurrentUserGuard] canActivate: [EndUserAgreementCurrentUserGuard]
}, },
{ path: 'entities/:entity-type',
loadChildren: () => import('./+item-page/item-page.module')
.then((m) => m.ItemPageModule),
canActivate: [EndUserAgreementCurrentUserGuard]
},
{ {
path: BITSTREAM_MODULE_PATH, path: BITSTREAM_MODULE_PATH,
loadChildren: () => import('./+bitstream-page/bitstream-page.module') loadChildren: () => import('./+bitstream-page/bitstream-page.module')

View File

@@ -12,6 +12,7 @@ import { DsoRedirectDataService } from './dso-redirect-data.service';
import { GetRequest, IdentifierType } from './request.models'; import { GetRequest, IdentifierType } from './request.models';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils'; import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
import { Item } from '../shared/item.model';
describe('DsoRedirectDataService', () => { describe('DsoRedirectDataService', () => {
let scheduler: TestScheduler; let scheduler: TestScheduler;
@@ -48,10 +49,10 @@ describe('DsoRedirectDataService', () => {
navigate: jasmine.createSpy('navigate') navigate: jasmine.createSpy('navigate')
}; };
remoteData = createSuccessfulRemoteDataObject({ remoteData = createSuccessfulRemoteDataObject(Object.assign(new Item(), {
type: 'item', type: 'item',
uuid: '123456789' uuid: '123456789'
}); }));
rdbService = jasmine.createSpyObj('rdbService', { rdbService = jasmine.createSpyObj('rdbService', {
buildSingle: cold('a', { buildSingle: cold('a', {
@@ -114,7 +115,24 @@ describe('DsoRedirectDataService', () => {
redir.subscribe(); redir.subscribe();
scheduler.schedule(() => redir); scheduler.schedule(() => redir);
scheduler.flush(); scheduler.flush();
expect(router.navigate).toHaveBeenCalledWith([remoteData.payload.type + 's/' + remoteData.payload.uuid]); expect(router.navigate).toHaveBeenCalledWith(['/items/' + remoteData.payload.uuid]);
});
it('should navigate to entities route with the corresponding entity type', () => {
remoteData.payload.type = 'item';
remoteData.payload.metadata = {
'relationship.type': [
{
language: 'en_US',
value: 'Publication'
}
],
};
const redir = service.findByIdAndIDType(dsoHandle, IdentifierType.HANDLE);
// The framework would normally subscribe but do it here so we can test navigation.
redir.subscribe();
scheduler.schedule(() => redir);
scheduler.flush();
expect(router.navigate).toHaveBeenCalledWith(['/entities/publication/' + remoteData.payload.uuid]);
}); });
it('should navigate to collections route', () => { it('should navigate to collections route', () => {

View File

@@ -18,6 +18,8 @@ import { IdentifierType } from './request.models';
import { RequestService } from './request.service'; import { RequestService } from './request.service';
import { getFirstCompletedRemoteData } from '../shared/operators'; import { getFirstCompletedRemoteData } from '../shared/operators';
import { DSpaceObject } from '../shared/dspace-object.model'; import { DSpaceObject } from '../shared/dspace-object.model';
import { Item } from '../shared/item.model';
import { getItemPageRoute } from '../../+item-page/item-page-routing-paths';
@Injectable() @Injectable()
export class DsoRedirectDataService extends DataService<any> { export class DsoRedirectDataService extends DataService<any> {
@@ -60,10 +62,18 @@ export class DsoRedirectDataService extends DataService<any> {
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
tap((response) => { tap((response) => {
if (response.hasSucceeded) { if (response.hasSucceeded) {
const uuid = response.payload.uuid; const dso = response.payload;
const newRoute = this.getEndpointFromDSOType(response.payload.type); const uuid = dso.uuid;
if (hasValue(uuid) && hasValue(newRoute)) { if (hasValue(uuid)) {
this.router.navigate([newRoute + '/' + uuid]); let newRoute = this.getEndpointFromDSOType(response.payload.type);
if (dso.type.startsWith('item')) {
newRoute = getItemPageRoute(dso as Item);
} else if (hasValue(newRoute)) {
newRoute += '/' + uuid;
}
if (hasValue(newRoute)) {
this.router.navigate([newRoute]);
}
} }
} }
}) })

View File

@@ -1,5 +1,13 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse, HttpXsrfTokenExtractor } from '@angular/common/http'; import {
HttpErrorResponse,
HttpEvent,
HttpHandler,
HttpInterceptor,
HttpRequest,
HttpResponse,
HttpXsrfTokenExtractor
} from '@angular/common/http';
import { Observable } from 'rxjs/internal/Observable'; import { Observable } from 'rxjs/internal/Observable';
import { tap, catchError } from 'rxjs/operators'; import { tap, catchError } from 'rxjs/operators';
import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; import { RESTURLCombiner } from '../url-combiner/rest-url-combiner';

View File

@@ -5,7 +5,7 @@
</div> </div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width"> class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -36,7 +36,7 @@
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>

View File

@@ -2,9 +2,7 @@ import { Component } from '@angular/core';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { focusShadow } from '../../../../../shared/animations/focus'; import { focusShadow } from '../../../../../shared/animations/focus';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component'; import { ItemSearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
@listableObjectComponent('JournalIssueSearchResult', ViewMode.GridElement) @listableObjectComponent('JournalIssueSearchResult', ViewMode.GridElement)
@Component({ @Component({
@@ -16,5 +14,5 @@ import { Item } from '../../../../../core/shared/item.model';
/** /**
* The component for displaying a grid element for an item search result of the type Journal Issue * The component for displaying a grid element for an item search result of the type Journal Issue
*/ */
export class JournalIssueSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> { export class JournalIssueSearchResultGridElementComponent extends ItemSearchResultGridElementComponent {
} }

View File

@@ -5,7 +5,7 @@
</div> </div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width"> class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -36,7 +36,7 @@
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>

View File

@@ -1,10 +1,8 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { focusShadow } from '../../../../../shared/animations/focus'; import { focusShadow } from '../../../../../shared/animations/focus';
import { ItemSearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
@listableObjectComponent('JournalVolumeSearchResult', ViewMode.GridElement) @listableObjectComponent('JournalVolumeSearchResult', ViewMode.GridElement)
@Component({ @Component({
@@ -16,5 +14,5 @@ import { focusShadow } from '../../../../../shared/animations/focus';
/** /**
* The component for displaying a grid element for an item search result of the type Journal Volume * The component for displaying a grid element for an item search result of the type Journal Volume
*/ */
export class JournalVolumeSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> { export class JournalVolumeSearchResultGridElementComponent extends ItemSearchResultGridElementComponent {
} }

View File

@@ -5,7 +5,7 @@
</div> </div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width"> class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -40,7 +40,7 @@
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>

View File

@@ -2,9 +2,7 @@ import { Component } from '@angular/core';
import { focusShadow } from '../../../../../shared/animations/focus'; import { focusShadow } from '../../../../../shared/animations/focus';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component'; import { ItemSearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
@listableObjectComponent('JournalSearchResult', ViewMode.GridElement) @listableObjectComponent('JournalSearchResult', ViewMode.GridElement)
@Component({ @Component({
@@ -16,5 +14,5 @@ import { Item } from '../../../../../core/shared/item.model';
/** /**
* The component for displaying a grid element for an item search result of the type Journal * The component for displaying a grid element for an item search result of the type Journal
*/ */
export class JournalSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> { export class JournalSearchResultGridElementComponent extends ItemSearchResultGridElementComponent {
} }

View File

@@ -1,7 +1,7 @@
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" <a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead" [routerLink]="[itemPageRoute]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a> [innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None" <span *ngIf="linkType == linkTypes.None"
class="lead" class="lead"

View File

@@ -1,9 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component'; import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
@listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement) @listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement)
@Component({ @Component({
@@ -14,5 +12,5 @@ import { Item } from '../../../../../core/shared/item.model';
/** /**
* The component for displaying a list element for an item search result of the type Journal Issue * The component for displaying a list element for an item search result of the type Journal Issue
*/ */
export class JournalIssueSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> { export class JournalIssueSearchResultListElementComponent extends ItemSearchResultListElementComponent {
} }

View File

@@ -1,7 +1,7 @@
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" <a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead" [routerLink]="[itemPageRoute]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a> [innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None" <span *ngIf="linkType == linkTypes.None"
class="lead" class="lead"

View File

@@ -1,9 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
@listableObjectComponent('JournalVolumeSearchResult', ViewMode.ListElement) @listableObjectComponent('JournalVolumeSearchResult', ViewMode.ListElement)
@Component({ @Component({
@@ -14,5 +12,5 @@ import { ViewMode } from '../../../../../core/shared/view-mode.model';
/** /**
* The component for displaying a list element for an item search result of the type Journal Volume * The component for displaying a list element for an item search result of the type Journal Volume
*/ */
export class JournalVolumeSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> { export class JournalVolumeSearchResultListElementComponent extends ItemSearchResultListElementComponent {
} }

View File

@@ -1,7 +1,7 @@
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" <a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead" [routerLink]="[itemPageRoute]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a> [innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None" <span *ngIf="linkType == linkTypes.None"
class="lead" class="lead"

View File

@@ -1,9 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
@listableObjectComponent('JournalSearchResult', ViewMode.ListElement) @listableObjectComponent('JournalSearchResult', ViewMode.ListElement)
@Component({ @Component({
@@ -14,5 +12,5 @@ import { ViewMode } from '../../../../../core/shared/view-mode.model';
/** /**
* The component for displaying a list element for an item search result of the type Journal * The component for displaying a list element for an item search result of the type Journal
*/ */
export class JournalSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> { export class JournalSearchResultListElementComponent extends ItemSearchResultListElementComponent {
} }

View File

@@ -3,7 +3,7 @@
{{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values> {{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2> </h2>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'items'" [dso]="object" [tooltipMsg]="'journalissue.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalissue.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -53,7 +53,7 @@
[label]="'journalissue.page.keyword'"> [label]="'journalissue.page.keyword'">
</ds-generic-item-page-field> </ds-generic-item-page-field>
<div> <div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']"> <a class="btn btn-outline-primary" [routerLink]="[itemPageRoute + '/full']">
{{"item.page.link.full" | translate}} {{"item.page.link.full" | translate}}
</a> </a>
</div> </div>

View File

@@ -3,7 +3,7 @@
{{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values> {{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2> </h2>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'items'" [dso]="object" [tooltipMsg]="'journalvolume.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journalvolume.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -36,7 +36,7 @@
[label]="'journalvolume.page.description'"> [label]="'journalvolume.page.description'">
</ds-generic-item-page-field> </ds-generic-item-page-field>
<div> <div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']"> <a class="btn btn-outline-primary" [routerLink]="[itemPageRoute + '/full']">
{{"item.page.link.full" | translate}} {{"item.page.link.full" | translate}}
</a> </a>
</div> </div>

View File

@@ -3,7 +3,7 @@
{{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values> {{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2> </h2>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'items'" [dso]="object" [tooltipMsg]="'journal.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'journal.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -35,7 +35,7 @@
[label]="'journal.page.description'"> [label]="'journal.page.description'">
</ds-generic-item-page-field> </ds-generic-item-page-field>
<div> <div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']"> <a class="btn btn-outline-primary" [routerLink]="[itemPageRoute + '/full']">
{{"item.page.link.full" | translate}} {{"item.page.link.full" | translate}}
</a> </a>
</div> </div>

View File

@@ -5,7 +5,7 @@
</div> </div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width"> class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -42,7 +42,7 @@
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>

View File

@@ -2,9 +2,7 @@ import { Component } from '@angular/core';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { focusShadow } from '../../../../../shared/animations/focus'; import { focusShadow } from '../../../../../shared/animations/focus';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component'; import { ItemSearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
@listableObjectComponent('OrgUnitSearchResult', ViewMode.GridElement) @listableObjectComponent('OrgUnitSearchResult', ViewMode.GridElement)
@Component({ @Component({
@@ -16,5 +14,5 @@ import { Item } from '../../../../../core/shared/item.model';
/** /**
* The component for displaying a grid element for an item search result of the type Organisation Unit * The component for displaying a grid element for an item search result of the type Organisation Unit
*/ */
export class OrgUnitSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> { export class OrgUnitSearchResultGridElementComponent extends ItemSearchResultGridElementComponent {
} }

View File

@@ -5,7 +5,7 @@
</div> </div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width"> class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -36,7 +36,7 @@
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>

View File

@@ -2,9 +2,7 @@ import { Component } from '@angular/core';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { focusShadow } from '../../../../../shared/animations/focus'; import { focusShadow } from '../../../../../shared/animations/focus';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component'; import { ItemSearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
@listableObjectComponent('PersonSearchResult', ViewMode.GridElement) @listableObjectComponent('PersonSearchResult', ViewMode.GridElement)
@Component({ @Component({
@@ -16,5 +14,5 @@ import { Item } from '../../../../../core/shared/item.model';
/** /**
* The component for displaying a grid element for an item search result of the type Person * The component for displaying a grid element for an item search result of the type Person
*/ */
export class PersonSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> { export class PersonSearchResultGridElementComponent extends ItemSearchResultGridElementComponent {
} }

View File

@@ -5,7 +5,7 @@
</div> </div>
<a *ngIf="linkType != linkTypes.None" <a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width"> class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -30,7 +30,7 @@
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>

View File

@@ -1,10 +1,8 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
import { Item } from '../../../../../core/shared/item.model';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { focusShadow } from '../../../../../shared/animations/focus'; import { focusShadow } from '../../../../../shared/animations/focus';
import { ItemSearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
@listableObjectComponent('ProjectSearchResult', ViewMode.GridElement) @listableObjectComponent('ProjectSearchResult', ViewMode.GridElement)
@Component({ @Component({
@@ -16,5 +14,5 @@ import { focusShadow } from '../../../../../shared/animations/focus';
/** /**
* The component for displaying a grid element for an item search result of the type Project * The component for displaying a grid element for an item search result of the type Project
*/ */
export class ProjectSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> { export class ProjectSearchResultGridElementComponent extends ItemSearchResultGridElementComponent {
} }

View File

@@ -1,7 +1,7 @@
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" <a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead" [routerLink]="[itemPageRoute]" class="lead"
[innerHTML]="firstMetadataValue('organization.legalName')"></a> [innerHTML]="firstMetadataValue('organization.legalName')"></a>
<span *ngIf="linkType == linkTypes.None" <span *ngIf="linkType == linkTypes.None"
class="lead" class="lead"

View File

@@ -1,9 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component'; import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
@listableObjectComponent('OrgUnitSearchResult', ViewMode.ListElement) @listableObjectComponent('OrgUnitSearchResult', ViewMode.ListElement)
@Component({ @Component({
@@ -14,5 +12,5 @@ import { Item } from '../../../../../core/shared/item.model';
/** /**
* The component for displaying a list element for an item search result of the type Organisation Unit * The component for displaying a list element for an item search result of the type Organisation Unit
*/ */
export class OrgUnitSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> { export class OrgUnitSearchResultListElementComponent extends ItemSearchResultListElementComponent {
} }

View File

@@ -1,7 +1,7 @@
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" <a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead" [routerLink]="[itemPageRoute]" class="lead"
[innerHTML]="name"></a> [innerHTML]="name"></a>
<span *ngIf="linkType == linkTypes.None" <span *ngIf="linkType == linkTypes.None"
class="lead" class="lead"

View File

@@ -1,9 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { Item } from '../../../../../core/shared/item.model'; import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
@listableObjectComponent('PersonSearchResult', ViewMode.ListElement) @listableObjectComponent('PersonSearchResult', ViewMode.ListElement)
@Component({ @Component({
@@ -14,7 +12,7 @@ import { Item } from '../../../../../core/shared/item.model';
/** /**
* The component for displaying a list element for an item search result of the type Person * The component for displaying a list element for an item search result of the type Person
*/ */
export class PersonSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> { export class PersonSearchResultListElementComponent extends ItemSearchResultListElementComponent {
get name() { get name() {
return this.value ? return this.value ?

View File

@@ -1,7 +1,7 @@
<ds-truncatable [id]="dso.id"> <ds-truncatable [id]="dso.id">
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge> <ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" <a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead" [routerLink]="[itemPageRoute]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a> [innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None" <span *ngIf="linkType == linkTypes.None"
class="lead" class="lead"

View File

@@ -1,9 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator'; import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model'; import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
@listableObjectComponent('ProjectSearchResult', ViewMode.ListElement) @listableObjectComponent('ProjectSearchResult', ViewMode.ListElement)
@Component({ @Component({
@@ -14,5 +12,5 @@ import { ViewMode } from '../../../../../core/shared/view-mode.model';
/** /**
* The component for displaying a list element for an item search result of the type Project * The component for displaying a list element for an item search result of the type Project
*/ */
export class ProjectSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> { export class ProjectSearchResultListElementComponent extends ItemSearchResultListElementComponent {
} }

View File

@@ -3,7 +3,7 @@
{{'orgunit.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['organization.legalName'])"></ds-metadata-values> {{'orgunit.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['organization.legalName'])"></ds-metadata-values>
</h2> </h2>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'items'" [dso]="object" [tooltipMsg]="'orgunit.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'orgunit.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -39,7 +39,7 @@
[label]="'orgunit.page.description'"> [label]="'orgunit.page.description'">
</ds-generic-item-page-field> </ds-generic-item-page-field>
<div> <div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']"> <a class="btn btn-outline-primary" [routerLink]="[itemPageRoute + '/full']">
{{"item.page.link.full" | translate}} {{"item.page.link.full" | translate}}
</a> </a>
</div> </div>

View File

@@ -3,7 +3,7 @@
{{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="[object?.firstMetadata('person.familyName'), object?.firstMetadata('person.givenName')]" [separator]="', '"></ds-metadata-values> {{'person.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="[object?.firstMetadata('person.familyName'), object?.firstMetadata('person.givenName')]" [separator]="', '"></ds-metadata-values>
</h2> </h2>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'items'" [dso]="object" [tooltipMsg]="'person.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'person.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -52,7 +52,7 @@
[label]="'person.page.firstname'"> [label]="'person.page.firstname'">
</ds-generic-item-page-field> </ds-generic-item-page-field>
<div> <div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']"> <a class="btn btn-outline-primary" [routerLink]="[itemPageRoute + '/full']">
{{"item.page.link.full" | translate}} {{"item.page.link.full" | translate}}
</a> </a>
</div> </div>

View File

@@ -3,7 +3,7 @@
{{'project.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values> {{'project.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2> </h2>
<div class="pl-2"> <div class="pl-2">
<ds-dso-page-edit-button [pageRoutePrefix]="'items'" [dso]="object" [tooltipMsg]="'project.page.edit'"></ds-dso-page-edit-button> <ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'project.page.edit'"></ds-dso-page-edit-button>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -59,7 +59,7 @@
[label]="'project.page.keyword'"> [label]="'project.page.keyword'">
</ds-generic-item-page-field> </ds-generic-item-page-field>
<div> <div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']"> <a class="btn btn-outline-primary" [routerLink]="[itemPageRoute + '/full']">
{{"item.page.link.full" | translate}} {{"item.page.link.full" | translate}}
</a> </a>
</div> </div>

View File

@@ -6,7 +6,7 @@
</span> </span>
</ng-template> </ng-template>
<ds-truncatable [id]="metadataRepresentation.id"> <ds-truncatable [id]="metadataRepresentation.id">
<a [routerLink]="['/items/' + metadataRepresentation.id]" <a [routerLink]="[itemPageRoute]"
[innerHTML]="metadataRepresentation.getValue()" [innerHTML]="metadataRepresentation.getValue()"
[ngbTooltip]="metadataRepresentation.allMetadata(['dc.description']).length > 0 ? descTemplate : null"></a> [ngbTooltip]="metadataRepresentation.allMetadata(['dc.description']).length > 0 ? descTemplate : null"></a>
</ds-truncatable> </ds-truncatable>

View File

@@ -9,7 +9,7 @@
</span> </span>
</ng-template> </ng-template>
<ds-truncatable [id]="metadataRepresentation.id"> <ds-truncatable [id]="metadataRepresentation.id">
<a [routerLink]="['/items/' + metadataRepresentation.id]" <a [routerLink]="[itemPageRoute]"
[innerHTML]="metadataRepresentation.getValue()" [innerHTML]="metadataRepresentation.getValue()"
[ngbTooltip]="metadataRepresentation.allMetadata(['person.jobTitle']).length > 0 ? descTemplate : null"></a> [ngbTooltip]="metadataRepresentation.allMetadata(['person.jobTitle']).length > 0 ? descTemplate : null"></a>
</ds-truncatable> </ds-truncatable>

View File

@@ -1,5 +1,5 @@
<a *ngIf="isAuthorized$ | async" <a *ngIf="isAuthorized$ | async"
[routerLink]="['/' + pageRoutePrefix, dso.id, 'edit']" [routerLink]="[pageRoute, 'edit']"
class="edit-button btn btn-dark text-light btn-sm" class="edit-button btn btn-dark text-light btn-sm"
[ngbTooltip]="tooltipMsg | translate"> [ngbTooltip]="tooltipMsg | translate">
<i class="fas fa-pencil-alt fa-fw"></i> <i class="fas fa-pencil-alt fa-fw"></i>

View File

@@ -40,7 +40,7 @@ describe('DsoPageEditButtonComponent', () => {
fixture = TestBed.createComponent(DsoPageEditButtonComponent); fixture = TestBed.createComponent(DsoPageEditButtonComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.dso = dso; component.dso = dso;
component.pageRoutePrefix = 'test'; component.pageRoute = 'test';
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@@ -21,7 +21,7 @@ export class DsoPageEditButtonComponent implements OnInit {
/** /**
* The prefix of the route to the edit page (before the object's UUID, e.g. "items") * The prefix of the route to the edit page (before the object's UUID, e.g. "items")
*/ */
@Input() pageRoutePrefix: string; @Input() pageRoute: string;
/** /**
* A message for the tooltip on the button * A message for the tooltip on the button

View File

@@ -5,6 +5,7 @@ import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component'; import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component';
import { getItemEditRoute } from '../../../../+item-page/item-page-routing-paths'; import { getItemEditRoute } from '../../../../+item-page/item-page-routing-paths';
import { Item } from '../../../../core/shared/item.model';
/** /**
* Component to wrap a list of existing items inside a modal * Component to wrap a list of existing items inside a modal
@@ -28,6 +29,6 @@ export class EditItemSelectorComponent extends DSOSelectorModalWrapperComponent
* Navigate to the item edit page * Navigate to the item edit page
*/ */
navigate(dso: DSpaceObject) { navigate(dso: DSpaceObject) {
this.router.navigate([getItemEditRoute(dso.uuid)]); this.router.navigate([getItemEditRoute(dso as Item)]);
} }
} }

View File

@@ -25,7 +25,7 @@
<td class="version-row-element-version">{{version?.version}}</td> <td class="version-row-element-version">{{version?.version}}</td>
<td class="version-row-element-item"> <td class="version-row-element-item">
<span *ngVar="(version?.item | async)?.payload as item"> <span *ngVar="(version?.item | async)?.payload as item">
<a *ngIf="item" [routerLink]="['/items', item?.id]">{{item?.handle}}</a> <a *ngIf="item" [routerLink]="[(itemPageRoutes$ | async)[item?.id]]">{{item?.handle}}</a>
<span *ngIf="version?.id === itemVersion?.id">*</span> <span *ngIf="version?.id === itemVersion?.id">*</span>
</span> </span>
</td> </td>

View File

@@ -4,7 +4,11 @@ import { Version } from '../../../core/shared/version.model';
import { RemoteData } from '../../../core/data/remote-data'; import { RemoteData } from '../../../core/data/remote-data';
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { BehaviorSubject, combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { VersionHistory } from '../../../core/shared/version-history.model'; import { VersionHistory } from '../../../core/shared/version-history.model';
import { getAllSucceededRemoteData, getRemoteDataPayload } from '../../../core/shared/operators'; import {
getAllSucceededRemoteData,
getAllSucceededRemoteDataPayload,
getRemoteDataPayload
} from '../../../core/shared/operators';
import { map, startWith, switchMap } from 'rxjs/operators'; import { map, startWith, switchMap } from 'rxjs/operators';
import { PaginatedList } from '../../../core/data/paginated-list.model'; import { PaginatedList } from '../../../core/data/paginated-list.model';
import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model'; import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model';
@@ -13,6 +17,7 @@ import { PaginatedSearchOptions } from '../../search/paginated-search-options.mo
import { AlertType } from '../../alert/aletr-type'; import { AlertType } from '../../alert/aletr-type';
import { followLink } from '../../utils/follow-link-config.model'; import { followLink } from '../../utils/follow-link-config.model';
import { hasValueOperator } from '../../empty.util'; import { hasValueOperator } from '../../empty.util';
import { getItemPageRoute } from '../../../+item-page/item-page-routing-paths';
@Component({ @Component({
selector: 'ds-item-versions', selector: 'ds-item-versions',
@@ -86,6 +91,15 @@ export class ItemVersionsComponent implements OnInit {
*/ */
currentPage$ = new BehaviorSubject<number>(1); currentPage$ = new BehaviorSubject<number>(1);
/**
* The routes to the versions their item pages
* Key: Item ID
* Value: Route to item page
*/
itemPageRoutes$: Observable<{
[itemId: string]: string
}>;
constructor(private versionHistoryService: VersionHistoryDataService) { constructor(private versionHistoryService: VersionHistoryDataService) {
} }
@@ -118,6 +132,15 @@ export class ItemVersionsComponent implements OnInit {
map((versions: PaginatedList<Version>) => versions.page.filter((version: Version) => version.eperson !== undefined).length > 0), map((versions: PaginatedList<Version>) => versions.page.filter((version: Version) => version.eperson !== undefined).length > 0),
startWith(false) startWith(false)
); );
this.itemPageRoutes$ = this.versionsRD$.pipe(
getAllSucceededRemoteDataPayload(),
switchMap((versions) => observableCombineLatest(...versions.page.map((version) => version.item.pipe(getAllSucceededRemoteDataPayload())))),
map((versions) => {
const itemPageRoutes = {};
versions.forEach((item) => itemPageRoutes[item.uuid] = getItemPageRoute(item));
return itemPageRoutes;
})
);
} }
/** /**

View File

@@ -109,7 +109,7 @@ export class ItemVersionsNoticeComponent implements OnInit {
*/ */
getItemPage(item: Item): string { getItemPage(item: Item): string {
if (hasValue(item)) { if (hasValue(item)) {
return getItemPageRoute(item.id); return getItemPageRoute(item);
} }
} }
} }

View File

@@ -0,0 +1,198 @@
{
"id": "0136fe00-43ca-439e-ae6c-b2b08c2b8f5e",
"uuid": "0136fe00-43ca-439e-ae6c-b2b08c2b8f5e",
"name": "The pathogenesis of dengue",
"handle": "123456789/156",
"metadata": {
"dc.contributor.author": [
{
"value": "Simmons, Cameron",
"language": null,
"authority": "virtual::1586",
"confidence": -1,
"place": 0
}
],
"dc.date.accessioned": [
{
"value": "2018-09-14T11:15:00Z",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.date.available": [
{
"value": "2017-07-12T04:44:34Z",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 1
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 2
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 3
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 4
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 5
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 6
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 7
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 8
},
{
"value": "2018-09-14T11:15:00Z",
"language": null,
"authority": null,
"confidence": -1,
"place": 9
}
],
"dc.date.issued": [
{
"value": "2011-09-23",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.description.abstract": [
{
"value": "Dengue is an important cause of childhood and adult morbidity in Asian and Latin American countries and its geographic footprint is growing. The clinical manifestations of dengue are the expression of a constellation of host and viral factors, some acquired, others intrinsic to the individual. The virulence of the virus plus the flavivirus infection history, age, gender and genotype of the host all appear to help shape the severity of infection. Similarly, the characteristics of the innate and acquired host immune response subsequent to infection are also likely determinants of outcome. This review summarises recent developments in the understanding of dengue pathogenesis and their relevance to dengue vaccine development.",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.identifier.uri": [
{
"value": "http://localhost:8080/dspace7/handle/123456789/156",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.language": [
{
"value": "English",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.title": [
{
"value": "The pathogenesis of dengue",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.type": [
{
"value": "Journal Article",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
}
],
"relation.isAuthorOfPublication": [
{
"value": "b1b2c768-bda1-448a-a073-fc541e8b24d9",
"language": null,
"authority": "virtual::1586",
"confidence": -1,
"place": 0
}
],
"relationship.type": [
{
"value": "Publication",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
}
]
},
"inArchive": true,
"discoverable": true,
"withdrawn": false,
"lastModified": "2019-05-06T15:24:25.037+0000",
"type": "item",
"_links": {
"bundles": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/bundles"
},
"mappedCollections": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/mappedCollections"
},
"owningCollection": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/owningCollection"
},
"relationships": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/relationships"
},
"version": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/version"
},
"templateItemOf": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/templateItemOf"
},
"self": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e"
}
}
}

View File

@@ -0,0 +1,180 @@
{
"id": "0136fe00-43ca-439e-ae6c-b2b08c2b8f5e",
"uuid": "0136fe00-43ca-439e-ae6c-b2b08c2b8f5e",
"name": "The pathogenesis of dengue",
"handle": "123456789/156",
"metadata": {
"dc.contributor.author": [
{
"value": "Simmons, Cameron",
"language": null,
"authority": "null",
"confidence": -1,
"place": 0
}
],
"dc.date.accessioned": [
{
"value": "2018-09-14T11:15:00Z",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.date.available": [
{
"value": "2017-07-12T04:44:34Z",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 1
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 2
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 3
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 4
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 5
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 6
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 7
},
{
"value": "2011-07-06",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 8
},
{
"value": "2018-09-14T11:15:00Z",
"language": null,
"authority": null,
"confidence": -1,
"place": 9
}
],
"dc.date.issued": [
{
"value": "2011-09-23",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.description.abstract": [
{
"value": "Dengue is an important cause of childhood and adult morbidity in Asian and Latin American countries and its geographic footprint is growing. The clinical manifestations of dengue are the expression of a constellation of host and viral factors, some acquired, others intrinsic to the individual. The virulence of the virus plus the flavivirus infection history, age, gender and genotype of the host all appear to help shape the severity of infection. Similarly, the characteristics of the innate and acquired host immune response subsequent to infection are also likely determinants of outcome. This review summarises recent developments in the understanding of dengue pathogenesis and their relevance to dengue vaccine development.",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.identifier.uri": [
{
"value": "http://localhost:8080/dspace7/handle/123456789/156",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.language": [
{
"value": "English",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.title": [
{
"value": "The pathogenesis of dengue",
"language": "en_US",
"authority": null,
"confidence": -1,
"place": 0
}
],
"dc.type": [
{
"value": "Journal Article",
"language": null,
"authority": null,
"confidence": -1,
"place": 0
}
]
},
"inArchive": true,
"discoverable": true,
"withdrawn": false,
"lastModified": "2019-05-06T15:24:25.037+0000",
"type": "item",
"_links": {
"bundles": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/bundles"
},
"mappedCollections": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/mappedCollections"
},
"owningCollection": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/owningCollection"
},
"relationships": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/relationships"
},
"version": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/version"
},
"templateItemOf": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e/templateItemOf"
},
"self": {
"href": "https://dspace7.4science.cloud/server/api/core/items/0136fe00-43ca-439e-ae6c-b2b08c2b8f5e"
}
}
}

View File

@@ -1,5 +1,7 @@
import { InjectionToken } from '@angular/core'; import { InjectionToken } from '@angular/core';
// import mockSubmissionResponse from './mock-submission-response.json'; // import mockSubmissionResponse from './mock-submission-response.json';
// import mockPublicationResponse from './mock-publication-response.json';
// import mockUntypedItemResponse from './mock-untyped-item-response.json';
export class ResponseMapMock extends Map<string, any> {} export class ResponseMapMock extends Map<string, any> {}
@@ -12,4 +14,6 @@ export const MOCK_RESPONSE_MAP: InjectionToken<ResponseMapMock> = new InjectionT
*/ */
export const mockResponseMap: ResponseMapMock = new Map([ export const mockResponseMap: ResponseMapMock = new Map([
// [ '/config/submissionforms/traditionalpageone', mockSubmissionResponse ] // [ '/config/submissionforms/traditionalpageone', mockSubmissionResponse ]
// [ '/api/pid/find', mockPublicationResponse ],
// [ '/api/pid/find', mockUntypedItemResponse ],
]); ]);

View File

@@ -1,15 +1,15 @@
import { Component, Injector, OnDestroy } from '@angular/core'; import { Component, Injector, OnDestroy } from '@angular/core';
import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
import { DSpaceObject} from '../../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
import { Router} from '@angular/router'; import { Router } from '@angular/router';
import { NotificationsService} from '../../../notifications/notifications.service'; import { NotificationsService } from '../../../notifications/notifications.service';
import { TranslateService} from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { SearchService} from '../../../../core/shared/search/search.service'; import { SearchService } from '../../../../core/shared/search/search.service';
import { RequestService} from '../../../../core/data/request.service'; import { RequestService } from '../../../../core/data/request.service';
import { Observable} from 'rxjs'; import { Observable } from 'rxjs';
import { RemoteData} from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { WorkflowItem} from '../../../../core/submission/models/workflowitem.model'; import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model';
import { switchMap, take } from 'rxjs/operators'; import { switchMap, take } from 'rxjs/operators';
import { CLAIMED_TASK } from '../../../../core/tasks/models/claimed-task-object.resource-type'; import { CLAIMED_TASK } from '../../../../core/tasks/models/claimed-task-object.resource-type';
import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators'; import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';

View File

@@ -1,4 +1,4 @@
import {Component, Injector} from '@angular/core'; import { Component, Injector } from '@angular/core';
import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component';
import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';

View File

@@ -1,5 +1,5 @@
<button class="btn btn-primary mt-1 mb-3" <button class="btn btn-primary mt-1 mb-3"
ngbTooltip="{{'submission.workflow.generic.view-help' | translate}}" ngbTooltip="{{'submission.workflow.generic.view-help' | translate}}"
[routerLink]="['/items/' + object.id]"> [routerLink]="[itemPageRoute]">
<i class="fa fa-info-circle"></i> {{"submission.workflow.generic.view" | translate}} <i class="fa fa-info-circle"></i> {{"submission.workflow.generic.view" | translate}}
</button> </button>

View File

@@ -1,14 +1,13 @@
import { Component, Injector, Input } from '@angular/core'; import { Component, Injector, Input, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { MyDSpaceActionsComponent } from '../mydspace-actions';
import { ItemDataService } from '../../../core/data/item-data.service'; import { ItemDataService } from '../../../core/data/item-data.service';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { NotificationsService } from '../../notifications/notifications.service'; import { NotificationsService } from '../../notifications/notifications.service';
import { RequestService } from '../../../core/data/request.service'; import { RequestService } from '../../../core/data/request.service';
import { SearchService } from '../../../core/shared/search/search.service'; import { SearchService } from '../../../core/shared/search/search.service';
import { getItemPageRoute } from '../../../+item-page/item-page-routing-paths';
/** /**
* This component represents mydspace actions related to Item object. * This component represents mydspace actions related to Item object.
@@ -19,13 +18,18 @@ import { SearchService } from '../../../core/shared/search/search.service';
templateUrl: './item-actions.component.html', templateUrl: './item-actions.component.html',
}) })
export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDataService> { export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDataService> implements OnInit {
/** /**
* The Item object * The Item object
*/ */
@Input() object: Item; @Input() object: Item;
/**
* Route to the item's page
*/
itemPageRoute: string;
/** /**
* Initialize instance variables * Initialize instance variables
* *
@@ -45,6 +49,10 @@ export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDat
super(Item.type, injector, router, notificationsService, translate, searchService, requestService); super(Item.type, injector, router, notificationsService, translate, searchService, requestService);
} }
ngOnInit(): void {
this.initPageRoute();
}
/** /**
* Init the target object * Init the target object
* *
@@ -52,6 +60,14 @@ export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDat
*/ */
initObjects(object: Item) { initObjects(object: Item) {
this.object = object; this.object = object;
this.initPageRoute();
}
/**
* Initialise the route to the item's page
*/
initPageRoute() {
this.itemPageRoute = getItemPageRoute(this.object);
} }
} }

View File

@@ -1,5 +1,5 @@
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
import {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing'; import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
@@ -12,7 +12,10 @@ import { RouterStub } from '../testing/router.stub';
import { getMockSearchService } from '../mocks/search-service.mock'; import { getMockSearchService } from '../mocks/search-service.mock';
import { getMockRequestService } from '../mocks/request.service.mock'; import { getMockRequestService } from '../mocks/request.service.mock';
import { Item } from '../../core/shared/item.model'; import { Item } from '../../core/shared/item.model';
import { createFailedRemoteDataObject, createSuccessfulRemoteDataObject } from '../remote-data.utils'; import {
createFailedRemoteDataObject,
createSuccessfulRemoteDataObject
} from '../remote-data.utils';
import { WorkflowItem } from '../../core/submission/models/workflowitem.model'; import { WorkflowItem } from '../../core/submission/models/workflowitem.model';
import { TranslateLoaderMock } from '../mocks/translate-loader.mock'; import { TranslateLoaderMock } from '../mocks/translate-loader.mock';
import { NotificationsService } from '../notifications/notifications.service'; import { NotificationsService } from '../notifications/notifications.service';

View File

@@ -3,7 +3,7 @@
<div class="position-absolute ml-1"> <div class="position-absolute ml-1">
<ng-content></ng-content> <ng-content></ng-content>
</div> </div>
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width"> class="card-img-top full-width">
<div> <div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async"> <ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -36,7 +36,7 @@
</ds-truncatable-part> </ds-truncatable-part>
</p> </p>
<div *ngIf="linkType != linkTypes.None" class="text-center"> <div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" <a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="lead btn btn-primary viewButton">View</a> class="lead btn btn-primary viewButton">View</a>
</div> </div>
</div> </div>

View File

@@ -5,6 +5,7 @@ import { listableObjectComponent } from '../../../../object-collection/shared/li
import { SearchResultGridElementComponent } from '../../search-result-grid-element.component'; import { SearchResultGridElementComponent } from '../../search-result-grid-element.component';
import { Item } from '../../../../../core/shared/item.model'; import { Item } from '../../../../../core/shared/item.model';
import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model'; import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
import { getItemPageRoute } from '../../../../../+item-page/item-page-routing-paths';
@listableObjectComponent('PublicationSearchResult', ViewMode.GridElement) @listableObjectComponent('PublicationSearchResult', ViewMode.GridElement)
@listableObjectComponent(ItemSearchResult, ViewMode.GridElement) @listableObjectComponent(ItemSearchResult, ViewMode.GridElement)
@@ -18,4 +19,13 @@ import { ItemSearchResult } from '../../../../object-collection/shared/item-sear
* The component for displaying a grid element for an item search result of the type Publication * The component for displaying a grid element for an item search result of the type Publication
*/ */
export class ItemSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> { export class ItemSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> {
/**
* Route to the item's page
*/
itemPageRoute: string;
ngOnInit(): void {
super.ngOnInit();
this.itemPageRoute = getItemPageRoute(this.dso);
}
} }

View File

@@ -1,6 +1,7 @@
import { MetadataRepresentationListElementComponent } from '../metadata-representation-list-element.component'; import { MetadataRepresentationListElementComponent } from '../metadata-representation-list-element.component';
import { Component } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
import { getItemPageRoute } from '../../../../+item-page/item-page-routing-paths';
@Component({ @Component({
selector: 'ds-item-metadata-representation-list-element', selector: 'ds-item-metadata-representation-list-element',
@@ -9,6 +10,15 @@ import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-rep
/** /**
* An abstract class for displaying a single ItemMetadataRepresentation * An abstract class for displaying a single ItemMetadataRepresentation
*/ */
export class ItemMetadataRepresentationListElementComponent extends MetadataRepresentationListElementComponent { export class ItemMetadataRepresentationListElementComponent extends MetadataRepresentationListElementComponent implements OnInit {
metadataRepresentation: ItemMetadataRepresentation; metadataRepresentation: ItemMetadataRepresentation;
/**
* Route to the item's page
*/
itemPageRoute: string;
ngOnInit(): void {
this.itemPageRoute = getItemPageRoute(this.metadataRepresentation);
}
} }

View File

@@ -10,7 +10,7 @@ import { RemoteData } from '../../../../../core/data/remote-data';
import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model'; import { WorkflowItem } from '../../../../../core/submission/models/workflowitem.model';
import { followLink } from '../../../../utils/follow-link-config.model'; import { followLink } from '../../../../utils/follow-link-config.model';
import { SearchResultListElementComponent } from '../../../search-result-list-element/search-result-list-element.component'; import { SearchResultListElementComponent } from '../../../search-result-list-element/search-result-list-element.component';
import { ClaimedTaskSearchResult} from '../../../../object-collection/shared/claimed-task-search-result.model'; import { ClaimedTaskSearchResult } from '../../../../object-collection/shared/claimed-task-search-result.model';
import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model'; import { ClaimedTask } from '../../../../../core/tasks/models/claimed-task-object.model';
/** /**

View File

@@ -2,7 +2,7 @@
<ds-truncatable [id]="dso.id" *ngIf="object !== undefined && object !== null"> <ds-truncatable [id]="dso.id" *ngIf="object !== undefined && object !== null">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" <a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead" [routerLink]="[itemPageRoute]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a> [innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None" class="lead" <span *ngIf="linkType == linkTypes.None" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span> [innerHTML]="firstMetadataValue('dc.title')"></span>

View File

@@ -4,6 +4,7 @@ import { ViewMode } from '../../../../../../core/shared/view-mode.model';
import { ItemSearchResult } from '../../../../../object-collection/shared/item-search-result.model'; import { ItemSearchResult } from '../../../../../object-collection/shared/item-search-result.model';
import { SearchResultListElementComponent } from '../../../search-result-list-element.component'; import { SearchResultListElementComponent } from '../../../search-result-list-element.component';
import { Item } from '../../../../../../core/shared/item.model'; import { Item } from '../../../../../../core/shared/item.model';
import { getItemPageRoute } from '../../../../../../+item-page/item-page-routing-paths';
@listableObjectComponent('PublicationSearchResult', ViewMode.ListElement) @listableObjectComponent('PublicationSearchResult', ViewMode.ListElement)
@listableObjectComponent(ItemSearchResult, ViewMode.ListElement) @listableObjectComponent(ItemSearchResult, ViewMode.ListElement)
@@ -16,4 +17,13 @@ import { Item } from '../../../../../../core/shared/item.model';
* The component for displaying a list element for an item search result of the type Publication * The component for displaying a list element for an item search result of the type Publication
*/ */
export class ItemSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> { export class ItemSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
/**
* Route to the item's page
*/
itemPageRoute: string;
ngOnInit(): void {
super.ngOnInit();
this.itemPageRoute = getItemPageRoute(this.dso);
}
} }

View File

@@ -26,7 +26,7 @@
</span> </span>
</td> </td>
<td><span *ngIf="item.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])">{{item.firstMetadataValue(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])}}</span></td> <td><span *ngIf="item.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])">{{item.firstMetadataValue(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])}}</span></td>
<td><a [routerLink]="['/items', item.id]">{{item.firstMetadataValue("dc.title")}}</a></td> <td><a [routerLink]="[(itemPageRoutes$ | async)[item.id]]">{{item.firstMetadataValue("dc.title")}}</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -2,7 +2,11 @@ import { Component, Input } from '@angular/core';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { ObjectSelectService } from '../object-select.service'; import { ObjectSelectService } from '../object-select.service';
import { ObjectSelectComponent } from '../object-select/object-select.component'; import { ObjectSelectComponent } from '../object-select/object-select.component';
import { isNotEmpty } from '../../empty.util'; import { hasValueOperator, isNotEmpty } from '../../empty.util';
import { Observable } from 'rxjs';
import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators';
import { map } from 'rxjs/operators';
import { getItemPageRoute } from '../../../+item-page/item-page-routing-paths';
@Component({ @Component({
selector: 'ds-item-select', selector: 'ds-item-select',
@@ -20,6 +24,15 @@ export class ItemSelectComponent extends ObjectSelectComponent<Item> {
@Input() @Input()
hideCollection = false; hideCollection = false;
/**
* The routes to the items their pages
* Key: Item ID
* Value: Route to item page
*/
itemPageRoutes$: Observable<{
[itemId: string]: string
}>;
constructor(protected objectSelectService: ObjectSelectService) { constructor(protected objectSelectService: ObjectSelectService) {
super(objectSelectService); super(objectSelectService);
} }
@@ -29,6 +42,15 @@ export class ItemSelectComponent extends ObjectSelectComponent<Item> {
if (!isNotEmpty(this.confirmButton)) { if (!isNotEmpty(this.confirmButton)) {
this.confirmButton = 'item.select.confirm'; this.confirmButton = 'item.select.confirm';
} }
this.itemPageRoutes$ = this.dsoRD$.pipe(
hasValueOperator(),
getAllSucceededRemoteDataPayload(),
map((items) => {
const itemPageRoutes = {};
items.page.forEach((item) => itemPageRoutes[item.uuid] = getItemPageRoute(item));
return itemPageRoutes;
})
);
} }
} }

View File

@@ -19,7 +19,11 @@ import { hasValue, isNotEmpty, isUndefined } from '../empty.util';
import { UploaderService } from './uploader.service'; import { UploaderService } from './uploader.service';
import { UploaderProperties } from './uploader-properties.model'; import { UploaderProperties } from './uploader-properties.model';
import { HttpXsrfTokenExtractor } from '@angular/common/http'; import { HttpXsrfTokenExtractor } from '@angular/common/http';
import { XSRF_REQUEST_HEADER, XSRF_RESPONSE_HEADER, XSRF_COOKIE } from '../../core/xsrf/xsrf.interceptor'; import {
XSRF_REQUEST_HEADER,
XSRF_RESPONSE_HEADER,
XSRF_COOKIE
} from '../../core/xsrf/xsrf.interceptor';
import { CookieService } from '../../core/services/cookie.service'; import { CookieService } from '../../core/services/cookie.service';
@Component({ @Component({

View File

@@ -1,8 +1,11 @@
import { GoogleAnalyticsService } from './google-analytics.service'; import { GoogleAnalyticsService } from './google-analytics.service';
import {Angulartics2GoogleAnalytics} from 'angulartics2/ga'; import { Angulartics2GoogleAnalytics } from 'angulartics2/ga';
import {ConfigurationDataService} from '../core/data/configuration-data.service'; import { ConfigurationDataService } from '../core/data/configuration-data.service';
import {createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$} from '../shared/remote-data.utils'; import {
import {ConfigurationProperty} from '../core/shared/configuration-property.model'; createFailedRemoteDataObject$,
createSuccessfulRemoteDataObject$
} from '../shared/remote-data.utils';
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
describe('GoogleAnalyticsService', () => { describe('GoogleAnalyticsService', () => {
const trackingIdProp = 'google.analytics.key'; const trackingIdProp = 'google.analytics.key';

Some files were not shown because too many files have changed in this diff Show More