Merge remote-tracking branch 'origin/main' into poc-eslint-plugin-autofix-selectors

This commit is contained in:
Yury Bondarenko
2024-04-22 14:32:19 +02:00
50 changed files with 482 additions and 175 deletions

View File

@@ -37,7 +37,6 @@
<ng-template ngbNavContent> <ng-template ngbNavContent>
<ds-pagination <ds-pagination
[paginationOptions]="(paginationOptions$ | async)" [paginationOptions]="(paginationOptions$ | async)"
[pageInfoState]="(objectsSelected$|async)?.payload.pageInfo"
[collectionSize]="(objectsSelected$|async)?.payload?.totalElements" [collectionSize]="(objectsSelected$|async)?.payload?.totalElements"
[objects]="(objectsSelected$|async)" [objects]="(objectsSelected$|async)"
[showPaginator]="false" [showPaginator]="false"

View File

@@ -45,7 +45,6 @@
<ds-pagination <ds-pagination
*ngIf="(pageInfoState$ | async)?.totalElements > 0 && (searching$ | async) !== true" *ngIf="(pageInfoState$ | async)?.totalElements > 0 && (searching$ | async) !== true"
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="pageInfoState$"
[collectionSize]="(pageInfoState$ | async)?.totalElements" [collectionSize]="(pageInfoState$ | async)?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">

View File

@@ -52,7 +52,6 @@
<ds-pagination <ds-pagination
*ngIf="(groups$ | async)?.payload?.totalElements > 0" *ngIf="(groups$ | async)?.payload?.totalElements > 0"
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="groupsPageInfoState$"
[collectionSize]="(groups$ | async)?.payload?.totalElements" [collectionSize]="(groups$ | async)?.payload?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true" [hidePagerWhenSinglePage]="true"

View File

@@ -5,7 +5,6 @@
<ds-pagination *ngIf="(ePeopleMembersOfGroup | async)?.totalElements > 0" <ds-pagination *ngIf="(ePeopleMembersOfGroup | async)?.totalElements > 0"
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="(ePeopleMembersOfGroup | async)"
[collectionSize]="(ePeopleMembersOfGroup | async)?.totalElements" [collectionSize]="(ePeopleMembersOfGroup | async)?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">
@@ -86,7 +85,6 @@
<ds-pagination *ngIf="(ePeopleSearch | async)?.totalElements > 0" <ds-pagination *ngIf="(ePeopleSearch | async)?.totalElements > 0"
[paginationOptions]="configSearch" [paginationOptions]="configSearch"
[pageInfoState]="(ePeopleSearch | async)"
[collectionSize]="(ePeopleSearch | async)?.totalElements" [collectionSize]="(ePeopleSearch | async)?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">

View File

@@ -5,7 +5,6 @@
<ds-pagination *ngIf="(subGroups$ | async)?.payload?.totalElements > 0" <ds-pagination *ngIf="(subGroups$ | async)?.payload?.totalElements > 0"
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="(subGroups$ | async)?.payload"
[collectionSize]="(subGroups$ | async)?.payload?.totalElements" [collectionSize]="(subGroups$ | async)?.payload?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">
@@ -84,7 +83,6 @@
<ds-pagination *ngIf="(searchResults$ | async)?.payload?.totalElements > 0" <ds-pagination *ngIf="(searchResults$ | async)?.payload?.totalElements > 0"
[paginationOptions]="configSearch" [paginationOptions]="configSearch"
[pageInfoState]="(searchResults$ | async)?.payload"
[collectionSize]="(searchResults$ | async)?.payload?.totalElements" [collectionSize]="(searchResults$ | async)?.payload?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">

View File

@@ -37,7 +37,6 @@
<ds-pagination <ds-pagination
*ngIf="(pageInfoState$ | async)?.totalElements > 0 && (loading$ | async) !== true" *ngIf="(pageInfoState$ | async)?.totalElements > 0 && (loading$ | async) !== true"
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="pageInfoState$"
[collectionSize]="(pageInfoState$ | async)?.totalElements" [collectionSize]="(pageInfoState$ | async)?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">

View File

@@ -10,7 +10,6 @@
[collectionSize]="(ldnServicesRD$ | async)?.payload?.totalElements" [collectionSize]="(ldnServicesRD$ | async)?.payload?.totalElements"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true" [hidePagerWhenSinglePage]="true"
[pageInfoState]="(ldnServicesRD$ | async)?.payload"
[paginationOptions]="pageConfig"> [paginationOptions]="pageConfig">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">

View File

@@ -11,7 +11,6 @@
<ds-pagination <ds-pagination
*ngIf="(bitstreamFormats | async)?.payload?.totalElements > 0" *ngIf="(bitstreamFormats | async)?.payload?.totalElements > 0"
[paginationOptions]="pageConfig" [paginationOptions]="pageConfig"
[pageInfoState]="(bitstreamFormats | async)?.payload"
[collectionSize]="(bitstreamFormats | async)?.payload?.totalElements" [collectionSize]="(bitstreamFormats | async)?.payload?.totalElements"
[hideGear]="false" [hideGear]="false"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">

View File

@@ -16,7 +16,6 @@
<ds-pagination <ds-pagination
*ngIf="fields?.totalElements > 0" *ngIf="fields?.totalElements > 0"
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="fields"
[collectionSize]="fields?.totalElements" [collectionSize]="fields?.totalElements"
[hideGear]="false" [hideGear]="false"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">

View File

@@ -23,7 +23,6 @@ import {
SortDirection, SortDirection,
SortOptions, SortOptions,
} from '../../core/cache/models/sort-options.model'; } from '../../core/cache/models/sort-options.model';
import { CollectionDataService } from '../../core/data/collection-data.service';
import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { ConfigurationDataService } from '../../core/data/configuration-data.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { ItemDataService } from '../../core/data/item-data.service'; import { ItemDataService } from '../../core/data/item-data.service';
@@ -39,6 +38,7 @@ import { SEARCH_CONFIG_SERVICE } from '../../my-dspace-page/my-dspace-configurat
import { ErrorComponent } from '../../shared/error/error.component'; import { ErrorComponent } from '../../shared/error/error.component';
import { HostWindowService } from '../../shared/host-window.service'; import { HostWindowService } from '../../shared/host-window.service';
import { LoadingComponent } from '../../shared/loading/loading.component'; import { LoadingComponent } from '../../shared/loading/loading.component';
import { getMockThemeService } from '../../shared/mocks/theme-service.mock';
import { NotificationsService } from '../../shared/notifications/notifications.service'; import { NotificationsService } from '../../shared/notifications/notifications.service';
import { ItemSelectComponent } from '../../shared/object-select/item-select/item-select.component'; import { ItemSelectComponent } from '../../shared/object-select/item-select/item-select.component';
import { ObjectSelectService } from '../../shared/object-select/object-select.service'; import { ObjectSelectService } from '../../shared/object-select/object-select.service';
@@ -58,6 +58,7 @@ import { RouterStub } from '../../shared/testing/router.stub';
import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub'; import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service.stub';
import { SearchServiceStub } from '../../shared/testing/search-service.stub'; import { SearchServiceStub } from '../../shared/testing/search-service.stub';
import { createPaginatedList } from '../../shared/testing/utils.test'; import { createPaginatedList } from '../../shared/testing/utils.test';
import { ThemeService } from '../../shared/theme-support/theme.service';
import { EnumKeysPipe } from '../../shared/utils/enum-keys-pipe'; import { EnumKeysPipe } from '../../shared/utils/enum-keys-pipe';
import { VarDirective } from '../../shared/utils/var.directive'; import { VarDirective } from '../../shared/utils/var.directive';
import { CollectionItemMapperComponent } from './collection-item-mapper.component'; import { CollectionItemMapperComponent } from './collection-item-mapper.component';
@@ -190,7 +191,6 @@ describe('CollectionItemMapperComponent', () => {
{ provide: SearchService, useValue: searchServiceStub }, { provide: SearchService, useValue: searchServiceStub },
{ provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: ItemDataService, useValue: itemDataServiceStub }, { provide: ItemDataService, useValue: itemDataServiceStub },
{ provide: CollectionDataService, useValue: collectionDataServiceStub },
{ provide: TranslateService, useValue: translateServiceStub }, { provide: TranslateService, useValue: translateServiceStub },
{ provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: HostWindowService, useValue: new HostWindowServiceStub(0) },
{ provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() }, { provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() },
@@ -199,6 +199,7 @@ describe('CollectionItemMapperComponent', () => {
{ provide: GroupDataService, useValue: groupDataService }, { provide: GroupDataService, useValue: groupDataService },
{ provide: LinkHeadService, useValue: linkHeadService }, { provide: LinkHeadService, useValue: linkHeadService },
{ provide: ConfigurationDataService, useValue: configurationDataService }, { provide: ConfigurationDataService, useValue: configurationDataService },
{ provide: ThemeService, useValue: getMockThemeService() },
], ],
}).overrideComponent(CollectionItemMapperComponent, { }).overrideComponent(CollectionItemMapperComponent, {
set: { set: {

View File

@@ -35,7 +35,6 @@ import {
SortDirection, SortDirection,
SortOptions, SortOptions,
} from '../../core/cache/models/sort-options.model'; } from '../../core/cache/models/sort-options.model';
import { CollectionDataService } from '../../core/data/collection-data.service';
import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { ItemDataService } from '../../core/data/item-data.service'; import { ItemDataService } from '../../core/data/item-data.service';
import { PaginatedList } from '../../core/data/paginated-list.model'; import { PaginatedList } from '../../core/data/paginated-list.model';
@@ -150,7 +149,6 @@ export class CollectionItemMapperComponent implements OnInit {
private searchService: SearchService, private searchService: SearchService,
private notificationsService: NotificationsService, private notificationsService: NotificationsService,
private itemDataService: ItemDataService, private itemDataService: ItemDataService,
private collectionDataService: CollectionDataService,
private translateService: TranslateService, private translateService: TranslateService,
private dsoNameService: DSONameService) { private dsoNameService: DSONameService) {
} }
@@ -187,6 +185,8 @@ export class CollectionItemMapperComponent implements OnInit {
this.shouldUpdate$.next(false); this.shouldUpdate$.next(false);
} }
return this.itemDataService.findListByHref(collectionRD.payload._links.mappedItems.href, Object.assign(options, { return this.itemDataService.findListByHref(collectionRD.payload._links.mappedItems.href, Object.assign(options, {
currentPage: options.pagination.currentPage,
elementsPerPage: options.pagination.pageSize,
sort: this.defaultSortOptions, sort: this.defaultSortOptions,
}),!shouldUpdate, false, followLink('owningCollection')).pipe( }),!shouldUpdate, false, followLink('owningCollection')).pipe(
getAllSucceededRemoteData(), getAllSucceededRemoteData(),

View File

@@ -7,6 +7,7 @@ import { browseByGuard } from '../browse-by/browse-by-guard';
import { browseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver'; import { browseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver';
import { authenticatedGuard } from '../core/auth/authenticated.guard'; import { authenticatedGuard } from '../core/auth/authenticated.guard';
import { collectionBreadcrumbResolver } from '../core/breadcrumbs/collection-breadcrumb.resolver'; import { collectionBreadcrumbResolver } from '../core/breadcrumbs/collection-breadcrumb.resolver';
import { communityBreadcrumbResolver } from '../core/breadcrumbs/community-breadcrumb.resolver';
import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
import { ComcolBrowseByComponent } from '../shared/comcol/sections/comcol-browse-by/comcol-browse-by.component'; import { ComcolBrowseByComponent } from '../shared/comcol/sections/comcol-browse-by/comcol-browse-by.component';
import { ComcolSearchSectionComponent } from '../shared/comcol/sections/comcol-search-section/comcol-search-section.component'; import { ComcolSearchSectionComponent } from '../shared/comcol/sections/comcol-search-section/comcol-search-section.component';
@@ -27,12 +28,29 @@ import { itemTemplatePageResolver } from './edit-item-template-page/item-templat
import { ThemedEditItemTemplatePageComponent } from './edit-item-template-page/themed-edit-item-template-page.component'; import { ThemedEditItemTemplatePageComponent } from './edit-item-template-page/themed-edit-item-template-page.component';
import { ThemedCollectionPageComponent } from './themed-collection-page.component'; import { ThemedCollectionPageComponent } from './themed-collection-page.component';
export const ROUTES: Route[] = [ export const ROUTES: Route[] = [
{ {
path: COLLECTION_CREATE_PATH, path: COLLECTION_CREATE_PATH,
component: CreateCollectionPageComponent,
canActivate: [authenticatedGuard, createCollectionPageGuard], canActivate: [authenticatedGuard, createCollectionPageGuard],
children: [
{
path: '',
component: CreateCollectionPageComponent,
resolve: {
breadcrumb: i18nBreadcrumbResolver,
},
data: {
breadcrumbKey: 'collection.create',
},
},
],
data: {
breadcrumbQueryParam: 'parent',
},
resolve: {
breadcrumb: communityBreadcrumbResolver,
},
runGuardsAndResolvers: 'always',
}, },
{ {
path: ':id', path: ':id',

View File

@@ -28,8 +28,26 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component'
export const ROUTES: Route[] = [ export const ROUTES: Route[] = [
{ {
path: COMMUNITY_CREATE_PATH, path: COMMUNITY_CREATE_PATH,
component: CreateCommunityPageComponent, children: [
{
path: '',
component: CreateCommunityPageComponent,
resolve: {
breadcrumb: i18nBreadcrumbResolver,
},
data: {
breadcrumbKey: 'community.create',
},
},
],
canActivate: [authenticatedGuard, createCommunityPageGuard], canActivate: [authenticatedGuard, createCommunityPageGuard],
data: {
breadcrumbQueryParam: 'parent',
},
resolve: {
breadcrumb: communityBreadcrumbResolver,
},
runGuardsAndResolvers: 'always',
}, },
{ {
path: ':id', path: ':id',

View File

@@ -8,11 +8,15 @@ import { Observable } from 'rxjs';
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model'; import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
import { COMMUNITY_PAGE_LINKS_TO_FOLLOW } from '../../community-page/community-page.resolver'; import { COMMUNITY_PAGE_LINKS_TO_FOLLOW } from '../../community-page/community-page.resolver';
import { hasValue } from '../../shared/empty.util';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
import { CommunityDataService } from '../data/community-data.service'; import { CommunityDataService } from '../data/community-data.service';
import { Community } from '../shared/community.model'; import { Community } from '../shared/community.model';
import { DSpaceObject } from '../shared/dspace-object.model'; import { DSpaceObject } from '../shared/dspace-object.model';
import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver'; import {
DSOBreadcrumbResolver,
DSOBreadcrumbResolverByUuid,
} from './dso-breadcrumb.resolver';
import { DSOBreadcrumbsService } from './dso-breadcrumbs.service'; import { DSOBreadcrumbsService } from './dso-breadcrumbs.service';
/** /**
@@ -25,11 +29,22 @@ export const communityBreadcrumbResolver: ResolveFn<BreadcrumbConfig<Community>>
dataService: CommunityDataService = inject(CommunityDataService), dataService: CommunityDataService = inject(CommunityDataService),
): Observable<BreadcrumbConfig<Community>> => { ): Observable<BreadcrumbConfig<Community>> => {
const linksToFollow: FollowLinkConfig<DSpaceObject>[] = COMMUNITY_PAGE_LINKS_TO_FOLLOW as FollowLinkConfig<DSpaceObject>[]; const linksToFollow: FollowLinkConfig<DSpaceObject>[] = COMMUNITY_PAGE_LINKS_TO_FOLLOW as FollowLinkConfig<DSpaceObject>[];
return DSOBreadcrumbResolver( if (hasValue(route.data.breadcrumbQueryParam) && hasValue(route.queryParams[route.data.breadcrumbQueryParam])) {
route, return DSOBreadcrumbResolverByUuid(
state, route,
breadcrumbService, state,
dataService, route.queryParams[route.data.breadcrumbQueryParam],
...linksToFollow, breadcrumbService,
) as Observable<BreadcrumbConfig<Community>>; dataService,
...linksToFollow,
) as Observable<BreadcrumbConfig<Community>>;
} else {
return DSOBreadcrumbResolver(
route,
state,
breadcrumbService,
dataService,
...linksToFollow,
) as Observable<BreadcrumbConfig<Community>>;
}
}; };

View File

@@ -18,7 +18,10 @@ describe('DSOBreadcrumbResolver', () => {
uuid = '1234-65487-12354-1235'; uuid = '1234-65487-12354-1235';
breadcrumbUrl = `/collections/${uuid}`; breadcrumbUrl = `/collections/${uuid}`;
currentUrl = `${breadcrumbUrl}/edit`; currentUrl = `${breadcrumbUrl}/edit`;
testCollection = Object.assign(new Collection(), { uuid }); testCollection = Object.assign(new Collection(), {
uuid: uuid,
type: 'collection',
});
dsoBreadcrumbService = {}; dsoBreadcrumbService = {};
collectionService = { collectionService = {
findById: () => createSuccessfulRemoteDataObject$(testCollection), findById: () => createSuccessfulRemoteDataObject$(testCollection),

View File

@@ -5,6 +5,7 @@ import {
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { getDSORoute } from '../../app-routing-paths';
import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model'; import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model';
import { hasValue } from '../../shared/empty.util'; import { hasValue } from '../../shared/empty.util';
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
@@ -32,15 +33,34 @@ export const DSOBreadcrumbResolver: (route: ActivatedRouteSnapshot, state: Route
dataService: IdentifiableDataService<DSpaceObject>, dataService: IdentifiableDataService<DSpaceObject>,
...linksToFollow: FollowLinkConfig<DSpaceObject>[] ...linksToFollow: FollowLinkConfig<DSpaceObject>[]
): Observable<BreadcrumbConfig<DSpaceObject>> => { ): Observable<BreadcrumbConfig<DSpaceObject>> => {
const uuid = route.params.id; return DSOBreadcrumbResolverByUuid(route, state, route.params.id, breadcrumbService, dataService, ...linksToFollow);
};
/**
* Method for resolving a breadcrumb config object with the given UUID
*
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
* @param {String} uuid The uuid of the DSO object
* @param {DSOBreadcrumbsService} breadcrumbService
* @param {IdentifiableDataService} dataService
* @param linksToFollow
* @returns BreadcrumbConfig object
*/
export const DSOBreadcrumbResolverByUuid: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot, uuid: string, breadcrumbService: DSOBreadcrumbsService, dataService: IdentifiableDataService<DSpaceObject>, ...linksToFollow: FollowLinkConfig<DSpaceObject>[]) => Observable<BreadcrumbConfig<DSpaceObject>> = (
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
uuid: string,
breadcrumbService: DSOBreadcrumbsService,
dataService: IdentifiableDataService<DSpaceObject>,
...linksToFollow: FollowLinkConfig<DSpaceObject>[]
): Observable<BreadcrumbConfig<DSpaceObject>> => {
return dataService.findById(uuid, true, false, ...linksToFollow).pipe( return dataService.findById(uuid, true, false, ...linksToFollow).pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
getRemoteDataPayload(), getRemoteDataPayload(),
map((object: DSpaceObject) => { map((object: DSpaceObject) => {
if (hasValue(object)) { if (hasValue(object)) {
const fullPath = state.url; return { provider: breadcrumbService, key: object, url: getDSORoute(object) };
const url = (fullPath.substring(0, fullPath.indexOf(uuid))).concat(uuid);
return { provider: breadcrumbService, key: object, url: url };
} else { } else {
return undefined; return undefined;
} }

View File

@@ -0,0 +1,17 @@
import {
followLink,
FollowLinkConfig,
} from '../../../shared/utils/follow-link-config.model';
import { WorkflowItem } from '../models/workflowitem.model';
import { WorkspaceItem } from '../models/workspaceitem.model';
/**
* The self links defined in this list are expected to be requested somewhere in the near future
* Requesting them as embeds will limit the number of requests
*
* Needs to be in a separate file to prevent circular dependencies in webpack.
*/
export const SUBMISSION_LINKS_TO_FOLLOW: FollowLinkConfig<WorkflowItem | WorkspaceItem>[] = [
followLink('item'),
followLink('collection'),
];

View File

@@ -5,12 +5,12 @@ import {
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { followLink } from '../../../shared/utils/follow-link-config.model';
import { IdentifiableDataService } from '../../data/base/identifiable-data.service'; import { IdentifiableDataService } from '../../data/base/identifiable-data.service';
import { RemoteData } from '../../data/remote-data'; import { RemoteData } from '../../data/remote-data';
import { Item } from '../../shared/item.model'; import { Item } from '../../shared/item.model';
import { getFirstCompletedRemoteData } from '../../shared/operators'; import { getFirstCompletedRemoteData } from '../../shared/operators';
import { SubmissionObject } from '../models/submission-object.model'; import { SubmissionObject } from '../models/submission-object.model';
import { SUBMISSION_LINKS_TO_FOLLOW } from './submission-links-to-follow';
/** /**
* Method for resolving an item based on the parameters in the current route * Method for resolving an item based on the parameters in the current route
@@ -28,7 +28,7 @@ export const SubmissionObjectResolver: (route: ActivatedRouteSnapshot, state: Ro
return dataService.findById(route.params.id, return dataService.findById(route.params.id,
true, true,
false, false,
followLink('item'), ...SUBMISSION_LINKS_TO_FOLLOW,
).pipe( ).pipe(
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
switchMap((wfiRD: RemoteData<any>) => wfiRD.payload.item as Observable<RemoteData<Item>>), switchMap((wfiRD: RemoteData<any>) => wfiRD.payload.item as Observable<RemoteData<Item>>),

View File

@@ -0,0 +1,51 @@
import {
ActivatedRouteSnapshot,
Resolve,
RouterStateSnapshot,
} from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { BreadcrumbConfig } from '../../../breadcrumbs/breadcrumb/breadcrumb-config.model';
import { IdentifiableDataService } from '../../data/base/identifiable-data.service';
import {
getFirstCompletedRemoteData,
getRemoteDataPayload,
} from '../../shared/operators';
import { SubmissionObject } from '../models/submission-object.model';
import { SubmissionParentBreadcrumbsService } from '../submission-parent-breadcrumb.service';
import { SUBMISSION_LINKS_TO_FOLLOW } from './submission-links-to-follow';
/**
* This class represents a resolver that requests a specific item before the route is activated
*/
export abstract class SubmissionParentBreadcrumbResolver implements Resolve<BreadcrumbConfig<SubmissionObject>> {
protected constructor(
protected dataService: IdentifiableDataService<any>,
protected breadcrumbService: SubmissionParentBreadcrumbsService,
) {
}
/**
* Method for resolving an item based on the parameters in the current route
* @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot
* @param {RouterStateSnapshot} state The current RouterStateSnapshot
* @returns Observable<<RemoteData<Item>> Emits the found item based on the parameters in the current route,
* or an error if something went wrong
*/
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<BreadcrumbConfig<SubmissionObject>> {
return this.dataService.findById(route.params.id,
true,
false,
...SUBMISSION_LINKS_TO_FOLLOW,
).pipe(
getFirstCompletedRemoteData(),
getRemoteDataPayload(),
map((submissionObject: SubmissionObject) => ({
provider: this.breadcrumbService,
key: submissionObject,
} as BreadcrumbConfig<SubmissionObject>)),
);
}
}

View File

@@ -0,0 +1,70 @@
import { Injectable } from '@angular/core';
import {
combineLatest,
Observable,
of as observableOf,
switchMap,
} from 'rxjs';
import { getDSORoute } from '../../app-routing-paths';
import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model';
import { hasValue } from '../../shared/empty.util';
import { SubmissionService } from '../../submission/submission.service';
import { BreadcrumbsProviderService } from '../breadcrumbs/breadcrumbsProviderService';
import { DSOBreadcrumbsService } from '../breadcrumbs/dso-breadcrumbs.service';
import { DSONameService } from '../breadcrumbs/dso-name.service';
import { CollectionDataService } from '../data/collection-data.service';
import { RemoteData } from '../data/remote-data';
import { Collection } from '../shared/collection.model';
import {
getFirstCompletedRemoteData,
getRemoteDataPayload,
} from '../shared/operators';
import { SubmissionObject } from './models/submission-object.model';
/**
* Service to calculate the parent {@link DSpaceObject} breadcrumbs for a {@link SubmissionObject}
*/
@Injectable({
providedIn: 'root',
})
export class SubmissionParentBreadcrumbsService implements BreadcrumbsProviderService<SubmissionObject> {
constructor(
protected dsoNameService: DSONameService,
protected dsoBreadcrumbsService: DSOBreadcrumbsService,
protected submissionService: SubmissionService,
protected collectionService: CollectionDataService,
) {
}
/**
* Creates the parent breadcrumb structure for {@link SubmissionObject}s. It also automatically recreates the
* parent breadcrumb structure when you change a {@link SubmissionObject}'s by dispatching a
* {@link ChangeSubmissionCollectionAction}.
*
* @param submissionObject The {@link SubmissionObject} for which the parent breadcrumb structure needs to be created
*/
getBreadcrumbs(submissionObject: SubmissionObject): Observable<Breadcrumb[]> {
return combineLatest([
(submissionObject.collection as Observable<RemoteData<Collection>>).pipe(
getFirstCompletedRemoteData(),
getRemoteDataPayload(),
),
this.submissionService.getSubmissionCollectionId(submissionObject.id),
]).pipe(
switchMap(([collection, latestCollectionId]: [Collection, string]) => {
if (hasValue(latestCollectionId)) {
return this.collectionService.findById(latestCollectionId).pipe(
getFirstCompletedRemoteData(),
getRemoteDataPayload(),
);
} else {
return observableOf(collection);
}
}),
switchMap((collection: Collection) => this.dsoBreadcrumbsService.getBreadcrumbs(collection, getDSORoute(collection))),
);
}
}

View File

@@ -1,29 +1,25 @@
<ds-home-coar></ds-home-coar> <ds-home-coar></ds-home-coar>
<ds-home-news></ds-home-news> <ds-home-news></ds-home-news>
<div [ngClass]="showDiscoverFilters ? 'container-fluid' : 'container'"> <ds-configuration-search-page *ngIf="showDiscoverFilters"
<ds-page-with-sidebar [sidebarContent]="sidebar" [sideBarWidth]="showDiscoverFilters ? 3 : 0" [class]="showDiscoverFilters ? 'row mx-3' : ''"> [sideBarWidth]="3"
<div [class.col-sm-12]="showDiscoverFilters"> [showViewModes]="false"
<button *ngIf="showDiscoverFilters && (isXsOrSm$ | async) && sidebarService.isCollapsed" (click)="sidebarService.expand()" [searchEnabled]="false"
class="btn btn-outline-primary d-block ml-auto mb-3"> [inPlaceSearch]="false"
<i class="fas fa-sliders"></i> {{ 'search.sidebar.open' | translate }} [showScopeSelector]="false">
</button> <ng-container searchContentTop *ngTemplateOutlet="homeContent"></ng-container>
<ng-container *ngIf="(site$ | async) as site"> </ds-configuration-search-page>
<ds-view-tracker [object]="site"></ds-view-tracker> <div *ngIf="!showDiscoverFilters" class="container">
</ng-container> <ng-container *ngTemplateOutlet="homeContent"></ng-container>
<ds-search-form [inPlaceSearch]="false"
[searchPlaceholder]="'home.search-form.placeholder' | translate">
</ds-search-form>
<ds-top-level-community-list></ds-top-level-community-list>
<ds-recent-item-list *ngIf="recentSubmissionspageSize>0"></ds-recent-item-list>
</div>
</ds-page-with-sidebar>
</div> </div>
<ds-suggestions-popup></ds-suggestions-popup> <ds-suggestions-popup></ds-suggestions-popup>
<ng-template #sidebar> <ng-template #homeContent>
<div *ngIf="showDiscoverFilters"> <ng-container *ngIf="(site$ | async) as site">
<ds-configuration-search-page [sideBarWidth]="12" [showViewModes]="false" [searchEnabled]="false" <ds-view-tracker [object]="site"></ds-view-tracker>
[inPlaceSearch]="false" [showScopeSelector]="false"> </ng-container>
</ds-configuration-search-page> <ds-search-form [inPlaceSearch]="false"
</div> [searchPlaceholder]="'home.search-form.placeholder' | translate">
</ds-search-form>
<ds-top-level-community-list></ds-top-level-community-list>
<ds-recent-item-list *ngIf="recentSubmissionspageSize>0"></ds-recent-item-list>
</ng-template> </ng-template>

View File

@@ -1,5 +1,6 @@
:host ::ng-deep { @include media-breakpoint-down(md) {
.container-fluid .container { ds-themed-configuration-search-page + .container {
padding: 0; width: 100%;
max-width: none;
} }
} }

View File

@@ -2,6 +2,7 @@ import {
AsyncPipe, AsyncPipe,
NgClass, NgClass,
NgIf, NgIf,
NgTemplateOutlet,
} from '@angular/common'; } from '@angular/common';
import { import {
Component, Component,
@@ -20,10 +21,8 @@ import {
import { Site } from '../core/shared/site.model'; import { Site } from '../core/shared/site.model';
import { SuggestionsPopupComponent } from '../notifications/suggestions-popup/suggestions-popup.component'; import { SuggestionsPopupComponent } from '../notifications/suggestions-popup/suggestions-popup.component';
import { ThemedConfigurationSearchPageComponent } from '../search-page/themed-configuration-search-page.component'; import { ThemedConfigurationSearchPageComponent } from '../search-page/themed-configuration-search-page.component';
import { HostWindowService } from '../shared/host-window.service';
import { ThemedSearchFormComponent } from '../shared/search-form/themed-search-form.component'; import { ThemedSearchFormComponent } from '../shared/search-form/themed-search-form.component';
import { PageWithSidebarComponent } from '../shared/sidebar/page-with-sidebar.component'; import { PageWithSidebarComponent } from '../shared/sidebar/page-with-sidebar.component';
import { SidebarService } from '../shared/sidebar/sidebar.service';
import { ViewTrackerComponent } from '../statistics/angulartics/dspace/view-tracker.component'; import { ViewTrackerComponent } from '../statistics/angulartics/dspace/view-tracker.component';
import { HomeCoarComponent } from './home-coar/home-coar.component'; import { HomeCoarComponent } from './home-coar/home-coar.component';
import { ThemedHomeNewsComponent } from './home-news/themed-home-news.component'; import { ThemedHomeNewsComponent } from './home-news/themed-home-news.component';
@@ -35,27 +34,23 @@ import { ThemedTopLevelCommunityListComponent } from './top-level-community-list
styleUrls: ['./home-page.component.scss'], styleUrls: ['./home-page.component.scss'],
templateUrl: './home-page.component.html', templateUrl: './home-page.component.html',
standalone: true, standalone: true,
imports: [ThemedHomeNewsComponent, NgIf, ViewTrackerComponent, ThemedSearchFormComponent, ThemedTopLevelCommunityListComponent, RecentItemListComponent, AsyncPipe, TranslateModule, NgClass, ThemedConfigurationSearchPageComponent, SuggestionsPopupComponent, PageWithSidebarComponent, HomeCoarComponent], imports: [ThemedHomeNewsComponent, NgTemplateOutlet, NgIf, ViewTrackerComponent, ThemedSearchFormComponent, ThemedTopLevelCommunityListComponent, RecentItemListComponent, AsyncPipe, TranslateModule, NgClass, SuggestionsPopupComponent, ThemedConfigurationSearchPageComponent, PageWithSidebarComponent, HomeCoarComponent],
}) })
export class HomePageComponent implements OnInit { export class HomePageComponent implements OnInit {
site$: Observable<Site>; site$: Observable<Site>;
isXsOrSm$: Observable<boolean>;
recentSubmissionspageSize: number; recentSubmissionspageSize: number;
showDiscoverFilters: boolean; showDiscoverFilters: boolean;
constructor( constructor(
@Inject(APP_CONFIG) protected appConfig: AppConfig, @Inject(APP_CONFIG) protected appConfig: AppConfig,
protected route: ActivatedRoute, protected route: ActivatedRoute,
protected sidebarService: SidebarService,
protected windowService: HostWindowService,
) { ) {
this.recentSubmissionspageSize = this.appConfig.homePage.recentSubmissions.pageSize; this.recentSubmissionspageSize = this.appConfig.homePage.recentSubmissions.pageSize;
this.showDiscoverFilters = this.appConfig.homePage.showDiscoverFilters; this.showDiscoverFilters = this.appConfig.homePage.showDiscoverFilters;
} }
ngOnInit(): void { ngOnInit(): void {
this.isXsOrSm$ = this.windowService.isXsOrSm();
this.site$ = this.route.data.pipe( this.site$ = this.route.data.pipe(
map((data) => data.site as Site), map((data) => data.site as Site),
); );

View File

@@ -3,7 +3,6 @@
[hidePagerWhenSinglePage]="true" [hidePagerWhenSinglePage]="true"
[hidePaginationDetail]="true" [hidePaginationDetail]="true"
[paginationOptions]="options" [paginationOptions]="options"
[pageInfoState]="(objectsRD$ | async)?.payload"
[collectionSize]="(objectsRD$ | async)?.payload?.totalElements"> [collectionSize]="(objectsRD$ | async)?.payload?.totalElements">
<ng-container *ngIf="(loading$ | async) !== true"> <ng-container *ngIf="(loading$ | async) !== true">
<div [id]="bundle.id" class="bundle-bitstreams-list" <div [id]="bundle.id" class="bundle-bitstreams-list"

View File

@@ -10,7 +10,6 @@
<ng-container *ngVar="updates | dsObjectValues as updateValues"> <ng-container *ngVar="updates | dsObjectValues as updateValues">
<ds-pagination <ds-pagination
[paginationOptions]="paginationConfig" [paginationOptions]="paginationConfig"
[pageInfoState]="(relationshipsRd$ | async)?.payload?.pageInfo"
[collectionSize]="(relationshipsRd$ | async)?.payload?.totalElements + (this.nbAddedFields$ | async)" [collectionSize]="(relationshipsRd$ | async)?.payload?.totalElements + (this.nbAddedFields$ | async)"
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true"> [hidePagerWhenSinglePage]="true">

View File

@@ -6,7 +6,6 @@
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true" [hidePagerWhenSinglePage]="true"
[paginationOptions]="originalOptions" [paginationOptions]="originalOptions"
[pageInfoState]="originals"
[collectionSize]="originals?.totalElements" [collectionSize]="originals?.totalElements"
[retainScrollPosition]="true"> [retainScrollPosition]="true">
@@ -49,7 +48,6 @@
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true" [hidePagerWhenSinglePage]="true"
[paginationOptions]="licenseOptions" [paginationOptions]="licenseOptions"
[pageInfoState]="licenses"
[collectionSize]="licenses?.totalElements" [collectionSize]="licenses?.totalElements"
[retainScrollPosition]="true"> [retainScrollPosition]="true">

View File

@@ -10,7 +10,6 @@
[hideGear]="true" [hideGear]="true"
[hidePagerWhenSinglePage]="true" [hidePagerWhenSinglePage]="true"
[paginationOptions]="options" [paginationOptions]="options"
[pageInfoState]="versions"
[collectionSize]="versions?.totalElements" [collectionSize]="versions?.totalElements"
[retainScrollPosition]="true"> [retainScrollPosition]="true">
<table class="table table-striped table-bordered align-middle my-2"> <table class="table table-striped table-bordered align-middle my-2">

View File

@@ -7,6 +7,7 @@ import {
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { ServerResponseService } from 'src/app/core/services/server-response.service';
import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; import { ActivatedRouteStub } from '../../shared/testing/active-router.stub';
import { ObjectNotFoundComponent } from './objectnotfound.component'; import { ObjectNotFoundComponent } from './objectnotfound.component';
@@ -21,6 +22,10 @@ describe('ObjectNotFoundComponent', () => {
const activatedRouteStub = Object.assign(new ActivatedRouteStub(), { const activatedRouteStub = Object.assign(new ActivatedRouteStub(), {
params: observableOf({ id: testUUID, idType: uuidType }), params: observableOf({ id: testUUID, idType: uuidType }),
}); });
const serverResponseServiceStub = jasmine.createSpyObj('ServerResponseService', {
setNotFound: jasmine.createSpy('setNotFound'),
});
const activatedRouteStubHandle = Object.assign(new ActivatedRouteStub(), { const activatedRouteStubHandle = Object.assign(new ActivatedRouteStub(), {
params: observableOf({ id: handleId, idType: handlePrefix }), params: observableOf({ id: handleId, idType: handlePrefix }),
}); });
@@ -31,6 +36,7 @@ describe('ObjectNotFoundComponent', () => {
TranslateModule.forRoot(), TranslateModule.forRoot(),
ObjectNotFoundComponent, ObjectNotFoundComponent,
], providers: [ ], providers: [
{ provide: ServerResponseService, useValue: serverResponseServiceStub } ,
{ provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: ActivatedRoute, useValue: activatedRouteStub },
], ],
schemas: [NO_ERRORS_SCHEMA], schemas: [NO_ERRORS_SCHEMA],
@@ -52,6 +58,10 @@ describe('ObjectNotFoundComponent', () => {
expect(comp.idType).toEqual(uuidType); expect(comp.idType).toEqual(uuidType);
expect(comp.missingItem).toEqual('uuid: ' + testUUID); expect(comp.missingItem).toEqual('uuid: ' + testUUID);
}); });
it('should call serverResponseService.setNotFound', () => {
expect(serverResponseServiceStub.setNotFound).toHaveBeenCalled();
});
}); });
describe( 'legacy handle request', () => { describe( 'legacy handle request', () => {
@@ -61,6 +71,7 @@ describe('ObjectNotFoundComponent', () => {
TranslateModule.forRoot(), TranslateModule.forRoot(),
ObjectNotFoundComponent, ObjectNotFoundComponent,
], providers: [ ], providers: [
{ provide: ServerResponseService, useValue: serverResponseServiceStub },
{ provide: ActivatedRoute, useValue: activatedRouteStubHandle }, { provide: ActivatedRoute, useValue: activatedRouteStubHandle },
], ],
schemas: [NO_ERRORS_SCHEMA], schemas: [NO_ERRORS_SCHEMA],
@@ -78,6 +89,10 @@ describe('ObjectNotFoundComponent', () => {
expect(comp.idType).toEqual(handlePrefix); expect(comp.idType).toEqual(handlePrefix);
expect(comp.missingItem).toEqual('handle: ' + handlePrefix + '/' + handleId); expect(comp.missingItem).toEqual('handle: ' + handlePrefix + '/' + handleId);
}); });
it('should call serverResponseService.setNotFound', () => {
expect(serverResponseServiceStub.setNotFound).toHaveBeenCalled();
});
}); });
}); });

View File

@@ -9,6 +9,7 @@ import {
RouterLink, RouterLink,
} from '@angular/router'; } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { ServerResponseService } from 'src/app/core/services/server-response.service';
/** /**
* This component representing the `PageNotFound` DSpace page. * This component representing the `PageNotFound` DSpace page.
@@ -35,7 +36,7 @@ export class ObjectNotFoundComponent implements OnInit {
* @param {AuthService} authservice * @param {AuthService} authservice
* @param {ServerResponseService} responseService * @param {ServerResponseService} responseService
*/ */
constructor(private route: ActivatedRoute) { constructor(private route: ActivatedRoute, private serverResponseService: ServerResponseService) {
route.params.subscribe((params) => { route.params.subscribe((params) => {
this.idType = params.idType; this.idType = params.idType;
this.id = params.id; this.id = params.id;
@@ -48,6 +49,7 @@ export class ObjectNotFoundComponent implements OnInit {
} else { } else {
this.missingItem = 'handle: ' + this.idType + '/' + this.id; this.missingItem = 'handle: ' + this.idType + '/' + this.id;
} }
this.serverResponseService.setNotFound();
} }
} }

View File

@@ -7,12 +7,12 @@
[formControl]="input" ngbAutofocus (keyup.enter)="selectSingleResult()"> [formControl]="input" ngbAutofocus (keyup.enter)="selectSingleResult()">
</div> </div>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<div class="scrollable-menu list-group"> <div id="scrollable-menu-dso-selector-{{randomSeed}}" class="scrollable-menu list-group">
<div <div
infiniteScroll infiniteScroll
[infiniteScrollDistance]="1" [infiniteScrollDistance]="1"
[infiniteScrollThrottle]="0" [infiniteScrollThrottle]="0"
[infiniteScrollContainer]="'.scrollable-menu'" [infiniteScrollContainer]="'#scrollable-menu-dso-selector-' + randomSeed"
[fromRoot]="true" [fromRoot]="true"
(scrolled)="onScrollDown()"> (scrolled)="onScrollDown()">
<ng-container *ngIf="listEntries$ | async"> <ng-container *ngIf="listEntries$ | async">

View File

@@ -172,6 +172,11 @@ export class DSOSelectorComponent implements OnInit, OnDestroy {
*/ */
public subs: Subscription[] = []; public subs: Subscription[] = [];
/**
* Random seed of 4 characters to avoid duplicate ids
*/
randomSeed: string = Math.random().toString(36).substring(2, 6);
constructor( constructor(
protected searchService: SearchService, protected searchService: SearchService,
protected notifcationsService: NotificationsService, protected notifcationsService: NotificationsService,

View File

@@ -1,6 +1,5 @@
<ds-pagination <ds-pagination
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="objects?.payload"
[collectionSize]="objects?.payload?.totalElements" [collectionSize]="objects?.payload?.totalElements"
[sortOptions]="sortConfig" [sortOptions]="sortConfig"
[objects]="objects" [objects]="objects"

View File

@@ -1,6 +1,5 @@
<ds-pagination <ds-pagination
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="objects?.payload"
[collectionSize]="objects?.payload?.totalElements" [collectionSize]="objects?.payload?.totalElements"
[sortOptions]="sortConfig" [sortOptions]="sortConfig"
[hideGear]="hideGear" [hideGear]="hideGear"

View File

@@ -1,6 +1,5 @@
<ds-pagination <ds-pagination
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="$any(objects?.payload)"
[collectionSize]="objects?.payload?.totalElements" [collectionSize]="objects?.payload?.totalElements"
[objects]="objects" [objects]="objects"
[sortOptions]="sortConfig" [sortOptions]="sortConfig"

View File

@@ -3,7 +3,6 @@
*ngIf="collectionsRD?.payload?.totalElements > 0 || collectionsRD?.payload?.page?.length > 0" *ngIf="collectionsRD?.payload?.totalElements > 0 || collectionsRD?.payload?.page?.length > 0"
[paginationOptions]="paginationOptions" [paginationOptions]="paginationOptions"
[sortOptions]="sortOptions" [sortOptions]="sortOptions"
[pageInfoState]="collectionsRD?.payload"
[collectionSize]="collectionsRD?.payload?.totalElements" [collectionSize]="collectionsRD?.payload?.totalElements"
[hidePagerWhenSinglePage]="true" [hidePagerWhenSinglePage]="true"
[hideGear]="true"> [hideGear]="true">
@@ -16,9 +15,9 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let collection of collectionsRD?.payload?.page"> <tr *ngFor="let selectCollection of selectCollections$ | async">
<td><input #selectCollectionBtn [attr.aria-label]="(selectCollectionBtn.checked ? 'collection.select.table.deselect' : 'collection.select.table.select') | translate" class="collection-checkbox" [ngModel]="getSelected(collection.id) | async" (change)="switch(collection.id)" type="checkbox" name="{{collection.id}}"></td> <td><input #selectCollectionBtn [attr.aria-label]="(selectCollectionBtn.checked ? 'collection.select.table.deselect' : 'collection.select.table.select') | translate" [disabled]="(selectCollection.canSelect$ | async) === false" class="collection-checkbox" [ngModel]="selectCollection.selected$ | async" (change)="switch(selectCollection.dso.id)" type="checkbox" name="{{selectCollection.dso.id}}"></td>
<td><a [routerLink]="['/collections', collection.id]">{{ dsoNameService.getName(collection) }}</a></td> <td><a [routerLink]="selectCollection.route">{{ dsoNameService.getName(selectCollection.dso) }}</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -4,20 +4,31 @@ import {
NgFor, NgFor,
NgIf, NgIf,
} from '@angular/common'; } from '@angular/common';
import { Component } from '@angular/core'; import {
Component,
OnInit,
} from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import {
map,
Observable,
} from 'rxjs';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; import { getCollectionPageRoute } from '../../../collection-page/collection-page-routing-paths';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { PaginatedList } from '../../../core/data/paginated-list.model';
import { Collection } from '../../../core/shared/collection.model'; import { Collection } from '../../../core/shared/collection.model';
import { isNotEmpty } from '../../empty.util'; import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators';
import {
hasValueOperator,
isNotEmpty,
} from '../../empty.util';
import { ErrorComponent } from '../../error/error.component'; import { ErrorComponent } from '../../error/error.component';
import { ThemedLoadingComponent } from '../../loading/themed-loading.component'; import { ThemedLoadingComponent } from '../../loading/themed-loading.component';
import { PaginationComponent } from '../../pagination/pagination.component'; import { PaginationComponent } from '../../pagination/pagination.component';
import { VarDirective } from '../../utils/var.directive'; import { VarDirective } from '../../utils/var.directive';
import { ObjectSelectService } from '../object-select.service'; import { DSpaceObjectSelect } from '../object-select.model';
import { ObjectSelectComponent } from '../object-select/object-select.component'; import { ObjectSelectComponent } from '../object-select/object-select.component';
@Component({ @Component({
@@ -31,21 +42,29 @@ import { ObjectSelectComponent } from '../object-select/object-select.component'
/** /**
* A component used to select collections from a specific list and returning the UUIDs of the selected collections * A component used to select collections from a specific list and returning the UUIDs of the selected collections
*/ */
export class CollectionSelectComponent extends ObjectSelectComponent<Collection> { export class CollectionSelectComponent extends ObjectSelectComponent<Collection> implements OnInit {
constructor( /**
protected objectSelectService: ObjectSelectService, * Collection of all the data that is used to display the {@link Collection} in the HTML.
protected authorizationService: AuthorizationDataService, * By collecting this data here it doesn't need to be recalculated on evey change detection.
public dsoNameService: DSONameService, */
) { selectCollections$: Observable<DSpaceObjectSelect<Collection>[]>;
super(objectSelectService, authorizationService);
}
ngOnInit(): void { ngOnInit(): void {
super.ngOnInit(); super.ngOnInit();
if (!isNotEmpty(this.confirmButton)) { if (!isNotEmpty(this.confirmButton)) {
this.confirmButton = 'collection.select.confirm'; this.confirmButton = 'collection.select.confirm';
} }
this.selectCollections$ = this.dsoRD$.pipe(
hasValueOperator(),
getAllSucceededRemoteDataPayload(),
map((collections: PaginatedList<Collection>) => collections.page.map((collection: Collection) => Object.assign(new DSpaceObjectSelect<Collection>(), {
dso: collection,
canSelect$: this.canSelect(collection),
selected$: this.getSelected(collection.id),
route: getCollectionPageRoute(collection.id),
} as DSpaceObjectSelect<Collection>))),
);
} }
} }

View File

@@ -3,7 +3,6 @@
*ngIf="itemsRD?.payload?.totalElements > 0" *ngIf="itemsRD?.payload?.totalElements > 0"
[paginationOptions]="paginationOptions" [paginationOptions]="paginationOptions"
[sortOptions]="sortOptions" [sortOptions]="sortOptions"
[pageInfoState]="itemsRD?.payload"
[collectionSize]="itemsRD?.payload?.totalElements" [collectionSize]="itemsRD?.payload?.totalElements"
[hidePagerWhenSinglePage]="true" [hidePagerWhenSinglePage]="true"
[hideGear]="true"> [hideGear]="true">
@@ -18,17 +17,17 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let item of itemsRD?.payload?.page"> <tr *ngFor="let selectItem of selectItems$ | async">
<td><input #selectItemBtn [attr.aria-label]="(selectItemBtn.checked ? 'item.select.table.deselect' : 'item.select.table.select') | translate" [disabled]="(canSelect(item) | async) !== true" class="item-checkbox" [ngModel]="getSelected(item.id) | async" (change)="switch(item.id)" type="checkbox" name="{{item.id}}"></td> <td><input #selectItemBtn [attr.aria-label]="(selectItemBtn.checked ? 'item.select.table.deselect' : 'item.select.table.select') | translate" [disabled]="(selectItem.canSelect$ | async) === false" class="item-checkbox" [ngModel]="selectItem.selected$ | async" (change)="switch(selectItem.dso.id)" type="checkbox" name="{{selectItem.dso.id}}"></td>
<td *ngIf="!hideCollection"> <td *ngIf="!hideCollection">
<span *ngVar="(item.owningCollection | async)?.payload as collection"> <span *ngVar="(selectItem.dso.owningCollection | async)?.payload as collection">
<a *ngIf="collection" [routerLink]="['/collections', collection?.id]"> <a *ngIf="collection" [routerLink]="['/collections', collection?.id]">
{{ dsoNameService.getName(collection) }} {{ dsoNameService.getName(collection) }}
</a> </a>
</span> </span>
</td> </td>
<td><span *ngIf="item.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])">{{item.firstMetadataValue(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])}}</span></td> <td><span *ngIf="selectItem.dso.hasMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])">{{selectItem.dso.firstMetadataValue(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])}}</span></td>
<td><a [routerLink]="[(itemPageRoutes$ | async)[item.id]]">{{ dsoNameService.getName(item) }}</a></td> <td><a [routerLink]="selectItem.route">{{ dsoNameService.getName(selectItem.dso) }}</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -187,15 +187,16 @@ describe('ItemSelectComponent', () => {
beforeEach(() => { beforeEach(() => {
comp.featureId = FeatureID.CanManageMappings; comp.featureId = FeatureID.CanManageMappings;
spyOn(authorizationDataService, 'isAuthorized').and.returnValue(of(false)); spyOn(authorizationDataService, 'isAuthorized').and.returnValue(of(false));
comp.ngOnInit();
}); });
it('should disable the checkbox', waitForAsync(() => { it('should disable the checkbox', waitForAsync(async () => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { await fixture.whenStable();
const checkbox = fixture.debugElement.query(By.css('input.item-checkbox')).nativeElement;
expect(authorizationDataService.isAuthorized).toHaveBeenCalled(); const checkbox = fixture.debugElement.query(By.css('input.item-checkbox')).nativeElement;
expect(checkbox.disabled).toBeTrue(); expect(authorizationDataService.isAuthorized).toHaveBeenCalled();
}); expect(checkbox.disabled).toBeTrue();
})); }));
}); });
}); });

View File

@@ -7,6 +7,7 @@ import {
import { import {
Component, Component,
Input, Input,
OnInit,
} from '@angular/core'; } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
@@ -14,8 +15,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; import { PaginatedList } from '../../../core/data/paginated-list.model';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators'; import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators';
import { getItemPageRoute } from '../../../item-page/item-page-routing-paths'; import { getItemPageRoute } from '../../../item-page/item-page-routing-paths';
@@ -27,7 +27,7 @@ import { ErrorComponent } from '../../error/error.component';
import { ThemedLoadingComponent } from '../../loading/themed-loading.component'; import { ThemedLoadingComponent } from '../../loading/themed-loading.component';
import { PaginationComponent } from '../../pagination/pagination.component'; import { PaginationComponent } from '../../pagination/pagination.component';
import { VarDirective } from '../../utils/var.directive'; import { VarDirective } from '../../utils/var.directive';
import { ObjectSelectService } from '../object-select.service'; import { DSpaceObjectSelect } from '../object-select.model';
import { ObjectSelectComponent } from '../object-select/object-select.component'; import { ObjectSelectComponent } from '../object-select/object-select.component';
@Component({ @Component({
@@ -40,7 +40,7 @@ import { ObjectSelectComponent } from '../object-select/object-select.component'
/** /**
* A component used to select items from a specific list and returning the UUIDs of the selected items * A component used to select items from a specific list and returning the UUIDs of the selected items
*/ */
export class ItemSelectComponent extends ObjectSelectComponent<Item> { export class ItemSelectComponent extends ObjectSelectComponent<Item> implements OnInit {
/** /**
* Whether or not to hide the collection column * Whether or not to hide the collection column
@@ -49,35 +49,25 @@ export class ItemSelectComponent extends ObjectSelectComponent<Item> {
hideCollection = false; hideCollection = false;
/** /**
* The routes to the items their pages * Collection of all the data that is used to display the {@link Item} in the HTML.
* Key: Item ID * By collecting this data here it doesn't need to be recalculated on evey change detection.
* Value: Route to item page
*/ */
itemPageRoutes$: Observable<{ selectItems$: Observable<DSpaceObjectSelect<Item>[]>;
[itemId: string]: string
}>;
constructor(
protected objectSelectService: ObjectSelectService,
protected authorizationService: AuthorizationDataService,
public dsoNameService: DSONameService,
) {
super(objectSelectService, authorizationService);
}
ngOnInit(): void { ngOnInit(): void {
super.ngOnInit(); super.ngOnInit();
if (!isNotEmpty(this.confirmButton)) { if (!isNotEmpty(this.confirmButton)) {
this.confirmButton = 'item.select.confirm'; this.confirmButton = 'item.select.confirm';
} }
this.itemPageRoutes$ = this.dsoRD$.pipe( this.selectItems$ = this.dsoRD$.pipe(
hasValueOperator(), hasValueOperator(),
getAllSucceededRemoteDataPayload(), getAllSucceededRemoteDataPayload(),
map((items) => { map((items: PaginatedList<Item>) => items.page.map((item: Item) => Object.assign(new DSpaceObjectSelect<Item>(), {
const itemPageRoutes = {}; dso: item,
items.page.forEach((item) => itemPageRoutes[item.uuid] = getItemPageRoute(item)); canSelect$: this.canSelect(item),
return itemPageRoutes; selected$: this.getSelected(item.id),
}), route: getItemPageRoute(item),
} as DSpaceObjectSelect<Item>))),
); );
} }

View File

@@ -0,0 +1,30 @@
import { Observable } from 'rxjs';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
/**
* Class used to collect all the data that that is used by the {@link ObjectSelectComponent} in the HTML.
*/
export class DSpaceObjectSelect<T extends DSpaceObject> {
/**
* The {@link DSpaceObject} to display
*/
dso: T;
/**
* Whether the {@link DSpaceObject} can be selected
*/
canSelect$: Observable<boolean>;
/**
* Whether the {@link DSpaceObject} is selected
*/
selected$: Observable<boolean>;
/**
* The {@link DSpaceObject}'s route
*/
route: string;
}

View File

@@ -15,6 +15,7 @@ import {
take, take,
} from 'rxjs/operators'; } from 'rxjs/operators';
import { DSONameService } from '../../../core/breadcrumbs/dso-name.service';
import { SortOptions } from '../../../core/cache/models/sort-options.model'; import { SortOptions } from '../../../core/cache/models/sort-options.model';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
@@ -31,7 +32,7 @@ import { ObjectSelectService } from '../object-select.service';
selector: 'ds-object-select-abstract', selector: 'ds-object-select-abstract',
template: '', template: '',
}) })
export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestroy { export abstract class ObjectSelectComponent<TDomain extends DSpaceObject> implements OnInit, OnDestroy {
/** /**
* A unique key used for the object select service * A unique key used for the object select service
@@ -102,8 +103,11 @@ export abstract class ObjectSelectComponent<TDomain> implements OnInit, OnDestro
*/ */
selectedIds$: Observable<string[]>; selectedIds$: Observable<string[]>;
constructor(protected objectSelectService: ObjectSelectService, constructor(
protected authorizationService: AuthorizationDataService) { protected objectSelectService: ObjectSelectService,
protected authorizationService: AuthorizationDataService,
public dsoNameService: DSONameService,
) {
} }
ngOnInit(): void { ngOnInit(): void {

View File

@@ -1,6 +1,5 @@
<ds-pagination <ds-pagination
[paginationOptions]="config" [paginationOptions]="config"
[pageInfoState]="objects?.payload"
[collectionSize]="objects?.payload?.totalElements" [collectionSize]="objects?.payload?.totalElements"
[sortOptions]="sortConfig" [sortOptions]="sortConfig"
[hideGear]="hideGear" [hideGear]="hideGear"

View File

@@ -39,13 +39,11 @@ import { PaginatedList } from '../../core/data/paginated-list.model';
import { RemoteData } from '../../core/data/remote-data'; import { RemoteData } from '../../core/data/remote-data';
import { PaginationService } from '../../core/pagination/pagination.service'; import { PaginationService } from '../../core/pagination/pagination.service';
import { PaginationRouteParams } from '../../core/pagination/pagination-route-params.interface'; import { PaginationRouteParams } from '../../core/pagination/pagination-route-params.interface';
import { PageInfo } from '../../core/shared/page-info.model';
import { ViewMode } from '../../core/shared/view-mode.model'; import { ViewMode } from '../../core/shared/view-mode.model';
import { hasValue } from '../empty.util'; import { hasValue } from '../empty.util';
import { HostWindowService } from '../host-window.service'; import { HostWindowService } from '../host-window.service';
import { ListableObject } from '../object-collection/shared/listable-object.model'; import { ListableObject } from '../object-collection/shared/listable-object.model';
import { RSSComponent } from '../rss-feed/rss.component'; import { RSSComponent } from '../rss-feed/rss.component';
import { HostWindowState } from '../search/host-window.reducer';
import { EnumKeysPipe } from '../utils/enum-keys-pipe'; import { EnumKeysPipe } from '../utils/enum-keys-pipe';
import { PaginationComponentOptions } from './pagination-component-options.model'; import { PaginationComponentOptions } from './pagination-component-options.model';
@@ -73,11 +71,6 @@ export class PaginationComponent implements OnDestroy, OnInit {
*/ */
@Input() collectionSize: number; @Input() collectionSize: number;
/**
* Page state of a Remote paginated objects.
*/
@Input() pageInfoState: Observable<PageInfo> = undefined;
/** /**
* Configuration for the NgbPagination component. * Configuration for the NgbPagination component.
*/ */
@@ -167,18 +160,13 @@ export class PaginationComponent implements OnDestroy, OnInit {
/** /**
* Current page. * Current page.
*/ */
public currentPage$; public currentPage$: Observable<number>;
/** /**
* Current page in the state of a Remote paginated objects. * Current page in the state of a Remote paginated objects.
*/ */
public currentPageState: number = undefined; public currentPageState: number = undefined;
/**
* An observable of HostWindowState type
*/
public hostWindow: Observable<HostWindowState>;
/** /**
* ID for the pagination instance. This ID is used in the routing to retrieve the pagination options. * ID for the pagination instance. This ID is used in the routing to retrieve the pagination options.
* This ID needs to be unique between different pagination components when more than one will be displayed on the same page. * This ID needs to be unique between different pagination components when more than one will be displayed on the same page.
@@ -268,7 +256,7 @@ export class PaginationComponent implements OnDestroy, OnInit {
map((currentPagination) => currentPagination.pageSize), map((currentPagination) => currentPagination.pageSize),
); );
let sortOptions; let sortOptions: SortOptions;
if (this.sortOptions) { if (this.sortOptions) {
sortOptions = this.sortOptions; sortOptions = this.sortOptions;
} else { } else {
@@ -282,16 +270,6 @@ export class PaginationComponent implements OnDestroy, OnInit {
); );
} }
/**
* @param cdRef
* ChangeDetectorRef is a singleton service provided by Angular.
* @param route
* Route is a singleton service provided by Angular.
* @param router
* Router is a singleton service provided by Angular.
* @param hostWindowService
* the HostWindowService singleton.
*/
constructor(private cdRef: ChangeDetectorRef, constructor(private cdRef: ChangeDetectorRef,
private paginationService: PaginationService, private paginationService: PaginationService,
public hostWindowService: HostWindowService) { public hostWindowService: HostWindowService) {
@@ -330,17 +308,6 @@ export class PaginationComponent implements OnDestroy, OnInit {
this.emitPaginationChange(); this.emitPaginationChange();
} }
/**
* Method to change the route to the given sort field
*
* @param sortField
* The sort field being navigated to.
*/
public doSortFieldChange(field: string) {
this.updateParams({ page: 1, sortField: field });
this.emitPaginationChange();
}
/** /**
* Method to emit a general pagination change event * Method to emit a general pagination change event
*/ */
@@ -364,10 +331,10 @@ export class PaginationComponent implements OnDestroy, OnInit {
if (collectionSize) { if (collectionSize) {
showingDetails = this.paginationService.getCurrentPagination(this.id, this.paginationOptions).pipe( showingDetails = this.paginationService.getCurrentPagination(this.id, this.paginationOptions).pipe(
map((currentPaginationOptions) => { map((currentPaginationOptions) => {
let lastItem; let lastItem: number;
const pageMax = currentPaginationOptions.pageSize * currentPaginationOptions.currentPage; const pageMax = currentPaginationOptions.pageSize * currentPaginationOptions.currentPage;
const firstItem = currentPaginationOptions.pageSize * (currentPaginationOptions.currentPage - 1) + 1; const firstItem: number = currentPaginationOptions.pageSize * (currentPaginationOptions.currentPage - 1) + 1;
if (collectionSize > pageMax) { if (collectionSize > pageMax) {
lastItem = pageMax; lastItem = pageMax;
} else { } else {

View File

@@ -33,6 +33,7 @@
| translate}} | translate}}
</button> </button>
</div> </div>
<ng-content select="[searchContentTop]"></ng-content>
<ds-search-results *ngIf="inPlaceSearch" <ds-search-results *ngIf="inPlaceSearch"
[searchResults]="resultsRD$ | async" [searchResults]="resultsRD$ | async"
[searchConfig]="searchOptions$ | async" [searchConfig]="searchOptions$ | async"

View File

@@ -1,7 +1,12 @@
import { HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { Store } from '@ngrx/store'; import {
createSelector,
MemoizedSelector,
select,
Store,
} from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { import {
Observable, Observable,
@@ -71,6 +76,20 @@ import {
SubmissionState, SubmissionState,
} from './submission.reducers'; } from './submission.reducers';
function getSubmissionSelector(submissionId: string): MemoizedSelector<SubmissionState, SubmissionObjectEntry> {
return createSelector(
submissionSelector,
(state: SubmissionState) => state.objects[submissionId],
);
}
function getSubmissionCollectionIdSelector(submissionId: string): MemoizedSelector<SubmissionState, string> {
return createSelector(
getSubmissionSelector(submissionId),
(submission: SubmissionObjectEntry) => submission?.collection,
);
}
/** /**
* A service that provides methods used in submission process. * A service that provides methods used in submission process.
*/ */
@@ -120,10 +139,19 @@ export class SubmissionService {
* @param collectionId * @param collectionId
* The collection id * The collection id
*/ */
changeSubmissionCollection(submissionId, collectionId) { changeSubmissionCollection(submissionId: string, collectionId: string): void {
this.store.dispatch(new ChangeSubmissionCollectionAction(submissionId, collectionId)); this.store.dispatch(new ChangeSubmissionCollectionAction(submissionId, collectionId));
} }
/**
* Listen to collection changes for a certain {@link SubmissionObject}
*
* @param submissionId The submission id
*/
getSubmissionCollectionId(submissionId: string): Observable<string> {
return this.store.pipe(select(getSubmissionCollectionIdSelector(submissionId)));
}
/** /**
* Perform a REST call to create a new workspaceitem and return response * Perform a REST call to create a new workspaceitem and return response
* *

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { BreadcrumbConfig } from '../breadcrumbs/breadcrumb/breadcrumb-config.model';
import { SubmissionObject } from '../core/submission/models/submission-object.model';
import { SubmissionParentBreadcrumbResolver } from '../core/submission/resolver/submission-parent-breadcrumb.resolver';
import { SubmissionParentBreadcrumbsService } from '../core/submission/submission-parent-breadcrumb.service';
import { WorkflowItemDataService } from '../core/submission/workflowitem-data.service';
/**
* This class represents a resolver that retrieves the breadcrumbs of the workflow item
*/
@Injectable({
providedIn: 'root',
})
export class ItemFromWorkflowBreadcrumbResolver extends SubmissionParentBreadcrumbResolver implements Resolve<BreadcrumbConfig<SubmissionObject>> {
constructor(
protected dataService: WorkflowItemDataService,
protected breadcrumbService: SubmissionParentBreadcrumbsService,
) {
super(dataService, breadcrumbService);
}
}

View File

@@ -6,6 +6,7 @@ import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item-
import { ThemedSubmissionEditComponent } from '../submission/edit/themed-submission-edit.component'; import { ThemedSubmissionEditComponent } from '../submission/edit/themed-submission-edit.component';
import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component'; import { AdvancedWorkflowActionPageComponent } from './advanced-workflow-action/advanced-workflow-action-page/advanced-workflow-action-page.component';
import { itemFromWorkflowResolver } from './item-from-workflow.resolver'; import { itemFromWorkflowResolver } from './item-from-workflow.resolver';
import { ItemFromWorkflowBreadcrumbResolver } from './item-from-workflow-breadcrumb.resolver';
import { ThemedWorkflowItemDeleteComponent } from './workflow-item-delete/themed-workflow-item-delete.component'; import { ThemedWorkflowItemDeleteComponent } from './workflow-item-delete/themed-workflow-item-delete.component';
import { workflowItemPageResolver } from './workflow-item-page.resolver'; import { workflowItemPageResolver } from './workflow-item-page.resolver';
import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/themed-workflow-item-send-back.component'; import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/themed-workflow-item-send-back.component';
@@ -20,7 +21,10 @@ import {
export const ROUTES: Routes = [ export const ROUTES: Routes = [
{ {
path: ':id', path: ':id',
resolve: { wfi: workflowItemPageResolver }, resolve: {
breadcrumb: ItemFromWorkflowBreadcrumbResolver,
wfi: workflowItemPageResolver,
},
children: [ children: [
{ {
canActivate: [authenticatedGuard], canActivate: [authenticatedGuard],

View File

@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { BreadcrumbConfig } from '../breadcrumbs/breadcrumb/breadcrumb-config.model';
import { SubmissionObject } from '../core/submission/models/submission-object.model';
import { SubmissionParentBreadcrumbResolver } from '../core/submission/resolver/submission-parent-breadcrumb.resolver';
import { SubmissionParentBreadcrumbsService } from '../core/submission/submission-parent-breadcrumb.service';
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
/**
* This class represents a resolver that retrieves the breadcrumbs of the workspace item
*/
@Injectable({
providedIn: 'root',
})
export class ItemFromWorkspaceBreadcrumbResolver extends SubmissionParentBreadcrumbResolver implements Resolve<BreadcrumbConfig<SubmissionObject>> {
constructor(
protected dataService: WorkspaceitemDataService,
protected breadcrumbService: SubmissionParentBreadcrumbsService,
) {
super(dataService, breadcrumbService);
}
}

View File

@@ -5,6 +5,7 @@ import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso
import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item-page.component'; import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item-page.component';
import { ThemedSubmissionEditComponent } from '../submission/edit/themed-submission-edit.component'; import { ThemedSubmissionEditComponent } from '../submission/edit/themed-submission-edit.component';
import { itemFromWorkspaceResolver } from './item-from-workspace.resolver'; import { itemFromWorkspaceResolver } from './item-from-workspace.resolver';
import { ItemFromWorkspaceBreadcrumbResolver } from './item-from-workspace-breadcrumb.resolver';
import { workspaceItemPageResolver } from './workspace-item-page.resolver'; import { workspaceItemPageResolver } from './workspace-item-page.resolver';
import { ThemedWorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/themed-workspaceitems-delete-page.component'; import { ThemedWorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/themed-workspaceitems-delete-page.component';
@@ -15,7 +16,10 @@ export const ROUTES: Route[] = [
}, },
{ {
path: ':id', path: ':id',
resolve: { wsi: workspaceItemPageResolver }, resolve: {
breadcrumb: ItemFromWorkspaceBreadcrumbResolver,
wsi: workspaceItemPageResolver,
},
children: [ children: [
{ {
canActivate: [authenticatedGuard], canActivate: [authenticatedGuard],

View File

@@ -1108,6 +1108,8 @@
"claimed-declined-task-search-result-list-element.title": "Declined, sent back to Review Manager's workflow", "claimed-declined-task-search-result-list-element.title": "Declined, sent back to Review Manager's workflow",
"collection.create.breadcrumbs": "Create collection",
"collection.browse.logo": "Browse for a collection logo", "collection.browse.logo": "Browse for a collection logo",
"collection.create.head": "Create a Collection", "collection.create.head": "Create a Collection",
@@ -1398,6 +1400,8 @@
"community.subcoms-cols.breadcrumbs": "Subcommunities and Collections", "community.subcoms-cols.breadcrumbs": "Subcommunities and Collections",
"community.create.breadcrumbs": "Create Community",
"community.create.head": "Create a Community", "community.create.head": "Create a Community",
"community.create.notifications.success": "Successfully created the Community", "community.create.notifications.success": "Successfully created the Community",