diff --git a/config/config.example.yml b/config/config.example.yml index 4b9c1a27ace..0933619e013 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -285,8 +285,17 @@ item: # settings menu. See pageSizeOptions in 'pagination-component-options.model.ts'. pageSize: 5 +# Community Page Config +community: + # Search tab config + searchSection: + showSidebar: true + # Collection Page Config collection: + # Search tab config + searchSection: + showSidebar: true edit: undoTimeout: 10000 # 10 seconds @@ -391,4 +400,3 @@ comcolSelectionSort: # suggestion: # - collectionId: 8f7df5ca-f9c2-47a4-81ec-8a6393d6e5af # source: "openaire" - diff --git a/src/app/collection-page/collection-page-routing.module.ts b/src/app/collection-page/collection-page-routing.module.ts index 5ddef6ca68b..ed0c77c2f8a 100644 --- a/src/app/collection-page/collection-page-routing.module.ts +++ b/src/app/collection-page/collection-page-routing.module.ts @@ -25,7 +25,7 @@ import { DSOEditMenuResolver } from '../shared/dso-page/dso-edit-menu.resolver'; import { ComcolBrowseByComponent } from '../shared/comcol/sections/comcol-browse-by/comcol-browse-by.component'; import { BrowseByGuard } from '../browse-by/browse-by-guard'; import { BrowseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver'; -import { CollectionRecentlyAddedComponent } from './sections/recently-added/collection-recently-added.component'; +import { ComcolSearchSectionComponent } from '../shared/comcol/sections/comcol-search-section/comcol-search-section.component'; @NgModule({ imports: [ @@ -73,7 +73,7 @@ import { CollectionRecentlyAddedComponent } from './sections/recently-added/coll { path: '', pathMatch: 'full', - component: CollectionRecentlyAddedComponent, + component: ComcolSearchSectionComponent, }, { path: 'browse/:id', diff --git a/src/app/collection-page/collection-page.module.ts b/src/app/collection-page/collection-page.module.ts index 8782be0a45f..5db85d48e98 100644 --- a/src/app/collection-page/collection-page.module.ts +++ b/src/app/collection-page/collection-page.module.ts @@ -19,7 +19,6 @@ import { ComcolModule } from '../shared/comcol/comcol.module'; import { DsoSharedModule } from '../dso-shared/dso-shared.module'; import { DsoPageModule } from '../shared/dso-page/dso-page.module'; import { BrowseByPageModule } from '../browse-by/browse-by-page.module'; -import { CollectionRecentlyAddedComponent } from './sections/recently-added/collection-recently-added.component'; const DECLARATIONS = [ CollectionPageComponent, @@ -29,7 +28,6 @@ const DECLARATIONS = [ EditItemTemplatePageComponent, ThemedEditItemTemplatePageComponent, CollectionItemMapperComponent, - CollectionRecentlyAddedComponent, ]; @NgModule({ diff --git a/src/app/collection-page/sections/recently-added/collection-recently-added.component.html b/src/app/collection-page/sections/recently-added/collection-recently-added.component.html deleted file mode 100644 index 002b8cceda7..00000000000 --- a/src/app/collection-page/sections/recently-added/collection-recently-added.component.html +++ /dev/null @@ -1,18 +0,0 @@ - -
-

{{'collection.page.browse.recent.head' | translate}}

- - -
- - - -
diff --git a/src/app/collection-page/sections/recently-added/collection-recently-added.component.spec.ts b/src/app/collection-page/sections/recently-added/collection-recently-added.component.spec.ts deleted file mode 100644 index 4acc24e3f59..00000000000 --- a/src/app/collection-page/sections/recently-added/collection-recently-added.component.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { CollectionRecentlyAddedComponent } from './collection-recently-added.component'; -import { APP_CONFIG } from '../../../../config/app-config.interface'; -import { environment } from '../../../../environments/environment.test'; -import { ActivatedRoute } from '@angular/router'; -import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub'; -import { PaginationService } from '../../../core/pagination/pagination.service'; -import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; -import { SearchServiceStub } from '../../../shared/testing/search-service.stub'; -import { SearchService } from '../../../core/shared/search/search.service'; -import { VarDirective } from '../../../shared/utils/var.directive'; -import { TranslateModule } from '@ngx-translate/core'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; - -describe('CollectionRecentlyAddedComponent', () => { - let component: CollectionRecentlyAddedComponent; - let fixture: ComponentFixture; - - let activatedRoute: ActivatedRouteStub; - let paginationService: PaginationServiceStub; - let searchService: SearchServiceStub; - - beforeEach(async () => { - activatedRoute = new ActivatedRouteStub(); - paginationService = new PaginationServiceStub(); - searchService = new SearchServiceStub(); - - await TestBed.configureTestingModule({ - declarations: [ - CollectionRecentlyAddedComponent, - VarDirective, - ], - imports: [ - TranslateModule.forRoot(), - ], - providers: [ - { provide: ActivatedRoute, useValue: activatedRoute }, - { provide: APP_CONFIG, useValue: environment }, - { provide: PaginationService, useValue: paginationService }, - { provide: SearchService, useValue: SearchServiceStub }, - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA], - }).compileComponents(); - - fixture = TestBed.createComponent(CollectionRecentlyAddedComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/collection-page/sections/recently-added/collection-recently-added.component.ts b/src/app/collection-page/sections/recently-added/collection-recently-added.component.ts deleted file mode 100644 index 65af77a63b5..00000000000 --- a/src/app/collection-page/sections/recently-added/collection-recently-added.component.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Component, OnInit, Inject, OnDestroy } from '@angular/core'; -import { Observable, combineLatest as observableCombineLatest } from 'rxjs'; -import { RemoteData } from '../../../core/data/remote-data'; -import { PaginatedList } from '../../../core/data/paginated-list.model'; -import { Item } from '../../../core/shared/item.model'; -import { switchMap, map, startWith, take } from 'rxjs/operators'; -import { getFirstSucceededRemoteData, toDSpaceObjectListRD } from '../../../core/shared/operators'; -import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; -import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model'; -import { BROWSE_LINKS_TO_FOLLOW } from '../../../core/browse/browse.service'; -import { PaginationService } from '../../../core/pagination/pagination.service'; -import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; -import { SortOptions, SortDirection } from '../../../core/cache/models/sort-options.model'; -import { APP_CONFIG, AppConfig } from '../../../../config/app-config.interface'; -import { SearchService } from '../../../core/shared/search/search.service'; -import { Collection } from '../../../core/shared/collection.model'; -import { ActivatedRoute, Data } from '@angular/router'; -import { fadeIn } from '../../../shared/animations/fade'; - -@Component({ - selector: 'ds-collection-recently-added', - templateUrl: './collection-recently-added.component.html', - styleUrls: ['./collection-recently-added.component.scss'], - animations: [fadeIn], -}) -export class CollectionRecentlyAddedComponent implements OnInit, OnDestroy { - - paginationConfig: PaginationComponentOptions; - - sortConfig: SortOptions; - - collectionRD$: Observable>; - - itemRD$: Observable>>; - - constructor( - @Inject(APP_CONFIG) protected appConfig: AppConfig, - protected paginationService: PaginationService, - protected route: ActivatedRoute, - protected searchService: SearchService, - ) { - this.paginationConfig = Object.assign(new PaginationComponentOptions(), { - id: 'cp', - currentPage: 1, - pageSize: this.appConfig.browseBy.pageSize, - }); - - this.sortConfig = new SortOptions('dc.date.accessioned', SortDirection.DESC); - } - - ngOnInit(): void { - this.collectionRD$ = this.route.data.pipe( - map((data: Data) => data.dso as RemoteData), - take(1), - ); - - this.itemRD$ = observableCombineLatest([ - this.paginationService.getCurrentPagination(this.paginationConfig.id, this.paginationConfig), - this.paginationService.getCurrentSort(this.paginationConfig.id, this.sortConfig), - ]).pipe( - switchMap(([currentPagination, currentSort]: [PaginationComponentOptions, SortOptions]) => this.collectionRD$.pipe( - getFirstSucceededRemoteData(), - map((rd: RemoteData) => rd.payload.id), - switchMap((id: string) => this.searchService.search( - new PaginatedSearchOptions({ - scope: id, - pagination: currentPagination, - sort: currentSort, - dsoTypes: [DSpaceObjectType.ITEM] - }), null, true, true, ...BROWSE_LINKS_TO_FOLLOW).pipe( - toDSpaceObjectListRD() - ) as Observable>>), - startWith(undefined), // Make sure switching pages shows loading component - )), - ); - } - - ngOnDestroy(): void { - this.paginationService.clearPagination(this.paginationConfig.id); - } - -} diff --git a/src/app/community-page/community-page-routing.module.ts b/src/app/community-page/community-page-routing.module.ts index 5ca544bb541..f38e670546b 100644 --- a/src/app/community-page/community-page-routing.module.ts +++ b/src/app/community-page/community-page-routing.module.ts @@ -19,6 +19,8 @@ import { SubComColSectionComponent } from './sections/sub-com-col-section/sub-co import { BrowseByI18nBreadcrumbResolver } from '../browse-by/browse-by-i18n-breadcrumb.resolver'; import { BrowseByGuard } from '../browse-by/browse-by-guard'; 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 { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; @NgModule({ imports: [ @@ -56,7 +58,16 @@ import { ComcolBrowseByComponent } from '../shared/comcol/sections/comcol-browse { path: '', pathMatch: 'full', + component: ComcolSearchSectionComponent, + }, + { + path: 'subcoms-cols', + pathMatch: 'full', component: SubComColSectionComponent, + resolve: { + breadcrumb: I18nBreadcrumbResolver, + }, + data: { breadcrumbKey: 'community.subcoms-cols' }, }, { path: 'browse/:id', diff --git a/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.spec.ts b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.spec.ts index 804299d3d98..cb3c41aa97d 100644 --- a/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.spec.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.spec.ts @@ -11,6 +11,7 @@ describe('SubComColSectionComponent', () => { beforeEach(async () => { activatedRoute = new ActivatedRouteStub(); + activatedRoute.parent = new ActivatedRouteStub(); await TestBed.configureTestingModule({ declarations: [ diff --git a/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.ts b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.ts index ff30e51607b..a72674adec3 100644 --- a/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.ts +++ b/src/app/community-page/sections/sub-com-col-section/sub-com-col-section.component.ts @@ -20,7 +20,7 @@ export class SubComColSectionComponent implements OnInit { } ngOnInit(): void { - this.community$ = this.route.data.pipe( + this.community$ = this.route.parent.data.pipe( map((data: Data) => (data.dso as RemoteData).payload), ); } diff --git a/src/app/search-page/configuration-search-page.component.ts b/src/app/search-page/configuration-search-page.component.ts index 768149de6a8..9196dda0251 100644 --- a/src/app/search-page/configuration-search-page.component.ts +++ b/src/app/search-page/configuration-search-page.component.ts @@ -8,6 +8,7 @@ import { SearchConfigurationService } from '../core/shared/search/search-configu import { RouteService } from '../core/services/route.service'; import { SearchService } from '../core/shared/search/search.service'; import { Router } from '@angular/router'; +import { APP_CONFIG, AppConfig } from '../../config/app-config.interface'; /** * This component renders a search page using a configuration as input. @@ -32,7 +33,9 @@ export class ConfigurationSearchPageComponent extends SearchComponent { protected windowService: HostWindowService, @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, protected routeService: RouteService, - protected router: Router) { - super(service, sidebarService, windowService, searchConfigService, routeService, router); + protected router: Router, + @Inject(APP_CONFIG) protected appConfig: AppConfig, + ) { + super(service, sidebarService, windowService, searchConfigService, routeService, router, appConfig); } } diff --git a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts index 2a3ec220d6a..c6e77d49e25 100644 --- a/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts +++ b/src/app/shared/comcol/comcol-page-browse-by/comcol-page-browse-by.component.ts @@ -55,16 +55,21 @@ export class ComcolPageBrowseByComponent implements OnDestroy, OnInit { if (this.contentType === 'collection') { comColRoute = getCollectionPageRoute(this.id); allOptions.push({ - id: 'recent_submissions', - label: 'collection.page.browse.recent.head', + id: 'search', + label: 'collection.page.browse.search.head', routerLink: comColRoute, }); } else if (this.contentType === 'community') { comColRoute = getCommunityPageRoute(this.id); + allOptions.push({ + id: 'search', + label: 'collection.page.browse.search.head', + routerLink: comColRoute, + }); allOptions.push({ id: 'comcols', label: 'community.all-lists.head', - routerLink: comColRoute, + routerLink: `${comColRoute}/subcoms-cols`, }); } diff --git a/src/app/shared/comcol/comcol.module.ts b/src/app/shared/comcol/comcol.module.ts index 7ce48a3a55d..21c6e368916 100644 --- a/src/app/shared/comcol/comcol.module.ts +++ b/src/app/shared/comcol/comcol.module.ts @@ -18,6 +18,8 @@ import { FormModule } from '../form/form.module'; import { UploadModule } from '../upload/upload.module'; import { ComcolBrowseByComponent } from './sections/comcol-browse-by/comcol-browse-by.component'; import { BrowseByModule } from '../../browse-by/browse-by.module'; +import { SearchModule } from '../search/search.module'; +import { ComcolSearchSectionComponent } from './sections/comcol-search-section/comcol-search-section.component'; const COMPONENTS = [ ComcolPageContentComponent, @@ -33,6 +35,7 @@ const COMPONENTS = [ ComcolRoleComponent, ThemedComcolPageHandleComponent, ComcolBrowseByComponent, + ComcolSearchSectionComponent, ]; @NgModule({ @@ -45,6 +48,7 @@ const COMPONENTS = [ SharedModule, UploadModule, BrowseByModule, + SearchModule, ], exports: [ ...COMPONENTS, diff --git a/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.html b/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.html new file mode 100644 index 00000000000..7c97dabf43a --- /dev/null +++ b/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.html @@ -0,0 +1,7 @@ + + diff --git a/src/app/collection-page/sections/recently-added/collection-recently-added.component.scss b/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.scss similarity index 100% rename from src/app/collection-page/sections/recently-added/collection-recently-added.component.scss rename to src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.scss diff --git a/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.spec.ts b/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.spec.ts new file mode 100644 index 00000000000..6b1f9236b1f --- /dev/null +++ b/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.spec.ts @@ -0,0 +1,35 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComcolSearchSectionComponent } from './comcol-search-section.component'; +import { ActivatedRoute } from '@angular/router'; +import { ActivatedRouteStub } from '../../../testing/active-router.stub'; +import { APP_CONFIG } from '../../../../../config/app-config.interface'; +import { environment } from '../../../../../environments/environment.test'; + +describe('ComcolSearchSectionComponent', () => { + let component: ComcolSearchSectionComponent; + let fixture: ComponentFixture; + + let route: ActivatedRouteStub; + + beforeEach(async () => { + route = new ActivatedRouteStub(); + + await TestBed.configureTestingModule({ + declarations: [ + ComcolSearchSectionComponent, + ], + providers: [ + { provide: APP_CONFIG, useValue: environment }, + { provide: ActivatedRoute, useValue: route }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ComcolSearchSectionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.ts b/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.ts new file mode 100644 index 00000000000..fe50147395a --- /dev/null +++ b/src/app/shared/comcol/sections/comcol-search-section/comcol-search-section.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { Observable } from 'rxjs'; +import { ActivatedRoute, Data } from '@angular/router'; +import { map } from 'rxjs/operators'; +import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { Community } from '../../../../core/shared/community.model'; +import { Collection } from '../../../../core/shared/collection.model'; +import { APP_CONFIG, AppConfig } from '../../../../../config/app-config.interface'; +import { hasValue } from '../../../empty.util'; + +/** + * The search tab on community & collection pages + */ +@Component({ + selector: 'ds-comcol-search-section', + templateUrl: './comcol-search-section.component.html', + styleUrls: ['./comcol-search-section.component.scss'], + providers: [ + { + provide: SEARCH_CONFIG_SERVICE, + useClass: SearchConfigurationService, + }, + ], +}) +export class ComcolSearchSectionComponent implements OnInit { + + comcol$: Observable; + + showSidebar$: Observable; + + constructor( + @Inject(APP_CONFIG) public appConfig: AppConfig, + protected route: ActivatedRoute, + ) { + } + + ngOnInit(): void { + this.comcol$ = this.route.data.pipe( + map((data: Data) => (data.dso as RemoteData).payload), + ); + this.showSidebar$ = this.comcol$.pipe( + map((comcol: Community | Collection) => hasValue(comcol) && this.appConfig[comcol.type as any].searchSection.showSidebar), + ); + } + +} diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts index 95a063bdd67..e6718ede58f 100644 --- a/src/app/shared/search-form/search-form.component.ts +++ b/src/app/shared/search-form/search-form.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output, OnChanges } from '@angular/core'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { Router } from '@angular/router'; -import { isNotEmpty } from '../empty.util'; +import { isNotEmpty, hasValue } from '../empty.util'; import { SearchService } from '../../core/shared/search/search.service'; import { currentPath } from '../utils/route.utils'; import { PaginationService } from '../../core/pagination/pagination.service'; @@ -39,6 +39,11 @@ export class SearchFormComponent implements OnChanges { @Input() scope = ''; + /** + * Hides the scope in the url, this can be useful when you hardcode the scope in another way + */ + @Input() hideScopeInUrl = false; + selectedScope: BehaviorSubject = new BehaviorSubject(undefined); @Input() currentUrl: string; @@ -122,6 +127,9 @@ export class SearchFormComponent implements OnChanges { }, data ); + if (hasValue(data.scope) && this.hideScopeInUrl) { + delete queryParams.scope; + } void this.router.navigate(this.getSearchLinkParts(), { queryParams: queryParams, diff --git a/src/app/shared/search-form/themed-search-form.component.ts b/src/app/shared/search-form/themed-search-form.component.ts index 50b3751b06d..83c396b2f9a 100644 --- a/src/app/shared/search-form/themed-search-form.component.ts +++ b/src/app/shared/search-form/themed-search-form.component.ts @@ -18,6 +18,8 @@ export class ThemedSearchFormComponent extends ThemedComponent = new EventEmitter(); protected inAndOutputNames: (keyof SearchFormComponent & keyof this)[] = [ - 'query', 'inPlaceSearch', 'scope', 'currentUrl', 'large', 'brandColor', 'searchPlaceholder', 'showScopeSelector', + 'query', + 'inPlaceSearch', + 'scope', + 'hideScopeInUrl', + 'currentUrl', + 'large', + 'brandColor', + 'searchPlaceholder', + 'showScopeSelector', 'submitSearch', ]; diff --git a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts index 4469a124ce6..a6212ca9c5e 100644 --- a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts @@ -27,6 +27,8 @@ import { SearchConfigurationServiceStub } from '../../../../testing/search-confi import { VocabularyEntryDetail } from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { FacetValue} from '../../../models/facet-value.model'; import { SearchFilterConfig } from '../../../models/search-filter-config.model'; +import { APP_CONFIG } from '../../../../../../config/app-config.interface'; +import { environment } from '../../../../../../environments/environment.test'; describe('SearchHierarchyFilterComponent', () => { @@ -34,7 +36,7 @@ describe('SearchHierarchyFilterComponent', () => { let showVocabularyTreeLink: DebugElement; const testSearchLink = 'test-search'; - const testSearchFilter = 'test-search-filter'; + const testSearchFilter = 'subject'; const VocabularyTreeViewComponent = { select: new EventEmitter(), }; @@ -73,6 +75,7 @@ describe('SearchHierarchyFilterComponent', () => { { provide: Router, useValue: router }, { provide: NgbModal, useValue: ngbModal }, { provide: VocabularyService, useValue: vocabularyService }, + { provide: APP_CONFIG, useValue: environment }, { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, { provide: IN_PLACE_SEARCH, useValue: false }, { provide: FILTER_CONFIG, useValue: Object.assign(new SearchFilterConfig(), { name: testSearchFilter }) }, @@ -86,7 +89,7 @@ describe('SearchHierarchyFilterComponent', () => { function init() { fixture = TestBed.createComponent(SearchHierarchyFilterComponent); fixture.detectChanges(); - showVocabularyTreeLink = fixture.debugElement.query(By.css('a#show-test-search-filter-tree')); + showVocabularyTreeLink = fixture.debugElement.query(By.css(`a#show-${testSearchFilter}-tree`)); } describe('if the vocabulary doesn\'t exist', () => { diff --git a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts index d53fa37cf4b..18ddbbdf97e 100644 --- a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts @@ -24,9 +24,11 @@ import { filter, map, take } from 'rxjs/operators'; import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; import { Observable, BehaviorSubject } from 'rxjs'; import { PageInfo } from '../../../../../core/shared/page-info.model'; -import { environment } from '../../../../../../environments/environment'; import { addOperatorToFilterValue } from '../../../search.utils'; import { VocabularyTreeviewModalComponent } from '../../../../form/vocabulary-treeview-modal/vocabulary-treeview-modal.component'; +import { hasValue } from '../../../../empty.util'; +import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface'; +import { FilterVocabularyConfig } from '../../../../../../config/filter-vocabulary-config'; @Component({ selector: 'ds-search-hierarchy-filter', @@ -47,6 +49,7 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i protected router: Router, protected modalService: NgbModal, protected vocabularyService: VocabularyService, + @Inject(APP_CONFIG) protected appConfig: AppConfig, @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean, @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, @@ -67,17 +70,20 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i super.onSubmit(addOperatorToFilterValue(data, 'query')); } - ngOnInit() { + ngOnInit(): void { super.ngOnInit(); - this.vocabularyExists$ = this.vocabularyService.searchTopEntries( - this.getVocabularyEntry(), new PageInfo(), true, false, - ).pipe( - filter(rd => rd.hasCompleted), - take(1), - map(rd => { - return rd.hasSucceeded; - }), - ); + const vocabularyName: string = this.getVocabularyEntry(); + if (hasValue(vocabularyName)) { + this.vocabularyExists$ = this.vocabularyService.searchTopEntries( + vocabularyName, new PageInfo(), true, false, + ).pipe( + filter(rd => rd.hasCompleted), + take(1), + map(rd => { + return rd.hasSucceeded; + }), + ); + } } /** @@ -93,11 +99,11 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i name: this.getVocabularyEntry(), closed: true }; - modalRef.result.then((detail: VocabularyEntryDetail) => { - this.selectedValues$ + void modalRef.result.then((detail: VocabularyEntryDetail) => { + this.subs.push(this.selectedValues$ .pipe(take(1)) .subscribe((selectedValues) => { - this.router.navigate( + void this.router.navigate( [this.searchService.getSearchLink()], { queryParams: { @@ -107,16 +113,16 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i queryParamsHandling: 'merge', }, ); - }); - }).catch(); + })); + }); } /** * Returns the matching vocabulary entry for the given search filter. * These are configurable in the config file. */ - getVocabularyEntry() { - const foundVocabularyConfig = environment.vocabularies.filter((v) => v.filter === this.filterConfig.name); + getVocabularyEntry(): string { + const foundVocabularyConfig: FilterVocabularyConfig[] = this.appConfig.vocabularies.filter((v: FilterVocabularyConfig) => v.filter === this.filterConfig.name); if (foundVocabularyConfig.length > 0 && foundVocabularyConfig[0].enabled === true) { return foundVocabularyConfig[0].vocabulary; } diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index e7dd02e2863..acd60f2616b 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -85,6 +85,7 @@ ; @@ -209,7 +211,8 @@ export function configureSearchComponentTestingModule(compType, additionalDeclar { provide: SEARCH_CONFIG_SERVICE, useValue: searchConfigurationServiceStub - } + }, + { provide: APP_CONFIG, useValue: environment }, ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(compType, { diff --git a/src/app/shared/search/search.component.ts b/src/app/shared/search/search.component.ts index fc07893d721..d2343234cc5 100644 --- a/src/app/shared/search/search.component.ts +++ b/src/app/shared/search/search.component.ts @@ -31,13 +31,13 @@ import { ViewMode } from '../../core/shared/view-mode.model'; import { SelectionConfig } from './search-results/search-results.component'; import { ListableObject } from '../object-collection/shared/listable-object.model'; import { CollectionElementLinkType } from '../object-collection/collection-element-link.type'; -import { environment } from 'src/environments/environment'; import { SubmissionObject } from '../../core/submission/models/submission-object.model'; import { SearchFilterConfig } from './models/search-filter-config.model'; import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; import { ITEM_MODULE_PATH } from '../../item-page/item-page-routing-paths'; import { COLLECTION_MODULE_PATH } from '../../collection-page/collection-page-routing-paths'; import { COMMUNITY_MODULE_PATH } from '../../community-page/community-page-routing-paths'; +import { AppConfig, APP_CONFIG } from '../../../config/app-config.interface'; @Component({ selector: 'ds-search', @@ -171,6 +171,11 @@ export class SearchComponent implements OnDestroy, OnInit { */ @Input() scope: string; + /** + * Hides the scope in the url, this can be useful when you hardcode the scope in another way + */ + @Input() hideScopeInUrl: boolean; + /** * The current configuration used during the search */ @@ -278,7 +283,9 @@ export class SearchComponent implements OnDestroy, OnInit { protected windowService: HostWindowService, @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, protected routeService: RouteService, - protected router: Router) { + protected router: Router, + @Inject(APP_CONFIG) protected appConfig: AppConfig, + ) { this.isXsOrSm$ = this.windowService.isXsOrSm(); } @@ -445,8 +452,10 @@ export class SearchComponent implements OnDestroy, OnInit { let followLinks = [ followLink('thumbnail', { isOptional: true }), followLink('item', { isOptional: true }, followLink('thumbnail', { isOptional: true })) as any, - followLink('accessStatus', { isOptional: true, shouldEmbed: environment.item.showAccessStatuses }), ]; + if (this.appConfig.item.showAccessStatuses) { + followLinks.push(followLink('accessStatus', { isOptional: true })); + } if (this.configuration === 'supervision') { followLinks.push(followLink('supervisionOrders', { isOptional: true }) as any); } @@ -476,18 +485,20 @@ export class SearchComponent implements OnDestroy, OnInit { * This method should only be called once and is essentially what SearchTrackingComponent used to do (now removed) * @private */ - private subscribeToRoutingEvents() { - this.subs.push( - this.router.events.pipe( - filter((event) => event instanceof NavigationStart), - map((event: NavigationStart) => this.getDsoUUIDFromUrl(event.url)), - hasValueOperator(), - ).subscribe((uuid) => { - if (this.resultsRD$.value.hasSucceeded) { - this.service.trackSearch(this.searchOptions$.value, this.resultsRD$.value.payload as SearchObjects, uuid); - } - }), - ); + private subscribeToRoutingEvents(): void { + if (this.trackStatistics) { + this.subs.push( + this.router.events.pipe( + filter((event) => event instanceof NavigationStart), + map((event: NavigationStart) => this.getDsoUUIDFromUrl(event.url)), + hasValueOperator(), + ).subscribe((uuid) => { + if (this.resultsRD$.value.hasSucceeded) { + this.service.trackSearch(this.searchOptions$.value, this.resultsRD$.value.payload as SearchObjects, uuid); + } + }), + ); + } } /** diff --git a/src/app/shared/search/themed-search.component.ts b/src/app/shared/search/themed-search.component.ts index ef0afad7b3c..d2f16d80b27 100644 --- a/src/app/shared/search/themed-search.component.ts +++ b/src/app/shared/search/themed-search.component.ts @@ -43,6 +43,7 @@ export class ThemedSearchComponent extends ThemedComponent { 'trackStatistics', 'query', 'scope', + 'hideScopeInUrl', 'resultFound', 'deselectObject', 'selectObject', @@ -94,6 +95,8 @@ export class ThemedSearchComponent extends ThemedComponent { @Input() scope: string; + @Input() hideScopeInUrl: boolean; + @Output() resultFound: EventEmitter> = new EventEmitter(); @Output() deselectObject: EventEmitter = new EventEmitter(); diff --git a/src/app/shared/sidebar/page-with-sidebar.component.html b/src/app/shared/sidebar/page-with-sidebar.component.html index 9feb6c792ef..75f40ef8d63 100644 --- a/src/app/shared/sidebar/page-with-sidebar.component.html +++ b/src/app/shared/sidebar/page-with-sidebar.component.html @@ -3,10 +3,12 @@
-
+
diff --git a/src/app/shared/testing/active-router.stub.ts b/src/app/shared/testing/active-router.stub.ts index 495920555b3..4c6dd456d3e 100644 --- a/src/app/shared/testing/active-router.stub.ts +++ b/src/app/shared/testing/active-router.stub.ts @@ -1,19 +1,19 @@ import { map } from 'rxjs/operators'; -import { convertToParamMap, Params } from '@angular/router'; - +import { ActivatedRoute, convertToParamMap, Data, Params } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; export class ActivatedRouteStub { - private _testParams?: any; - private _testData?: any; + private _testParams?: Params; + private _testData?: Data; // ActivatedRoute.params is Observable - private subject?: BehaviorSubject = new BehaviorSubject(this.testParams); - private dataSubject?: BehaviorSubject = new BehaviorSubject(this.testData); + private subject?: BehaviorSubject = new BehaviorSubject(this.testParams); + private dataSubject?: BehaviorSubject = new BehaviorSubject(this.testData); params = this.subject.asObservable(); queryParams = this.subject.asObservable(); paramMap = this.subject.asObservable().pipe(map((params: Params) => convertToParamMap(params))); + parent: ActivatedRoute | ActivatedRouteStub; queryParamMap = this.subject.asObservable().pipe(map((params: Params) => convertToParamMap(params))); data = this.dataSubject.asObservable(); @@ -35,17 +35,17 @@ export class ActivatedRouteStub { return this._testParams; } - set testParams(params: {}) { + set testParams(params: Params) { this._testParams = params; this.subject.next(params); } // Test data get testData() { - return this._testParams; + return this._testData; } - set testData(data: {}) { + set testData(data: Data) { this._testData = data; this.dataSubject.next(data); } diff --git a/src/app/statistics/angulartics/dspace-provider.ts b/src/app/statistics/angulartics/dspace-provider.ts index 86c16b5c011..957b09a0c8a 100644 --- a/src/app/statistics/angulartics/dspace-provider.ts +++ b/src/app/statistics/angulartics/dspace-provider.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { Angulartics2 } from 'angulartics2'; +import { Angulartics2, EventTrack } from 'angulartics2'; import { StatisticsService } from '../statistics.service'; /** @@ -23,7 +23,7 @@ export class Angulartics2DSpace { .subscribe((event) => this.eventTrack(event)); } - private eventTrack(event) { + private eventTrack(event: Partial): void { if (event.action === 'page_view') { this.statisticsService.trackViewEvent(event.properties.object, event.properties.referrer); } else if (event.action === 'search') { @@ -32,7 +32,7 @@ export class Angulartics2DSpace { event.properties.page, event.properties.sort, event.properties.filters, - event.properties.clickedObject, + event.properties.clickedObject?.split('?')[0], ); } } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index a70a107d440..a23eb0ddb9d 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1108,9 +1108,7 @@ "collection.logo": "Collection logo", - "collection.page.browse.recent.head": "Recent Submissions", - - "collection.page.browse.recent.empty": "No items to show", + "collection.page.browse.search.head": "Search", "collection.page.edit": "Edit this collection", @@ -1120,6 +1118,8 @@ "collection.page.news": "News", + "collection.search.results.head": "Search Results", + "collection.select.confirm": "Confirm selected", "collection.select.empty": "No collections to show", @@ -1196,6 +1196,8 @@ "community.browse.logo": "Browse for a community logo", + "community.subcoms-cols.breadcrumbs": "Subcommunities and Collections", + "community.create.head": "Create a Community", "community.create.notifications.success": "Successfully created the Community", @@ -1348,6 +1350,8 @@ "community.all-lists.head": "Subcommunities and Collections", + "community.search.results.head": "Search Results", + "community.sub-collection-list.head": "Collections in this Community", "community.sub-community-list.head": "Communities in this Community", diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 6c4b99cb0f4..51a116fa70e 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -8,6 +8,7 @@ import { SubmissionConfig } from './submission-config.interface'; import { FormConfig } from './form-config.interfaces'; import { LangConfig } from './lang-config.interface'; import { ItemConfig } from './item-config.interface'; +import { CommunityPageConfig } from './community-page-config.interface'; import { CollectionPageConfig } from './collection-page-config.interface'; import { ThemeConfig } from './theme.config'; import { AuthConfig } from './auth-config.interfaces'; @@ -41,6 +42,7 @@ interface AppConfig extends Config { communityList: CommunityListConfig; homePage: HomeConfig; item: ItemConfig; + community: CommunityPageConfig; collection: CollectionPageConfig; themes: ThemeConfig[]; mediaViewer: MediaViewerConfig; diff --git a/src/config/collection-page-config.interface.ts b/src/config/collection-page-config.interface.ts index c056df66eda..6b8352e686f 100644 --- a/src/config/collection-page-config.interface.ts +++ b/src/config/collection-page-config.interface.ts @@ -1,7 +1,18 @@ import { Config } from './config.interface'; +/** + * Collection Page Config + */ export interface CollectionPageConfig extends Config { + searchSection: CollectionSearchSectionConfig; edit: { undoTimeout: number; }; } + +/** + * Config related to the collection's search tab + */ +export interface CollectionSearchSectionConfig { + showSidebar: boolean; +} diff --git a/src/config/community-page-config.interface.ts b/src/config/community-page-config.interface.ts new file mode 100644 index 00000000000..268f4d6a5e3 --- /dev/null +++ b/src/config/community-page-config.interface.ts @@ -0,0 +1,15 @@ +import { Config } from './config.interface'; + +/** + * Community Page Config + */ +export interface CommunityPageConfig extends Config { + searchSection: CommunitySearchSectionConfig; +} + +/** + * Config related to the community's search tab + */ +export interface CommunitySearchSectionConfig { + showSidebar: boolean; +} diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 3b3eb00ddbf..dc90f4f9511 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -23,6 +23,7 @@ import { HomeConfig } from './homepage-config.interface'; import { MarkdownConfig } from './markdown-config.interface'; import { FilterVocabularyConfig } from './filter-vocabulary-config'; import { DiscoverySortConfig } from './discovery-sort.config'; +import { CommunityPageConfig } from './community-page-config.interface'; import {QualityAssuranceConfig} from './quality-assurance.config'; export class DefaultAppConfig implements AppConfig { @@ -291,8 +292,18 @@ export class DefaultAppConfig implements AppConfig { } }; + // Community Page Config + community: CommunityPageConfig = { + searchSection: { + showSidebar: true, + }, + }; + // Collection Page Config collection: CollectionPageConfig = { + searchSection: { + showSidebar: true, + }, edit: { undoTimeout: 10000 // 10 seconds } diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index afc4082dde3..02551e4c331 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -257,7 +257,15 @@ export const environment: BuildConfig = { pageSize: 5 } }, + community: { + searchSection: { + showSidebar: true, + }, + }, collection: { + searchSection: { + showSidebar: true, + }, edit: { undoTimeout: 10000 // 10 seconds }