Merge branch 'main' into CST-12109-WITHDRAWN-REINSTATE-requests

This commit is contained in:
Alisa Ismailati
2024-02-22 17:19:08 +01:00
35 changed files with 288 additions and 222 deletions

View File

@@ -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"

View File

@@ -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',

View File

@@ -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({

View File

@@ -1,18 +0,0 @@
<ng-container *ngVar="(itemRD$ | async) as itemRD">
<div class="mt-4" *ngIf="itemRD?.hasSucceeded" @fadeIn>
<h3 class="sr-only">{{'collection.page.browse.recent.head' | translate}}</h3>
<ds-viewable-collection
[config]="paginationConfig"
[sortConfig]="sortConfig"
[objects]="itemRD"
[hideGear]="true">
</ds-viewable-collection>
</div>
<ds-error *ngIf="itemRD?.hasFailed"
message="{{'error.recent-submissions' | translate}}"></ds-error>
<ds-themed-loading *ngIf="!itemRD || itemRD.isLoading"
message="{{'loading.recent-submissions' | translate}}"></ds-themed-loading>
<div *ngIf="!itemRD?.isLoading && itemRD?.payload?.page.length === 0" class="alert alert-info w-100" role="alert">
{{'collection.page.browse.recent.empty' | translate}}
</div>
</ng-container>

View File

@@ -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<CollectionRecentlyAddedComponent>;
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();
});
});

View File

@@ -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<RemoteData<Collection>>;
itemRD$: Observable<RemoteData<PaginatedList<Item>>>;
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<Collection>),
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<Collection>) => rd.payload.id),
switchMap((id: string) => this.searchService.search<Item>(
new PaginatedSearchOptions({
scope: id,
pagination: currentPagination,
sort: currentSort,
dsoTypes: [DSpaceObjectType.ITEM]
}), null, true, true, ...BROWSE_LINKS_TO_FOLLOW).pipe(
toDSpaceObjectListRD()
) as Observable<RemoteData<PaginatedList<Item>>>),
startWith(undefined), // Make sure switching pages shows loading component
)),
);
}
ngOnDestroy(): void {
this.paginationService.clearPagination(this.paginationConfig.id);
}
}

View File

@@ -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',

View File

@@ -11,6 +11,7 @@ describe('SubComColSectionComponent', () => {
beforeEach(async () => {
activatedRoute = new ActivatedRouteStub();
activatedRoute.parent = new ActivatedRouteStub();
await TestBed.configureTestingModule({
declarations: [

View File

@@ -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<Community>).payload),
);
}

View File

@@ -88,7 +88,7 @@ describe('SearchNavbarComponent', () => {
fixture.detectChanges();
}));
it('to search page with empty query', () => {
const extras: NavigationExtras = { queryParams: { query: '' }, queryParamsHandling: 'merge' };
const extras: NavigationExtras = { queryParams: { query: '' } };
expect(component.onSubmit).toHaveBeenCalledWith({ query: '' });
expect(router.navigate).toHaveBeenCalledWith(['search'], extras);
});
@@ -113,7 +113,7 @@ describe('SearchNavbarComponent', () => {
fixture.detectChanges();
}));
it('to search page with query', async () => {
const extras: NavigationExtras = { queryParams: { query: 'test' }, queryParamsHandling: 'merge' };
const extras: NavigationExtras = { queryParams: { query: 'test' } };
expect(component.onSubmit).toHaveBeenCalledWith({ query: 'test' });
expect(router.navigate).toHaveBeenCalledWith(['search'], extras);

View File

@@ -66,8 +66,7 @@ export class SearchNavbarComponent {
this.searchForm.reset();
this.router.navigate(linkToNavigateTo, {
queryParams: queryParams,
queryParamsHandling: 'merge'
queryParams: queryParams
});
}
}

View File

@@ -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);
}
}

View File

@@ -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`,
});
}

View File

@@ -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,

View File

@@ -0,0 +1,7 @@
<ds-themed-search
[configuration]="(comcol$ | async)?.type"
[showSidebar]="showSidebar$ | async"
[showScopeSelector]="false"
[hideScopeInUrl]="true"
[scope]="(comcol$ | async)?.id">
</ds-themed-search>

View File

@@ -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<ComcolSearchSectionComponent>;
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();
});
});

View File

@@ -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<Community | Collection>;
showSidebar$: Observable<boolean>;
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<Community | Collection>).payload),
);
this.showSidebar$ = this.comcol$.pipe(
map((comcol: Community | Collection) => hasValue(comcol) && this.appConfig[comcol.type as any].searchSection.showSidebar),
);
}
}

View File

@@ -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<DSpaceObject> = new BehaviorSubject<DSpaceObject>(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,

View File

@@ -18,6 +18,8 @@ export class ThemedSearchFormComponent extends ThemedComponent<SearchFormCompone
@Input() scope: string;
@Input() hideScopeInUrl: boolean;
@Input() currentUrl: string;
@Input() large: boolean;
@@ -31,7 +33,15 @@ export class ThemedSearchFormComponent extends ThemedComponent<SearchFormCompone
@Output() submitSearch: EventEmitter<any> = 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',
];

View File

@@ -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<VocabularyEntryDetail>(),
};
@@ -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', () => {

View File

@@ -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,10 +70,12 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i
super.onSubmit(addOperatorToFilterValue(data, 'query'));
}
ngOnInit() {
ngOnInit(): void {
super.ngOnInit();
const vocabularyName: string = this.getVocabularyEntry();
if (hasValue(vocabularyName)) {
this.vocabularyExists$ = this.vocabularyService.searchTopEntries(
this.getVocabularyEntry(), new PageInfo(), true, false,
vocabularyName, new PageInfo(), true, false,
).pipe(
filter(rd => rd.hasCompleted),
take(1),
@@ -79,6 +84,7 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i
}),
);
}
}
/**
* Open the vocabulary tree modal popup.
@@ -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;
}

View File

@@ -85,6 +85,7 @@
<ds-themed-search-form *ngIf="searchEnabled" id="search-form"
[query]="(searchOptions$ | async)?.query"
[scope]="(searchOptions$ | async)?.scope"
[hideScopeInUrl]="hideScopeInUrl"
[currentUrl]="searchLink"
[showScopeSelector]="showScopeSelector"
[inPlaceSearch]="inPlaceSearch"

View File

@@ -33,6 +33,8 @@ import { SearchFilterConfig } from './models/search-filter-config.model';
import { FilterType } from './models/filter-type.model';
import { getCommunityPageRoute } from '../../community-page/community-page-routing-paths';
import { getCollectionPageRoute } from '../../collection-page/collection-page-routing-paths';
import { environment } from '../../../environments/environment.test';
import { APP_CONFIG } from '../../../config/app-config.interface';
let comp: SearchComponent;
let fixture: ComponentFixture<SearchComponent>;
@@ -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, {

View File

@@ -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<Item>('thumbnail', { isOptional: true }),
followLink<SubmissionObject>('item', { isOptional: true }, followLink<Item>('thumbnail', { isOptional: true })) as any,
followLink<Item>('accessStatus', { isOptional: true, shouldEmbed: environment.item.showAccessStatuses }),
];
if (this.appConfig.item.showAccessStatuses) {
followLinks.push(followLink<Item>('accessStatus', { isOptional: true }));
}
if (this.configuration === 'supervision') {
followLinks.push(followLink<WorkspaceItem>('supervisionOrders', { isOptional: true }) as any);
}
@@ -476,7 +485,8 @@ 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() {
private subscribeToRoutingEvents(): void {
if (this.trackStatistics) {
this.subs.push(
this.router.events.pipe(
filter((event) => event instanceof NavigationStart),
@@ -489,6 +499,7 @@ export class SearchComponent implements OnDestroy, OnInit {
}),
);
}
}
/**
* Get the UUID from a DSO url

View File

@@ -43,6 +43,7 @@ export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
'trackStatistics',
'query',
'scope',
'hideScopeInUrl',
'resultFound',
'deselectObject',
'selectObject',
@@ -94,6 +95,8 @@ export class ThemedSearchComponent extends ThemedComponent<SearchComponent> {
@Input() scope: string;
@Input() hideScopeInUrl: boolean;
@Output() resultFound: EventEmitter<SearchObjects<DSpaceObject>> = new EventEmitter();
@Output() deselectObject: EventEmitter<ListableObject> = new EventEmitter();

View File

@@ -3,10 +3,12 @@
<div class="row-with-sidebar row-offcanvas row-offcanvas-left"
[@pushInOut]="(isSidebarCollapsed$ | async) ? 'collapsed' : 'expanded'">
<div id="{{id}}-sidebar-content"
[class.invisible]="(isSidebarCollapsed$ | async) === true && (isXsOrSm$ | async) === true"
class="col-12 col-md-{{sideBarWidth}} sidebar-content {{sidebarClasses | async}}">
<ng-container *ngTemplateOutlet="sidebarContent"></ng-container>
</div>
<div class="col-12 col-md-{{12 - sideBarWidth}}">
<div class="col-12 col-md-{{12 - sideBarWidth}}"
[class.invisible]="(isSidebarCollapsed$ | async) === false && (isXsOrSm$ | async) === true">
<ng-content></ng-content>
</div>
</div>

View File

@@ -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<any> = new BehaviorSubject(this.testParams);
private dataSubject?: BehaviorSubject<any> = new BehaviorSubject(this.testData);
private subject?: BehaviorSubject<Params> = new BehaviorSubject(this.testParams);
private dataSubject?: BehaviorSubject<Data> = 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);
}

View File

@@ -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<EventTrack>): 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],
);
}
}

View File

@@ -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",
@@ -4424,6 +4428,8 @@
"submission.import-external.source.datacite": "DataCite",
"submission.import-external.source.doi": "DOI",
"submission.import-external.source.scielo": "SciELO",
"submission.import-external.source.scopus": "Scopus",

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -257,7 +257,15 @@ export const environment: BuildConfig = {
pageSize: 5
}
},
community: {
searchSection: {
showSidebar: true,
},
},
collection: {
searchSection: {
showSidebar: true,
},
edit: {
undoTimeout: 10000 // 10 seconds
}