diff --git a/src/app/access-control/bulk-access/browse/bulk-access-browse.component.html b/src/app/access-control/bulk-access/browse/bulk-access-browse.component.html index e24eee3a24..f96ddf4a23 100644 --- a/src/app/access-control/bulk-access/browse/bulk-access-browse.component.html +++ b/src/app/access-control/bulk-access/browse/bulk-access-browse.component.html @@ -37,7 +37,6 @@ diff --git a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html index b4777a56a6..b4c0781ac7 100644 --- a/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html +++ b/src/app/access-control/epeople-registry/eperson-form/eperson-form.component.html @@ -52,7 +52,6 @@ @@ -86,7 +85,6 @@ diff --git a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html index ab7bd84f6b..66404bde0d 100644 --- a/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html +++ b/src/app/access-control/group-registry/group-form/subgroup-list/subgroups-list.component.html @@ -5,7 +5,6 @@ @@ -84,7 +83,6 @@ diff --git a/src/app/access-control/group-registry/groups-registry.component.html b/src/app/access-control/group-registry/groups-registry.component.html index 5d9174cde2..c2d998d954 100644 --- a/src/app/access-control/group-registry/groups-registry.component.html +++ b/src/app/access-control/group-registry/groups-registry.component.html @@ -37,7 +37,6 @@ diff --git a/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.html b/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.html index 0aaa39bda2..3dcce659c2 100644 --- a/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.html +++ b/src/app/admin/admin-ldn-services/ldn-services-directory/ldn-services-directory.component.html @@ -10,7 +10,6 @@ [collectionSize]="(ldnServicesRD$ | async)?.payload?.totalElements" [hideGear]="true" [hidePagerWhenSinglePage]="true" - [pageInfoState]="(ldnServicesRD$ | async)?.payload" [paginationOptions]="pageConfig">
diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.html b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.html index abb0a2fd53..912e931f40 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.html +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.html @@ -11,7 +11,6 @@ diff --git a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.html b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.html index 97123d29a5..f748279a1d 100644 --- a/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.html +++ b/src/app/admin/admin-registries/metadata-schema/metadata-schema.component.html @@ -16,7 +16,6 @@ diff --git a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts index 7f8a6076a1..71ba467084 100644 --- a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts +++ b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts @@ -23,7 +23,6 @@ import { SortDirection, SortOptions, } from '../../core/cache/models/sort-options.model'; -import { CollectionDataService } from '../../core/data/collection-data.service'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-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 { HostWindowService } from '../../shared/host-window.service'; import { LoadingComponent } from '../../shared/loading/loading.component'; +import { getMockThemeService } from '../../shared/mocks/theme-service.mock'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { ItemSelectComponent } from '../../shared/object-select/item-select/item-select.component'; 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 { SearchServiceStub } from '../../shared/testing/search-service.stub'; import { createPaginatedList } from '../../shared/testing/utils.test'; +import { ThemeService } from '../../shared/theme-support/theme.service'; import { EnumKeysPipe } from '../../shared/utils/enum-keys-pipe'; import { VarDirective } from '../../shared/utils/var.directive'; import { CollectionItemMapperComponent } from './collection-item-mapper.component'; @@ -190,7 +191,6 @@ describe('CollectionItemMapperComponent', () => { { provide: SearchService, useValue: searchServiceStub }, { provide: NotificationsService, useValue: new NotificationsServiceStub() }, { provide: ItemDataService, useValue: itemDataServiceStub }, - { provide: CollectionDataService, useValue: collectionDataServiceStub }, { provide: TranslateService, useValue: translateServiceStub }, { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: ObjectSelectService, useValue: new ObjectSelectServiceStub() }, @@ -199,6 +199,7 @@ describe('CollectionItemMapperComponent', () => { { provide: GroupDataService, useValue: groupDataService }, { provide: LinkHeadService, useValue: linkHeadService }, { provide: ConfigurationDataService, useValue: configurationDataService }, + { provide: ThemeService, useValue: getMockThemeService() }, ], }).overrideComponent(CollectionItemMapperComponent, { set: { diff --git a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts index 709efcdd00..c60e23e81e 100644 --- a/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts +++ b/src/app/collection-page/collection-item-mapper/collection-item-mapper.component.ts @@ -35,7 +35,6 @@ import { SortDirection, SortOptions, } 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 { ItemDataService } from '../../core/data/item-data.service'; import { PaginatedList } from '../../core/data/paginated-list.model'; @@ -150,7 +149,6 @@ export class CollectionItemMapperComponent implements OnInit { private searchService: SearchService, private notificationsService: NotificationsService, private itemDataService: ItemDataService, - private collectionDataService: CollectionDataService, private translateService: TranslateService, private dsoNameService: DSONameService) { } @@ -187,6 +185,8 @@ export class CollectionItemMapperComponent implements OnInit { this.shouldUpdate$.next(false); } return this.itemDataService.findListByHref(collectionRD.payload._links.mappedItems.href, Object.assign(options, { + currentPage: options.pagination.currentPage, + elementsPerPage: options.pagination.pageSize, sort: this.defaultSortOptions, }),!shouldUpdate, false, followLink('owningCollection')).pipe( getAllSucceededRemoteData(), diff --git a/src/app/collection-page/collection-page-routes.ts b/src/app/collection-page/collection-page-routes.ts index 91e9d59992..889b910d6a 100644 --- a/src/app/collection-page/collection-page-routes.ts +++ b/src/app/collection-page/collection-page-routes.ts @@ -7,6 +7,7 @@ import { browseByGuard } from '../browse-by/browse-by-guard'; import { browseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver'; import { authenticatedGuard } from '../core/auth/authenticated.guard'; 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 { 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'; @@ -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 { ThemedCollectionPageComponent } from './themed-collection-page.component'; - export const ROUTES: Route[] = [ { path: COLLECTION_CREATE_PATH, - component: CreateCollectionPageComponent, canActivate: [authenticatedGuard, createCollectionPageGuard], + children: [ + { + path: '', + component: CreateCollectionPageComponent, + resolve: { + breadcrumb: i18nBreadcrumbResolver, + }, + data: { + breadcrumbKey: 'collection.create', + }, + }, + ], + data: { + breadcrumbQueryParam: 'parent', + }, + resolve: { + breadcrumb: communityBreadcrumbResolver, + }, + runGuardsAndResolvers: 'always', }, { path: ':id', diff --git a/src/app/community-page/community-page-routes.ts b/src/app/community-page/community-page-routes.ts index 995af8274f..656b96c311 100644 --- a/src/app/community-page/community-page-routes.ts +++ b/src/app/community-page/community-page-routes.ts @@ -28,8 +28,26 @@ import { ThemedCommunityPageComponent } from './themed-community-page.component' export const ROUTES: Route[] = [ { path: COMMUNITY_CREATE_PATH, - component: CreateCommunityPageComponent, + children: [ + { + path: '', + component: CreateCommunityPageComponent, + resolve: { + breadcrumb: i18nBreadcrumbResolver, + }, + data: { + breadcrumbKey: 'community.create', + }, + }, + ], canActivate: [authenticatedGuard, createCommunityPageGuard], + data: { + breadcrumbQueryParam: 'parent', + }, + resolve: { + breadcrumb: communityBreadcrumbResolver, + }, + runGuardsAndResolvers: 'always', }, { path: ':id', diff --git a/src/app/core/breadcrumbs/community-breadcrumb.resolver.ts b/src/app/core/breadcrumbs/community-breadcrumb.resolver.ts index 1064a1cc19..0c37b5ca4f 100644 --- a/src/app/core/breadcrumbs/community-breadcrumb.resolver.ts +++ b/src/app/core/breadcrumbs/community-breadcrumb.resolver.ts @@ -8,11 +8,15 @@ import { Observable } from 'rxjs'; import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model'; 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 { CommunityDataService } from '../data/community-data.service'; import { Community } from '../shared/community.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'; /** @@ -25,11 +29,22 @@ export const communityBreadcrumbResolver: ResolveFn> dataService: CommunityDataService = inject(CommunityDataService), ): Observable> => { const linksToFollow: FollowLinkConfig[] = COMMUNITY_PAGE_LINKS_TO_FOLLOW as FollowLinkConfig[]; - return DSOBreadcrumbResolver( - route, - state, - breadcrumbService, - dataService, - ...linksToFollow, - ) as Observable>; + if (hasValue(route.data.breadcrumbQueryParam) && hasValue(route.queryParams[route.data.breadcrumbQueryParam])) { + return DSOBreadcrumbResolverByUuid( + route, + state, + route.queryParams[route.data.breadcrumbQueryParam], + breadcrumbService, + dataService, + ...linksToFollow, + ) as Observable>; + } else { + return DSOBreadcrumbResolver( + route, + state, + breadcrumbService, + dataService, + ...linksToFollow, + ) as Observable>; + } }; diff --git a/src/app/core/breadcrumbs/dso-breadcrumb.resolver.spec.ts b/src/app/core/breadcrumbs/dso-breadcrumb.resolver.spec.ts index ae19128d4e..c40a14a323 100644 --- a/src/app/core/breadcrumbs/dso-breadcrumb.resolver.spec.ts +++ b/src/app/core/breadcrumbs/dso-breadcrumb.resolver.spec.ts @@ -18,7 +18,10 @@ describe('DSOBreadcrumbResolver', () => { uuid = '1234-65487-12354-1235'; breadcrumbUrl = `/collections/${uuid}`; currentUrl = `${breadcrumbUrl}/edit`; - testCollection = Object.assign(new Collection(), { uuid }); + testCollection = Object.assign(new Collection(), { + uuid: uuid, + type: 'collection', + }); dsoBreadcrumbService = {}; collectionService = { findById: () => createSuccessfulRemoteDataObject$(testCollection), diff --git a/src/app/core/breadcrumbs/dso-breadcrumb.resolver.ts b/src/app/core/breadcrumbs/dso-breadcrumb.resolver.ts index cb1f96b103..992627ddfa 100644 --- a/src/app/core/breadcrumbs/dso-breadcrumb.resolver.ts +++ b/src/app/core/breadcrumbs/dso-breadcrumb.resolver.ts @@ -5,6 +5,7 @@ import { import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { getDSORoute } from '../../app-routing-paths'; import { BreadcrumbConfig } from '../../breadcrumbs/breadcrumb/breadcrumb-config.model'; import { hasValue } from '../../shared/empty.util'; import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model'; @@ -32,15 +33,34 @@ export const DSOBreadcrumbResolver: (route: ActivatedRouteSnapshot, state: Route dataService: IdentifiableDataService, ...linksToFollow: FollowLinkConfig[] ): Observable> => { - 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, ...linksToFollow: FollowLinkConfig[]) => Observable> = ( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot, + uuid: string, + breadcrumbService: DSOBreadcrumbsService, + dataService: IdentifiableDataService, + ...linksToFollow: FollowLinkConfig[] +): Observable> => { return dataService.findById(uuid, true, false, ...linksToFollow).pipe( getFirstCompletedRemoteData(), getRemoteDataPayload(), map((object: DSpaceObject) => { if (hasValue(object)) { - const fullPath = state.url; - const url = (fullPath.substring(0, fullPath.indexOf(uuid))).concat(uuid); - return { provider: breadcrumbService, key: object, url: url }; + return { provider: breadcrumbService, key: object, url: getDSORoute(object) }; } else { return undefined; } diff --git a/src/app/core/submission/resolver/submission-links-to-follow.ts b/src/app/core/submission/resolver/submission-links-to-follow.ts new file mode 100644 index 0000000000..1ddda024c5 --- /dev/null +++ b/src/app/core/submission/resolver/submission-links-to-follow.ts @@ -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[] = [ + followLink('item'), + followLink('collection'), +]; diff --git a/src/app/core/submission/resolver/submission-object.resolver.ts b/src/app/core/submission/resolver/submission-object.resolver.ts index 4ddd9dea93..3ead988c9b 100644 --- a/src/app/core/submission/resolver/submission-object.resolver.ts +++ b/src/app/core/submission/resolver/submission-object.resolver.ts @@ -5,12 +5,12 @@ import { import { Observable } from 'rxjs'; import { switchMap } from 'rxjs/operators'; -import { followLink } from '../../../shared/utils/follow-link-config.model'; import { IdentifiableDataService } from '../../data/base/identifiable-data.service'; import { RemoteData } from '../../data/remote-data'; import { Item } from '../../shared/item.model'; import { getFirstCompletedRemoteData } from '../../shared/operators'; 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 @@ -28,7 +28,7 @@ export const SubmissionObjectResolver: (route: ActivatedRouteSnapshot, state: Ro return dataService.findById(route.params.id, true, false, - followLink('item'), + ...SUBMISSION_LINKS_TO_FOLLOW, ).pipe( getFirstCompletedRemoteData(), switchMap((wfiRD: RemoteData) => wfiRD.payload.item as Observable>), diff --git a/src/app/core/submission/resolver/submission-parent-breadcrumb.resolver.ts b/src/app/core/submission/resolver/submission-parent-breadcrumb.resolver.ts new file mode 100644 index 0000000000..92412be869 --- /dev/null +++ b/src/app/core/submission/resolver/submission-parent-breadcrumb.resolver.ts @@ -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> { + + protected constructor( + protected dataService: IdentifiableDataService, + 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<> 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> { + 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)), + ); + } +} diff --git a/src/app/core/submission/submission-parent-breadcrumb.service.ts b/src/app/core/submission/submission-parent-breadcrumb.service.ts new file mode 100644 index 0000000000..4241d00192 --- /dev/null +++ b/src/app/core/submission/submission-parent-breadcrumb.service.ts @@ -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 { + + 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 { + return combineLatest([ + (submissionObject.collection as Observable>).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))), + ); + } + +} diff --git a/src/app/home-page/home-page.component.html b/src/app/home-page/home-page.component.html index 822554dd31..ad48fb328c 100644 --- a/src/app/home-page/home-page.component.html +++ b/src/app/home-page/home-page.component.html @@ -1,29 +1,25 @@ -
- -
- - - - - - - - -
-
+ + + +
+
- -
- - -
+ + + + + + + + diff --git a/src/app/home-page/home-page.component.scss b/src/app/home-page/home-page.component.scss index 653de42b44..5905a5959b 100644 --- a/src/app/home-page/home-page.component.scss +++ b/src/app/home-page/home-page.component.scss @@ -1,5 +1,6 @@ -:host ::ng-deep { - .container-fluid .container { - padding: 0; +@include media-breakpoint-down(md) { + ds-themed-configuration-search-page + .container { + width: 100%; + max-width: none; } } diff --git a/src/app/home-page/home-page.component.ts b/src/app/home-page/home-page.component.ts index 7da0fbf8d7..c954e90402 100644 --- a/src/app/home-page/home-page.component.ts +++ b/src/app/home-page/home-page.component.ts @@ -2,6 +2,7 @@ import { AsyncPipe, NgClass, NgIf, + NgTemplateOutlet, } from '@angular/common'; import { Component, @@ -20,10 +21,8 @@ import { import { Site } from '../core/shared/site.model'; import { SuggestionsPopupComponent } from '../notifications/suggestions-popup/suggestions-popup.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 { 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 { HomeCoarComponent } from './home-coar/home-coar.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'], templateUrl: './home-page.component.html', 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 { site$: Observable; - isXsOrSm$: Observable; recentSubmissionspageSize: number; showDiscoverFilters: boolean; constructor( @Inject(APP_CONFIG) protected appConfig: AppConfig, protected route: ActivatedRoute, - protected sidebarService: SidebarService, - protected windowService: HostWindowService, ) { this.recentSubmissionspageSize = this.appConfig.homePage.recentSubmissions.pageSize; this.showDiscoverFilters = this.appConfig.homePage.showDiscoverFilters; } ngOnInit(): void { - this.isXsOrSm$ = this.windowService.isXsOrSm(); this.site$ = this.route.data.pipe( map((data) => data.site as Site), ); diff --git a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.html b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.html index 7b1b21ab27..c3d6ebc823 100644 --- a/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.html +++ b/src/app/item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/paginated-drag-and-drop-bitstream-list/paginated-drag-and-drop-bitstream-list.component.html @@ -3,7 +3,6 @@ [hidePagerWhenSinglePage]="true" [hidePaginationDetail]="true" [paginationOptions]="options" - [pageInfoState]="(objectsRD$ | async)?.payload" [collectionSize]="(objectsRD$ | async)?.payload?.totalElements">
diff --git a/src/app/item-page/full/field-components/file-section/full-file-section.component.html b/src/app/item-page/full/field-components/file-section/full-file-section.component.html index 545796391b..8c534e6630 100644 --- a/src/app/item-page/full/field-components/file-section/full-file-section.component.html +++ b/src/app/item-page/full/field-components/file-section/full-file-section.component.html @@ -6,7 +6,6 @@ [hideGear]="true" [hidePagerWhenSinglePage]="true" [paginationOptions]="originalOptions" - [pageInfoState]="originals" [collectionSize]="originals?.totalElements" [retainScrollPosition]="true"> @@ -49,7 +48,6 @@ [hideGear]="true" [hidePagerWhenSinglePage]="true" [paginationOptions]="licenseOptions" - [pageInfoState]="licenses" [collectionSize]="licenses?.totalElements" [retainScrollPosition]="true"> diff --git a/src/app/item-page/versions/item-versions.component.html b/src/app/item-page/versions/item-versions.component.html index bda86f2810..e9ea3bb0f9 100644 --- a/src/app/item-page/versions/item-versions.component.html +++ b/src/app/item-page/versions/item-versions.component.html @@ -10,7 +10,6 @@ [hideGear]="true" [hidePagerWhenSinglePage]="true" [paginationOptions]="options" - [pageInfoState]="versions" [collectionSize]="versions?.totalElements" [retainScrollPosition]="true">
diff --git a/src/app/lookup-by-id/objectnotfound/objectnotfound.component.spec.ts b/src/app/lookup-by-id/objectnotfound/objectnotfound.component.spec.ts index c19f9c427d..5e7194fbd5 100644 --- a/src/app/lookup-by-id/objectnotfound/objectnotfound.component.spec.ts +++ b/src/app/lookup-by-id/objectnotfound/objectnotfound.component.spec.ts @@ -7,6 +7,7 @@ import { import { ActivatedRoute } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; 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 { ObjectNotFoundComponent } from './objectnotfound.component'; @@ -21,6 +22,10 @@ describe('ObjectNotFoundComponent', () => { const activatedRouteStub = Object.assign(new ActivatedRouteStub(), { params: observableOf({ id: testUUID, idType: uuidType }), }); + const serverResponseServiceStub = jasmine.createSpyObj('ServerResponseService', { + setNotFound: jasmine.createSpy('setNotFound'), + }); + const activatedRouteStubHandle = Object.assign(new ActivatedRouteStub(), { params: observableOf({ id: handleId, idType: handlePrefix }), }); @@ -31,6 +36,7 @@ describe('ObjectNotFoundComponent', () => { TranslateModule.forRoot(), ObjectNotFoundComponent, ], providers: [ + { provide: ServerResponseService, useValue: serverResponseServiceStub } , { provide: ActivatedRoute, useValue: activatedRouteStub }, ], schemas: [NO_ERRORS_SCHEMA], @@ -52,6 +58,10 @@ describe('ObjectNotFoundComponent', () => { expect(comp.idType).toEqual(uuidType); expect(comp.missingItem).toEqual('uuid: ' + testUUID); }); + + it('should call serverResponseService.setNotFound', () => { + expect(serverResponseServiceStub.setNotFound).toHaveBeenCalled(); + }); }); describe( 'legacy handle request', () => { @@ -61,6 +71,7 @@ describe('ObjectNotFoundComponent', () => { TranslateModule.forRoot(), ObjectNotFoundComponent, ], providers: [ + { provide: ServerResponseService, useValue: serverResponseServiceStub }, { provide: ActivatedRoute, useValue: activatedRouteStubHandle }, ], schemas: [NO_ERRORS_SCHEMA], @@ -78,6 +89,10 @@ describe('ObjectNotFoundComponent', () => { expect(comp.idType).toEqual(handlePrefix); expect(comp.missingItem).toEqual('handle: ' + handlePrefix + '/' + handleId); }); + + it('should call serverResponseService.setNotFound', () => { + expect(serverResponseServiceStub.setNotFound).toHaveBeenCalled(); + }); }); }); diff --git a/src/app/lookup-by-id/objectnotfound/objectnotfound.component.ts b/src/app/lookup-by-id/objectnotfound/objectnotfound.component.ts index c277d9c656..e8ab615e9e 100644 --- a/src/app/lookup-by-id/objectnotfound/objectnotfound.component.ts +++ b/src/app/lookup-by-id/objectnotfound/objectnotfound.component.ts @@ -9,6 +9,7 @@ import { RouterLink, } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; +import { ServerResponseService } from 'src/app/core/services/server-response.service'; /** * This component representing the `PageNotFound` DSpace page. @@ -35,7 +36,7 @@ export class ObjectNotFoundComponent implements OnInit { * @param {AuthService} authservice * @param {ServerResponseService} responseService */ - constructor(private route: ActivatedRoute) { + constructor(private route: ActivatedRoute, private serverResponseService: ServerResponseService) { route.params.subscribe((params) => { this.idType = params.idType; this.id = params.id; @@ -48,6 +49,7 @@ export class ObjectNotFoundComponent implements OnInit { } else { this.missingItem = 'handle: ' + this.idType + '/' + this.id; } + this.serverResponseService.setNotFound(); } } diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html index 63b50620e7..c30bc389cc 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html @@ -7,12 +7,12 @@ [formControl]="input" ngbAutofocus (keyup.enter)="selectSingleResult()"> -
+
diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts b/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts index c5c3644b84..be65551114 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.ts @@ -172,6 +172,11 @@ export class DSOSelectorComponent implements OnInit, OnDestroy { */ public subs: Subscription[] = []; + /** + * Random seed of 4 characters to avoid duplicate ids + */ + randomSeed: string = Math.random().toString(36).substring(2, 6); + constructor( protected searchService: SearchService, protected notifcationsService: NotificationsService, diff --git a/src/app/shared/object-detail/object-detail.component.html b/src/app/shared/object-detail/object-detail.component.html index 09573b3607..c57d5344cd 100644 --- a/src/app/shared/object-detail/object-detail.component.html +++ b/src/app/shared/object-detail/object-detail.component.html @@ -1,6 +1,5 @@ @@ -16,9 +15,9 @@
- - - + + +
{{ dsoNameService.getName(collection) }}
{{ dsoNameService.getName(selectCollection.dso) }}
diff --git a/src/app/shared/object-select/collection-select/collection-select.component.ts b/src/app/shared/object-select/collection-select/collection-select.component.ts index 2c9266151c..267d8a2a60 100644 --- a/src/app/shared/object-select/collection-select/collection-select.component.ts +++ b/src/app/shared/object-select/collection-select/collection-select.component.ts @@ -4,20 +4,31 @@ import { NgFor, NgIf, } from '@angular/common'; -import { Component } from '@angular/core'; +import { + Component, + OnInit, +} from '@angular/core'; import { FormsModule } from '@angular/forms'; import { RouterLink } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; +import { + map, + Observable, +} from 'rxjs'; -import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; -import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { getCollectionPageRoute } from '../../../collection-page/collection-page-routing-paths'; +import { PaginatedList } from '../../../core/data/paginated-list.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 { ThemedLoadingComponent } from '../../loading/themed-loading.component'; import { PaginationComponent } from '../../pagination/pagination.component'; 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'; @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 */ -export class CollectionSelectComponent extends ObjectSelectComponent { +export class CollectionSelectComponent extends ObjectSelectComponent implements OnInit { - constructor( - protected objectSelectService: ObjectSelectService, - protected authorizationService: AuthorizationDataService, - public dsoNameService: DSONameService, - ) { - super(objectSelectService, authorizationService); - } + /** + * Collection of all the data that is used to display the {@link Collection} in the HTML. + * By collecting this data here it doesn't need to be recalculated on evey change detection. + */ + selectCollections$: Observable[]>; ngOnInit(): void { super.ngOnInit(); if (!isNotEmpty(this.confirmButton)) { this.confirmButton = 'collection.select.confirm'; } + this.selectCollections$ = this.dsoRD$.pipe( + hasValueOperator(), + getAllSucceededRemoteDataPayload(), + map((collections: PaginatedList) => collections.page.map((collection: Collection) => Object.assign(new DSpaceObjectSelect(), { + dso: collection, + canSelect$: this.canSelect(collection), + selected$: this.getSelected(collection.id), + route: getCollectionPageRoute(collection.id), + } as DSpaceObjectSelect))), + ); } } diff --git a/src/app/shared/object-select/item-select/item-select.component.html b/src/app/shared/object-select/item-select/item-select.component.html index edd7f3f58c..e0e71fb939 100644 --- a/src/app/shared/object-select/item-select/item-select.component.html +++ b/src/app/shared/object-select/item-select/item-select.component.html @@ -3,7 +3,6 @@ *ngIf="itemsRD?.payload?.totalElements > 0" [paginationOptions]="paginationOptions" [sortOptions]="sortOptions" - [pageInfoState]="itemsRD?.payload" [collectionSize]="itemsRD?.payload?.totalElements" [hidePagerWhenSinglePage]="true" [hideGear]="true"> @@ -18,17 +17,17 @@ - - + + - + {{ dsoNameService.getName(collection) }} - {{item.firstMetadataValue(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])}} - {{ dsoNameService.getName(item) }} + {{selectItem.dso.firstMetadataValue(['dc.contributor.author', 'dc.creator', 'dc.contributor.*'])}} + {{ dsoNameService.getName(selectItem.dso) }} diff --git a/src/app/shared/object-select/item-select/item-select.component.spec.ts b/src/app/shared/object-select/item-select/item-select.component.spec.ts index 15247affa9..86d4b5c8d4 100644 --- a/src/app/shared/object-select/item-select/item-select.component.spec.ts +++ b/src/app/shared/object-select/item-select/item-select.component.spec.ts @@ -187,15 +187,16 @@ describe('ItemSelectComponent', () => { beforeEach(() => { comp.featureId = FeatureID.CanManageMappings; 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.whenStable().then(() => { - const checkbox = fixture.debugElement.query(By.css('input.item-checkbox')).nativeElement; - expect(authorizationDataService.isAuthorized).toHaveBeenCalled(); - expect(checkbox.disabled).toBeTrue(); - }); + await fixture.whenStable(); + + const checkbox = fixture.debugElement.query(By.css('input.item-checkbox')).nativeElement; + expect(authorizationDataService.isAuthorized).toHaveBeenCalled(); + expect(checkbox.disabled).toBeTrue(); })); }); }); diff --git a/src/app/shared/object-select/item-select/item-select.component.ts b/src/app/shared/object-select/item-select/item-select.component.ts index 20adf30871..973b4aff87 100644 --- a/src/app/shared/object-select/item-select/item-select.component.ts +++ b/src/app/shared/object-select/item-select/item-select.component.ts @@ -7,6 +7,7 @@ import { import { Component, Input, + OnInit, } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { RouterLink } from '@angular/router'; @@ -14,8 +15,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; -import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; +import { PaginatedList } from '../../../core/data/paginated-list.model'; import { Item } from '../../../core/shared/item.model'; import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators'; 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 { PaginationComponent } from '../../pagination/pagination.component'; 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'; @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 */ -export class ItemSelectComponent extends ObjectSelectComponent { +export class ItemSelectComponent extends ObjectSelectComponent implements OnInit { /** * Whether or not to hide the collection column @@ -49,35 +49,25 @@ export class ItemSelectComponent extends ObjectSelectComponent { hideCollection = false; /** - * The routes to the items their pages - * Key: Item ID - * Value: Route to item page + * Collection of all the data that is used to display the {@link Item} in the HTML. + * By collecting this data here it doesn't need to be recalculated on evey change detection. */ - itemPageRoutes$: Observable<{ - [itemId: string]: string - }>; - - constructor( - protected objectSelectService: ObjectSelectService, - protected authorizationService: AuthorizationDataService, - public dsoNameService: DSONameService, - ) { - super(objectSelectService, authorizationService); - } + selectItems$: Observable[]>; ngOnInit(): void { super.ngOnInit(); if (!isNotEmpty(this.confirmButton)) { this.confirmButton = 'item.select.confirm'; } - this.itemPageRoutes$ = this.dsoRD$.pipe( + this.selectItems$ = this.dsoRD$.pipe( hasValueOperator(), getAllSucceededRemoteDataPayload(), - map((items) => { - const itemPageRoutes = {}; - items.page.forEach((item) => itemPageRoutes[item.uuid] = getItemPageRoute(item)); - return itemPageRoutes; - }), + map((items: PaginatedList) => items.page.map((item: Item) => Object.assign(new DSpaceObjectSelect(), { + dso: item, + canSelect$: this.canSelect(item), + selected$: this.getSelected(item.id), + route: getItemPageRoute(item), + } as DSpaceObjectSelect))), ); } diff --git a/src/app/shared/object-select/object-select.model.ts b/src/app/shared/object-select/object-select.model.ts new file mode 100644 index 0000000000..e3b66dac31 --- /dev/null +++ b/src/app/shared/object-select/object-select.model.ts @@ -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 { + + /** + * The {@link DSpaceObject} to display + */ + dso: T; + + /** + * Whether the {@link DSpaceObject} can be selected + */ + canSelect$: Observable; + + /** + * Whether the {@link DSpaceObject} is selected + */ + selected$: Observable; + + /** + * The {@link DSpaceObject}'s route + */ + route: string; + +} diff --git a/src/app/shared/object-select/object-select/object-select.component.ts b/src/app/shared/object-select/object-select/object-select.component.ts index c4940cc4a7..323a7cc396 100644 --- a/src/app/shared/object-select/object-select/object-select.component.ts +++ b/src/app/shared/object-select/object-select/object-select.component.ts @@ -15,6 +15,7 @@ import { take, } from 'rxjs/operators'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; import { SortOptions } from '../../../core/cache/models/sort-options.model'; import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../../core/data/feature-authorization/feature-id'; @@ -31,7 +32,7 @@ import { ObjectSelectService } from '../object-select.service'; selector: 'ds-object-select-abstract', template: '', }) -export abstract class ObjectSelectComponent implements OnInit, OnDestroy { +export abstract class ObjectSelectComponent implements OnInit, OnDestroy { /** * A unique key used for the object select service @@ -102,8 +103,11 @@ export abstract class ObjectSelectComponent implements OnInit, OnDestro */ selectedIds$: Observable; - constructor(protected objectSelectService: ObjectSelectService, - protected authorizationService: AuthorizationDataService) { + constructor( + protected objectSelectService: ObjectSelectService, + protected authorizationService: AuthorizationDataService, + public dsoNameService: DSONameService, + ) { } ngOnInit(): void { diff --git a/src/app/shared/object-table/object-table.component.html b/src/app/shared/object-table/object-table.component.html index 3047fc2a36..2bb15aaf1c 100644 --- a/src/app/shared/object-table/object-table.component.html +++ b/src/app/shared/object-table/object-table.component.html @@ -1,6 +1,5 @@ = undefined; - /** * Configuration for the NgbPagination component. */ @@ -167,18 +160,13 @@ export class PaginationComponent implements OnDestroy, OnInit { /** * Current page. */ - public currentPage$; + public currentPage$: Observable; /** * Current page in the state of a Remote paginated objects. */ public currentPageState: number = undefined; - /** - * An observable of HostWindowState type - */ - public hostWindow: Observable; - /** * 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. @@ -268,7 +256,7 @@ export class PaginationComponent implements OnDestroy, OnInit { map((currentPagination) => currentPagination.pageSize), ); - let sortOptions; + let sortOptions: SortOptions; if (this.sortOptions) { sortOptions = this.sortOptions; } 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, private paginationService: PaginationService, public hostWindowService: HostWindowService) { @@ -330,17 +308,6 @@ export class PaginationComponent implements OnDestroy, OnInit { 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 */ @@ -364,10 +331,10 @@ export class PaginationComponent implements OnDestroy, OnInit { if (collectionSize) { showingDetails = this.paginationService.getCurrentPagination(this.id, this.paginationOptions).pipe( map((currentPaginationOptions) => { - let lastItem; + let lastItem: number; 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) { lastItem = pageMax; } else { diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index 562d7e8ea7..d12780981b 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -33,6 +33,7 @@ | translate}}
+ { + return createSelector( + submissionSelector, + (state: SubmissionState) => state.objects[submissionId], + ); +} + +function getSubmissionCollectionIdSelector(submissionId: string): MemoizedSelector { + return createSelector( + getSubmissionSelector(submissionId), + (submission: SubmissionObjectEntry) => submission?.collection, + ); +} + /** * A service that provides methods used in submission process. */ @@ -120,10 +139,19 @@ export class SubmissionService { * @param collectionId * The collection id */ - changeSubmissionCollection(submissionId, collectionId) { + changeSubmissionCollection(submissionId: string, collectionId: string): void { 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 { + return this.store.pipe(select(getSubmissionCollectionIdSelector(submissionId))); + } + /** * Perform a REST call to create a new workspaceitem and return response * diff --git a/src/app/workflowitems-edit-page/item-from-workflow-breadcrumb.resolver.ts b/src/app/workflowitems-edit-page/item-from-workflow-breadcrumb.resolver.ts new file mode 100644 index 0000000000..1c29d6b861 --- /dev/null +++ b/src/app/workflowitems-edit-page/item-from-workflow-breadcrumb.resolver.ts @@ -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> { + + constructor( + protected dataService: WorkflowItemDataService, + protected breadcrumbService: SubmissionParentBreadcrumbsService, + ) { + super(dataService, breadcrumbService); + } + +} diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page-routes.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page-routes.ts index 95e1c69de8..4bc074c425 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page-routes.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page-routes.ts @@ -6,6 +6,7 @@ import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item- 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 { 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 { workflowItemPageResolver } from './workflow-item-page.resolver'; import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/themed-workflow-item-send-back.component'; @@ -20,7 +21,10 @@ import { export const ROUTES: Routes = [ { path: ':id', - resolve: { wfi: workflowItemPageResolver }, + resolve: { + breadcrumb: ItemFromWorkflowBreadcrumbResolver, + wfi: workflowItemPageResolver, + }, children: [ { canActivate: [authenticatedGuard], diff --git a/src/app/workspaceitems-edit-page/item-from-workspace-breadcrumb.resolver.ts b/src/app/workspaceitems-edit-page/item-from-workspace-breadcrumb.resolver.ts new file mode 100644 index 0000000000..912d578b45 --- /dev/null +++ b/src/app/workspaceitems-edit-page/item-from-workspace-breadcrumb.resolver.ts @@ -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> { + + constructor( + protected dataService: WorkspaceitemDataService, + protected breadcrumbService: SubmissionParentBreadcrumbsService, + ) { + super(dataService, breadcrumbService); + } + +} diff --git a/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routes.ts b/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routes.ts index 7e5d92ff01..8f07c24edc 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routes.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routes.ts @@ -5,6 +5,7 @@ import { i18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item-page.component'; import { ThemedSubmissionEditComponent } from '../submission/edit/themed-submission-edit.component'; import { itemFromWorkspaceResolver } from './item-from-workspace.resolver'; +import { ItemFromWorkspaceBreadcrumbResolver } from './item-from-workspace-breadcrumb.resolver'; import { workspaceItemPageResolver } from './workspace-item-page.resolver'; import { ThemedWorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/themed-workspaceitems-delete-page.component'; @@ -15,7 +16,10 @@ export const ROUTES: Route[] = [ }, { path: ':id', - resolve: { wsi: workspaceItemPageResolver }, + resolve: { + breadcrumb: ItemFromWorkspaceBreadcrumbResolver, + wsi: workspaceItemPageResolver, + }, children: [ { canActivate: [authenticatedGuard], diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index d4731f9bb9..9cb33aca9a 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1108,6 +1108,8 @@ "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.create.head": "Create a Collection", @@ -1398,6 +1400,8 @@ "community.subcoms-cols.breadcrumbs": "Subcommunities and Collections", + "community.create.breadcrumbs": "Create Community", + "community.create.head": "Create a Community", "community.create.notifications.success": "Successfully created the Community",