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,
Subscription,
BehaviorSubject,
combineLatest as observableCombineLatest, ObservedValueOf,
combineLatest as observableCombineLatest,
ObservedValueOf,
} from 'rxjs';
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 { EPersonDataService } from '../../../../../core/eperson/eperson-data.service';
import { GroupDataService } from '../../../../../core/eperson/group-data.service';
@@ -19,11 +20,12 @@ import { Group } from '../../../../../core/eperson/models/group.model';
import {
getRemoteDataPayload,
getFirstSucceededRemoteData,
getFirstCompletedRemoteData, getAllCompletedRemoteData
getFirstCompletedRemoteData,
getAllCompletedRemoteData
} from '../../../../../core/shared/operators';
import { NotificationsService } from '../../../../../shared/notifications/notifications.service';
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

View File

@@ -56,19 +56,19 @@ describe('ItemAdminSearchResultActionsComponent', () => {
it('should render an edit button with the correct link', () => {
const button = fixture.debugElement.query(By.css('a.edit-link'));
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', () => {
const button = fixture.debugElement.query(By.css('a.delete-link'));
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', () => {
const a = fixture.debugElement.query(By.css('a.move-link'));
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', () => {
@@ -80,7 +80,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
it('should render a withdraw button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.withdraw-link'));
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', () => {
@@ -103,7 +103,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
it('should render a reinstate button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.reinstate-link'));
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', () => {
const a = fixture.debugElement.query(By.css('a.private-link'));
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', () => {
@@ -139,7 +139,7 @@ describe('ItemAdminSearchResultActionsComponent', () => {
it('should render a make private button with the correct link', () => {
const a = fixture.debugElement.query(By.css('a.public-link'));
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
*/
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 { FileSizePipe } from '../../shared/utils/file-size-pipe';
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 { 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 { Item } from '../../core/shared/item.model';
const infoNotification: INotification = new Notification('id', NotificationType.Info, 'info');
const warningNotification: INotification = new Notification('id', NotificationType.Warning, 'warning');
@@ -109,9 +113,9 @@ describe('EditBitstreamPageComponent', () => {
self: 'bitstream-selflink'
},
bundle: createSuccessfulRemoteDataObject$({
item: createSuccessfulRemoteDataObject$({
item: createSuccessfulRemoteDataObject$(Object.assign(new Item(), {
uuid: 'some-uuid'
})
}))
})
});
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', () => {
comp.itemId = 'some-uuid1';
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', () => {
it('should redirect to the item edit page on the bitstreams tab with the itemId from the bundle links ', () => {
comp.itemId = undefined;
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 { RemoteData } from '../../core/data/remote-data';
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 { Item } from '../../core/shared/item.model';
@Component({
selector: 'ds-edit-bitstream-page',
@@ -264,9 +263,17 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
/**
* The ID 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
*/
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
* @type {Array}
@@ -293,6 +300,7 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
this.formGroup = this.formService.createFormGroup(this.formModel);
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.bitstreamFormatsRD$ = this.bitstreamFormatService.findAll(this.findAllOptions);
@@ -499,10 +507,10 @@ export class EditBitstreamPageComponent implements OnInit, OnDestroy {
*/
navigateToItemEditBitstreams() {
if (hasValue(this.itemId)) {
this.router.navigate([getItemEditRoute(this.itemId), 'bitstreams']);
this.router.navigate([getEntityEditRoute(this.entityType, this.itemId), 'bitstreams']);
} else {
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) => {
this.router.navigate(([getItemEditRoute(item), 'bitstreams']));
});

View File

@@ -35,7 +35,7 @@
</ds-comcol-page-content>
</header>
<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>
<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 { DSpaceObjectType } from '../core/shared/dspace-object-type.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 { hasValue, isNotEmpty } from '../shared/empty.util';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { AuthService } from '../core/auth/auth.service';
import {PaginationChangeEvent} from '../shared/pagination/paginationChangeEvent.interface';
import { getCollectionPageRoute } from './collection-page-routing-paths';
@Component({
selector: 'ds-collection-page',
@@ -44,6 +50,11 @@ export class CollectionPageComponent implements OnInit {
sortConfig: SortOptions
}>;
/**
* Route to the community page
*/
collectionPageRoute$: Observable<string>;
constructor(
private collectionDataService: CollectionDataService,
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.metadata.processRemoteData(this.collectionRD$);
});

View File

@@ -21,7 +21,7 @@
</ds-comcol-page-content>
</header>
<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>
<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 { 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 { getCommunityPageRoute } from './community-page-routing-paths';
@Component({
selector: 'ds-community-page',
@@ -36,6 +37,12 @@ export class CommunityPageComponent implements OnInit {
* The logo of this community
*/
logoRD$: Observable<RemoteData<Bitstream>>;
/**
* Route to the community page
*/
communityPageRoute$: Observable<string>;
constructor(
private communityDataService: CommunityDataService,
private metadata: MetadataService,
@@ -55,6 +62,10 @@ export class CommunityPageComponent implements OnInit {
map((rd: RemoteData<Community>) => rd.payload),
filter((community: Community) => hasValue(community)),
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 { RequestService } from '../../../core/data/request.service';
import { getBitstreamModuleRoute } from '../../../app-routing-paths';
import { getItemEditRoute } from '../../item-page-routing-paths';
import { getEntityEditRoute } from '../../item-page-routing-paths';
@Component({
selector: 'ds-upload-bitstream',
@@ -37,6 +37,12 @@ export class UploadBitstreamComponent implements OnInit, OnDestroy {
*/
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
*/
@@ -100,6 +106,7 @@ export class UploadBitstreamComponent implements OnInit, OnDestroy {
*/
ngOnInit(): void {
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.bundlesRD$ = this.itemRD$.pipe(
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
const queryParams = { itemId: this.itemId };
const queryParams = { itemId: this.itemId, entityType: this.entityType };
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
*/
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 { AbstractTrackableComponent } from '../../../shared/trackable/abstract-trackable.component';
import { environment } from '../../../../environments/environment';
import { getItemPageRoute } from '../../item-page-routing-paths';
import { ITEM_PAGE_LINKS_TO_FOLLOW } from '../../item-page.resolver';
import { getAllSucceededRemoteData } from '../../../core/shared/operators';
import { hasValue } from '../../../shared/empty.util';
@@ -36,6 +37,11 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
*/
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
* This is used to update the item in cache after bitstreams are deleted
@@ -69,6 +75,7 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
getAllSucceededRemoteData()
).subscribe((rd: RemoteData<Item>) => {
this.item = rd.payload;
this.itemPageRoute = getItemPageRoute(this.item);
this.postItemInit();
this.initializeUpdates();
});

View File

@@ -55,6 +55,6 @@ export class EditItemPageComponent implements OnInit {
* @param item The item for which the url is requested
*/
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="button-row top d-flex mt-2">
<button class="mr-auto btn btn-success"
[routerLink]="['/items/', item.id, 'bitstreams', 'new']"><i
[routerLink]="[itemPageRoute, 'bitstreams', 'new']"><i
class="fas fa-upload"></i>
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.upload-button" | translate}}</span>
</button>

View File

@@ -8,7 +8,7 @@
</div>
<div class="{{columnSizes.columns[3].buildClasses()}} text-center row-element">
<div class="btn-group bundle-action-buttons">
<button [routerLink]="['/items/', item.id, 'bitstreams', 'new']"
<button [routerLink]="[itemPageRoute, 'bitstreams', 'new']"
[queryParams]="{bundle: bundle.id}"
class="btn btn-outline-success btn-sm"
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 { ResponsiveColumnSizes } from '../../../../shared/responsive-table-sizes/responsive-column-sizes';
import { ResponsiveTableSizes } from '../../../../shared/responsive-table-sizes/responsive-table-sizes';
import { getItemPageRoute } from '../../../item-page-routing-paths';
@Component({
selector: 'ds-item-edit-bitstream-bundle',
@@ -49,11 +50,17 @@ export class ItemEditBitstreamBundleComponent implements OnInit {
*/
bundleNameColumn: ResponsiveColumnSizes;
/**
* Route to the item's page
*/
itemPageRoute: string;
constructor(private viewContainerRef: ViewContainerRef) {
}
ngOnInit(): void {
this.bundleNameColumn = this.columnSizes.combineColumns(0, 2);
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 mockItem: Item = Object.assign(new Item(), {
id: '932c7d50-d85a-44cb-b9dc-b427b12877bd',
uuid: '932c7d50-d85a-44cb-b9dc-b427b12877bd',
name: 'test-item'
});
const mockItemRD: RemoteData<Item> = createSuccessfulRemoteDataObject(mockItem);
@@ -212,7 +213,7 @@ describe('ItemCollectionMapperComponent', () => {
});
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 { SearchService } from '../../../core/shared/search/search.service';
import { NoContent } from '../../../core/shared/NoContent.model';
import { getItemPageRoute } from '../../item-page-routing-paths';
@Component({
selector: 'ds-item-collection-mapper',
@@ -312,7 +313,7 @@ export class ItemCollectionMapperComponent implements OnInit {
getRemoteDataPayload(),
take(1)
).subscribe((item: Item) => {
this.router.navigate(['/items/', item.id]);
this.router.navigate([getItemPageRoute(item)]);
});
}

View File

@@ -89,7 +89,7 @@
<button (click)="performAction()"
class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}}
</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}}
</button>

View File

@@ -183,7 +183,7 @@ describe('ItemDeleteComponent', () => {
describe('notify', () => {
it('should navigate to the item edit page on failed deletion of the item', () => {
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(['']);
} else {
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>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<p>
@@ -39,7 +39,7 @@
{{'item.edit.move.processing' | translate}}
</span>
</button>
<button [routerLink]="['/items/', (itemRD$ | async)?.payload?.id, 'edit']"
<button [routerLink]="[(itemPageRoute$ | async), 'edit']"
class="btn btn-outline-secondary">
{{'item.edit.move.cancel' | translate}}
</button>

View File

@@ -57,9 +57,9 @@ describe('ItemMoveComponent', () => {
const routeStub = {
data: observableOf({
dso: createSuccessfulRemoteDataObject({
dso: createSuccessfulRemoteDataObject(Object.assign(new Item(), {
id: 'item1'
})
}))
})
};
@@ -122,7 +122,10 @@ describe('ItemMoveComponent', () => {
});
describe('moveCollection', () => {
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.selectedCollection = collection1;
comp.moveCollection();

View File

@@ -10,7 +10,7 @@ import { NotificationsService } from '../../../shared/notifications/notification
import { TranslateService } from '@ngx-translate/core';
import {
getFirstSucceededRemoteData,
getFirstCompletedRemoteData
getFirstCompletedRemoteData, getAllSucceededRemoteDataPayload
} from '../../../core/shared/operators';
import { ItemDataService } from '../../../core/data/item-data.service';
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 { PaginatedSearchOptions } from '../../../shared/search/paginated-search-options.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({
selector: 'ds-item-move',
@@ -43,11 +43,16 @@ export class ItemMoveComponent implements OnInit {
selectedCollection: Collection;
canSubmit = false;
itemId: string;
item: Item;
processing = false;
pagination = new PaginationComponentOptions();
/**
* Route to the item's page
*/
itemPageRoute$: Observable<string>;
constructor(private route: ActivatedRoute,
private router: Router,
private notificationsService: NotificationsService,
@@ -58,8 +63,12 @@ export class ItemMoveComponent implements OnInit {
ngOnInit(): void {
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.itemId = rd.payload.id;
this.item = rd.payload;
}
);
this.pagination.pageSize = 5;
@@ -116,9 +125,9 @@ export class ItemMoveComponent implements OnInit {
*/
moveCollection() {
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>) => {
this.router.navigate([getItemEditRoute(this.itemId)]);
this.router.navigate([getItemEditRoute(this.item)]);
if (response.hasSucceeded) {
this.notificationsService.success(this.translateService.get('item.edit.move.success'));
} else {

View File

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

View File

@@ -12,7 +12,7 @@
{{'item.edit.tabs.status.labels.itemPage' | translate}}:
</div>
<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 *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(), {
id: 'fake-id',
uuid: 'fake-id',
handle: 'fake/handle',
lastModified: '2018',
_links: {
@@ -27,7 +28,7 @@ describe('ItemStatusComponent', () => {
}
});
const itemPageUrl = `items/${mockItem.id}`;
const itemPageUrl = `/items/${mockItem.uuid}`;
const routeStub = {
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 { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { hasValue } from '../../../shared/empty.util';
import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators';
@Component({
selector: 'ds-item-status',
@@ -50,6 +51,11 @@ export class ItemStatusComponent implements OnInit {
*/
actionsKeys;
/**
* Route to the item's page
*/
itemPageRoute$: Observable<string>;
constructor(private route: ActivatedRoute,
private authorizationService: AuthorizationDataService) {
}
@@ -109,15 +115,10 @@ export class ItemStatusComponent implements OnInit {
});
}
});
}
/**
* Get the url to the simple item page
* @returns {string} url
*/
getItemPage(item: Item): string {
return getItemPageRoute(item.id);
this.itemPageRoute$ = this.itemRD$.pipe(
getAllSucceededRemoteDataPayload(),
map((item) => getItemPageRoute(item))
);
}
/**
@@ -125,7 +126,7 @@ export class ItemStatusComponent implements OnInit {
* @returns {string} url
*/
getCurrentUrl(item: Item): string {
return getItemEditRoute(item.id);
return getItemEditRoute(item);
}
trackOperation(index: number, operation: ItemOperation) {

View File

@@ -6,11 +6,11 @@
<ds-modify-item-overview [item]="item"></ds-modify-item-overview>
<button (click)="performAction()" class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}}
</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}}
</button>
</div>
</div>
</div>
</div>

View File

@@ -74,9 +74,9 @@ describe('AbstractSimpleItemActionComponent', () => {
routeStub = {
data: observableOf({
dso: createSuccessfulRemoteDataObject({
dso: createSuccessfulRemoteDataObject(Object.assign(new Item(), {
id: 'fake-id'
})
}))
})
};
@@ -136,14 +136,14 @@ describe('AbstractSimpleItemActionComponent', () => {
comp.processRestResponse(successfulRemoteData);
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', () => {
comp.processRestResponse(failedRemoteData);
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 { first, map } from 'rxjs/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.
@@ -30,6 +30,11 @@ export class AbstractSimpleItemActionComponent implements OnInit {
headerMessage: string;
descriptionMessage: string;
/**
* Route to the item's page
*/
itemPageRoute: string;
protected predicate: Predicate<RemoteData<Item>>;
constructor(protected route: ActivatedRoute,
@@ -47,6 +52,7 @@ export class AbstractSimpleItemActionComponent implements OnInit {
this.itemRD$.pipe(first()).subscribe((rd) => {
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(
findSuccessfulAccordingTo(this.predicate)).subscribe(() => {
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 {
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">
<ds-item-page-title-field class="mr-auto" [item]="item"></ds-item-page-title-field>
<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 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}}
</a>
</div>

View File

@@ -1,4 +1,6 @@
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';
@@ -6,12 +8,30 @@ export function getItemModuleRoute() {
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) {
return new URLCombiner(getItemModuleRoute(), id, ITEM_EDIT_PATH).toString();
export function getItemEditRoute(item: Item) {
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';

View File

@@ -1,5 +1,5 @@
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 { RemoteData } from '../core/data/remote-data';
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 { Store } from '@ngrx/store';
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
@@ -31,7 +34,8 @@ export const ITEM_PAGE_LINKS_TO_FOLLOW: FollowLinkConfig<Item>[] = [
export class ItemPageResolver implements Resolve<RemoteData<Item>> {
constructor(
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
).pipe(
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>) => {

View File

@@ -11,9 +11,10 @@ import { Item } from '../../core/shared/item.model';
import { MetadataService } from '../../core/metadata/metadata.service';
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 { AuthService } from '../../core/auth/auth.service';
import { getItemPageRoute } from '../item-page-routing-paths';
/**
* This component renders a simple item page.
@@ -44,6 +45,11 @@ export class ItemPageComponent implements OnInit {
*/
viewMode = ViewMode.StandalonePage;
/**
* Route to the item's page
*/
itemPageRoute$: Observable<string>;
constructor(
private route: ActivatedRoute,
private router: Router,
@@ -61,5 +67,9 @@ export class ItemPageComponent implements OnInit {
redirectOn4xx(this.router, this.authService)
);
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>
</h2>
<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 class="row">
@@ -74,7 +74,7 @@
</ds-item-page-uri-field>
<ds-item-page-collections [item]="object"></ds-item-page-collections>
<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}}
</a>
</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 { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
import { Bitstream } from '../../../../core/shared/bitstream.model';
import { Item } from '../../../../core/shared/item.model';
import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/operators';
import { getItemPageRoute } from '../../../item-page-routing-paths';
@Component({
selector: 'ds-item',
@@ -12,12 +13,21 @@ import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/oper
/**
* A generic component for displaying metadata and relations of an item
*/
export class ItemComponent {
export class ItemComponent implements OnInit {
@Input() object: Item;
/**
* Route to the item page
*/
itemPageRoute: string;
constructor(protected bitstreamDataService: BitstreamDataService) {
}
ngOnInit(): void {
this.itemPageRoute = getItemPageRoute(this.object);
}
// TODO refactor to return RemoteData, and thumbnail template to deal with loading
getThumbnail(): Observable<Bitstream> {
return this.bitstreamDataService.getThumbnailFor(this.object).pipe(

View File

@@ -3,7 +3,7 @@
<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2>
<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 class="row">
@@ -59,7 +59,7 @@
</ds-item-page-uri-field>
<ds-item-page-collections [item]="object"></ds-item-page-collections>
<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}}
</a>
</div>

View File

@@ -1,7 +1,14 @@
import { HostWindowService } from '../shared/host-window.service';
import { SidebarService } from '../shared/sidebar/sidebar.service';
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 { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
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:
return getCollectionPageRoute(dso.uuid);
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),
canActivate: [EndUserAgreementCurrentUserGuard]
},
{ path: 'entities/:entity-type',
loadChildren: () => import('./+item-page/item-page.module')
.then((m) => m.ItemPageModule),
canActivate: [EndUserAgreementCurrentUserGuard]
},
{
path: BITSTREAM_MODULE_PATH,
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 { RequestService } from './request.service';
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
import { Item } from '../shared/item.model';
describe('DsoRedirectDataService', () => {
let scheduler: TestScheduler;
@@ -48,10 +49,10 @@ describe('DsoRedirectDataService', () => {
navigate: jasmine.createSpy('navigate')
};
remoteData = createSuccessfulRemoteDataObject({
remoteData = createSuccessfulRemoteDataObject(Object.assign(new Item(), {
type: 'item',
uuid: '123456789'
});
}));
rdbService = jasmine.createSpyObj('rdbService', {
buildSingle: cold('a', {
@@ -114,7 +115,24 @@ describe('DsoRedirectDataService', () => {
redir.subscribe();
scheduler.schedule(() => redir);
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', () => {

View File

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

View File

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

View File

@@ -2,9 +2,7 @@ import { Component } from '@angular/core';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { focusShadow } from '../../../../../shared/animations/focus';
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 { ItemSearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
@listableObjectComponent('JournalIssueSearchResult', ViewMode.GridElement)
@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
*/
export class JournalIssueSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> {
export class JournalIssueSearchResultGridElementComponent extends ItemSearchResultGridElementComponent {
}

View File

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

View File

@@ -1,10 +1,8 @@
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 { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
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)
@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
*/
export class JournalVolumeSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> {
export class JournalVolumeSearchResultGridElementComponent extends ItemSearchResultGridElementComponent {
}

View File

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

View File

@@ -2,9 +2,7 @@ import { Component } from '@angular/core';
import { focusShadow } from '../../../../../shared/animations/focus';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
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 { ItemSearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
@listableObjectComponent('JournalSearchResult', ViewMode.GridElement)
@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
*/
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-truncatable [id]="dso.id">
<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>
<span *ngIf="linkType == linkTypes.None"
class="lead"

View File

@@ -1,9 +1,7 @@
import { Component } from '@angular/core';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
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 { ItemSearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
@listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement)
@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
*/
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-truncatable [id]="dso.id">
<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>
<span *ngIf="linkType == linkTypes.None"
class="lead"

View File

@@ -1,9 +1,7 @@
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 { 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)
@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
*/
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-truncatable [id]="dso.id">
<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>
<span *ngIf="linkType == linkTypes.None"
class="lead"

View File

@@ -1,9 +1,7 @@
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 { 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)
@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
*/
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>
</h2>
<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 class="row">
@@ -53,7 +53,7 @@
[label]="'journalissue.page.keyword'">
</ds-generic-item-page-field>
<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}}
</a>
</div>

View File

@@ -3,7 +3,7 @@
{{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2>
<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 class="row">
@@ -36,7 +36,7 @@
[label]="'journalvolume.page.description'">
</ds-generic-item-page-field>
<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}}
</a>
</div>

View File

@@ -3,7 +3,7 @@
{{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2>
<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 class="row">
@@ -35,7 +35,7 @@
[label]="'journal.page.description'">
</ds-generic-item-page-field>
<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}}
</a>
</div>

View File

@@ -5,7 +5,7 @@
</div>
<a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -42,7 +42,7 @@
</p>
<div *ngIf="linkType != linkTypes.None" class="text-center">
<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>
</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 { ViewMode } from '../../../../../core/shared/view-mode.model';
import { focusShadow } from '../../../../../shared/animations/focus';
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 { ItemSearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
@listableObjectComponent('OrgUnitSearchResult', ViewMode.GridElement)
@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
*/
export class OrgUnitSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> {
export class OrgUnitSearchResultGridElementComponent extends ItemSearchResultGridElementComponent {
}

View File

@@ -5,7 +5,7 @@
</div>
<a *ngIf="linkType != linkTypes.None"
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -36,7 +36,7 @@
</p>
<div *ngIf="linkType != linkTypes.None" class="text-center">
<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>
</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 { ViewMode } from '../../../../../core/shared/view-mode.model';
import { focusShadow } from '../../../../../shared/animations/focus';
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 { ItemSearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component';
@listableObjectComponent('PersonSearchResult', ViewMode.GridElement)
@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
*/
export class PersonSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> {
export class PersonSearchResultGridElementComponent extends ItemSearchResultGridElementComponent {
}

View File

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

View File

@@ -1,10 +1,8 @@
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 { ViewMode } from '../../../../../core/shared/view-mode.model';
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)
@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
*/
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-truncatable [id]="dso.id">
<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>
<span *ngIf="linkType == linkTypes.None"
class="lead"

View File

@@ -1,9 +1,7 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
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 { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.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('OrgUnitSearchResult', ViewMode.ListElement)
@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
*/
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-truncatable [id]="dso.id">
<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>
<span *ngIf="linkType == linkTypes.None"
class="lead"

View File

@@ -1,9 +1,7 @@
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 { 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)
@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
*/
export class PersonSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
export class PersonSearchResultListElementComponent extends ItemSearchResultListElementComponent {
get name() {
return this.value ?

View File

@@ -1,7 +1,7 @@
<ds-truncatable [id]="dso.id">
<ds-type-badge *ngIf="showLabel" [object]="dso"></ds-type-badge>
<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>
<span *ngIf="linkType == linkTypes.None"
class="lead"

View File

@@ -1,9 +1,7 @@
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 { 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)
@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
*/
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>
</h2>
<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 class="row">
@@ -39,7 +39,7 @@
[label]="'orgunit.page.description'">
</ds-generic-item-page-field>
<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}}
</a>
</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>
</h2>
<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 class="row">
@@ -52,7 +52,7 @@
[label]="'person.page.firstname'">
</ds-generic-item-page-field>
<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}}
</a>
</div>

View File

@@ -3,7 +3,7 @@
{{'project.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2>
<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 class="row">
@@ -59,7 +59,7 @@
[label]="'project.page.keyword'">
</ds-generic-item-page-field>
<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}}
</a>
</div>

View File

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

View File

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

View File

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

View File

@@ -40,7 +40,7 @@ describe('DsoPageEditButtonComponent', () => {
fixture = TestBed.createComponent(DsoPageEditButtonComponent);
component = fixture.componentInstance;
component.dso = dso;
component.pageRoutePrefix = 'test';
component.pageRoute = 'test';
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")
*/
@Input() pageRoutePrefix: string;
@Input() pageRoute: string;
/**
* 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 { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component';
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
@@ -28,6 +29,6 @@ export class EditItemSelectorComponent extends DSOSelectorModalWrapperComponent
* Navigate to the item edit page
*/
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-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>
</td>

View File

@@ -4,7 +4,11 @@ import { Version } from '../../../core/shared/version.model';
import { RemoteData } from '../../../core/data/remote-data';
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable } from 'rxjs';
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 { PaginatedList } from '../../../core/data/paginated-list.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 { followLink } from '../../utils/follow-link-config.model';
import { hasValueOperator } from '../../empty.util';
import { getItemPageRoute } from '../../../+item-page/item-page-routing-paths';
@Component({
selector: 'ds-item-versions',
@@ -86,6 +91,15 @@ export class ItemVersionsComponent implements OnInit {
*/
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) {
}
@@ -118,6 +132,15 @@ export class ItemVersionsComponent implements OnInit {
map((versions: PaginatedList<Version>) => versions.page.filter((version: Version) => version.eperson !== undefined).length > 0),
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 {
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 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> {}
@@ -12,4 +14,6 @@ export const MOCK_RESPONSE_MAP: InjectionToken<ResponseMapMock> = new InjectionT
*/
export const mockResponseMap: ResponseMapMock = new Map([
// [ '/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 { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model';
import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service';
import { DSpaceObject} from '../../../../core/shared/dspace-object.model';
import { Router} from '@angular/router';
import { NotificationsService} from '../../../notifications/notifications.service';
import { TranslateService} from '@ngx-translate/core';
import { SearchService} from '../../../../core/shared/search/search.service';
import { RequestService} from '../../../../core/data/request.service';
import { Observable} from 'rxjs';
import { RemoteData} from '../../../../core/data/remote-data';
import { WorkflowItem} from '../../../../core/submission/models/workflowitem.model';
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
import { Router } from '@angular/router';
import { NotificationsService } from '../../../notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';
import { SearchService } from '../../../../core/shared/search/search.service';
import { RequestService } from '../../../../core/data/request.service';
import { Observable } from 'rxjs';
import { RemoteData } from '../../../../core/data/remote-data';
import { WorkflowItem } from '../../../../core/submission/models/workflowitem.model';
import { switchMap, take } from 'rxjs/operators';
import { CLAIMED_TASK } from '../../../../core/tasks/models/claimed-task-object.resource-type';
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 { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator';
import { Observable } from 'rxjs';

View File

@@ -1,5 +1,5 @@
<button class="btn btn-primary mt-1 mb-3"
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}}
</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 { TranslateService } from '@ngx-translate/core';
import { MyDSpaceActionsComponent } from '../mydspace-actions';
import { ItemDataService } from '../../../core/data/item-data.service';
import { Item } from '../../../core/shared/item.model';
import { NotificationsService } from '../../notifications/notifications.service';
import { RequestService } from '../../../core/data/request.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.
@@ -19,13 +18,18 @@ import { SearchService } from '../../../core/shared/search/search.service';
templateUrl: './item-actions.component.html',
})
export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDataService> {
export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDataService> implements OnInit {
/**
* The Item object
*/
@Input() object: Item;
/**
* Route to the item's page
*/
itemPageRoute: string;
/**
* Initialize instance variables
*
@@ -45,6 +49,10 @@ export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDat
super(Item.type, injector, router, notificationsService, translate, searchService, requestService);
}
ngOnInit(): void {
this.initPageRoute();
}
/**
* Init the target object
*
@@ -52,6 +60,14 @@ export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDat
*/
initObjects(object: Item) {
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 {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing';
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { of as observableOf } from 'rxjs';
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 { getMockRequestService } from '../mocks/request.service.mock';
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 { TranslateLoaderMock } from '../mocks/translate-loader.mock';
import { NotificationsService } from '../notifications/notifications.service';

View File

@@ -3,7 +3,7 @@
<div class="position-absolute ml-1">
<ng-content></ng-content>
</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">
<div>
<ds-grid-thumbnail [thumbnail]="getThumbnail() | async">
@@ -36,7 +36,7 @@
</ds-truncatable-part>
</p>
<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>
</div>
</div>

View File

@@ -5,6 +5,7 @@ import { listableObjectComponent } from '../../../../object-collection/shared/li
import { SearchResultGridElementComponent } from '../../search-result-grid-element.component';
import { Item } from '../../../../../core/shared/item.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(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
*/
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 { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
import { getItemPageRoute } from '../../../../+item-page/item-page-routing-paths';
@Component({
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
*/
export class ItemMetadataRepresentationListElementComponent extends MetadataRepresentationListElementComponent {
export class ItemMetadataRepresentationListElementComponent extends MetadataRepresentationListElementComponent implements OnInit {
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 { followLink } from '../../../../utils/follow-link-config.model';
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';
/**

View File

@@ -2,7 +2,7 @@
<ds-truncatable [id]="dso.id" *ngIf="object !== undefined && object !== null">
<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>
<span *ngIf="linkType == linkTypes.None" class="lead"
[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 { SearchResultListElementComponent } from '../../../search-result-list-element.component';
import { Item } from '../../../../../../core/shared/item.model';
import { getItemPageRoute } from '../../../../../../+item-page/item-page-routing-paths';
@listableObjectComponent('PublicationSearchResult', 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
*/
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>
</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>
</tbody>
</table>

View File

@@ -2,7 +2,11 @@ import { Component, Input } from '@angular/core';
import { Item } from '../../../core/shared/item.model';
import { ObjectSelectService } from '../object-select.service';
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({
selector: 'ds-item-select',
@@ -20,6 +24,15 @@ export class ItemSelectComponent extends ObjectSelectComponent<Item> {
@Input()
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) {
super(objectSelectService);
}
@@ -29,6 +42,15 @@ export class ItemSelectComponent extends ObjectSelectComponent<Item> {
if (!isNotEmpty(this.confirmButton)) {
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 { UploaderProperties } from './uploader-properties.model';
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';
@Component({

View File

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

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