diff --git a/config/config.example.yml b/config/config.example.yml
index feea06f7cb..8b010ba6ea 100644
--- a/config/config.example.yml
+++ b/config/config.example.yml
@@ -294,6 +294,8 @@ homePage:
# No. of communities to list per page on the home page
# This will always round to the nearest number from the list of page sizes. e.g. if you set it to 7 it'll use 10
pageSize: 5
+ # Enable or disable the Discover filters on the homepage
+ showDiscoverFilters: false
# Item Config
item:
@@ -422,3 +424,12 @@ comcolSelectionSort:
# suggestion:
# - collectionId: 8f7df5ca-f9c2-47a4-81ec-8a6393d6e5af
# source: "openaire"
+
+
+# Search settings
+search:
+ # Settings to enable/disable or configure advanced search filters.
+ advancedFilters:
+ enabled: false
+ # List of filters to enable in "Advanced Search" dropdown
+ filter: [ 'title', 'author', 'subject', 'entityType' ]
diff --git a/src/app/collection-page/collection-page.component.html b/src/app/collection-page/collection-page.component.html
index 21cc94af68..d5da37c12f 100644
--- a/src/app/collection-page/collection-page.component.html
+++ b/src/app/collection-page/collection-page.component.html
@@ -58,4 +58,4 @@
-
+
\ No newline at end of file
diff --git a/src/app/community-page/community-page.component.ts b/src/app/community-page/community-page.component.ts
index 206aa54cb0..148df02509 100644
--- a/src/app/community-page/community-page.component.ts
+++ b/src/app/community-page/community-page.component.ts
@@ -7,7 +7,7 @@ import { Bitstream } from '../core/shared/bitstream.model';
import { Community } from '../core/shared/community.model';
import { fadeInOut } from '../shared/animations/fade';
import { hasValue } from '../shared/empty.util';
-import { getAllSucceededRemoteDataPayload} from '../core/shared/operators';
+import { getAllSucceededRemoteDataPayload } from '../core/shared/operators';
import { AuthService } from '../core/auth/auth.service';
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../core/data/feature-authorization/feature-id';
diff --git a/src/app/home-page/home-page.component.html b/src/app/home-page/home-page.component.html
index 49329b3f04..c1d4b8a98c 100644
--- a/src/app/home-page/home-page.component.html
+++ b/src/app/home-page/home-page.component.html
@@ -1,10 +1,19 @@
-
-
-
-
-
-
- 0">
+
+
+
+
+
+
+
+
+
+
+
+ 0">
+
+
diff --git a/src/app/home-page/home-page.component.ts b/src/app/home-page/home-page.component.ts
index c151cbbb16..9adc478b90 100644
--- a/src/app/home-page/home-page.component.ts
+++ b/src/app/home-page/home-page.component.ts
@@ -1,9 +1,10 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, Inject, OnInit } from '@angular/core';
import { map } from 'rxjs/operators';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs';
import { Site } from '../core/shared/site.model';
import { environment } from '../../environments/environment';
+import { APP_CONFIG, AppConfig } from 'src/config/app-config.interface';
@Component({
selector: 'ds-home-page',
styleUrls: ['./home-page.component.scss'],
@@ -14,6 +15,7 @@ export class HomePageComponent implements OnInit {
site$: Observable;
recentSubmissionspageSize: number;
constructor(
+ @Inject(APP_CONFIG) protected appConfig: AppConfig,
private route: ActivatedRoute,
) {
this.recentSubmissionspageSize = environment.homePage.recentSubmissions.pageSize;
diff --git a/src/app/home-page/home-page.module.ts b/src/app/home-page/home-page.module.ts
index 7b656abd73..e3debef5ca 100644
--- a/src/app/home-page/home-page.module.ts
+++ b/src/app/home-page/home-page.module.ts
@@ -3,7 +3,6 @@ import { NgModule } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { HomeNewsComponent } from './home-news/home-news.component';
import { HomePageRoutingModule } from './home-page-routing.module';
-
import { HomePageComponent } from './home-page.component';
import { TopLevelCommunityListComponent } from './top-level-community-list/top-level-community-list.component';
import { StatisticsModule } from '../statistics/statistics.module';
@@ -13,6 +12,7 @@ import { RecentItemListComponent } from './recent-item-list/recent-item-list.com
import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module';
import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module';
import { ThemedTopLevelCommunityListComponent } from './top-level-community-list/themed-top-level-community-list.component';
+import { SearchModule } from '../shared/search/search.module';
import { NotificationsModule } from '../notifications/notifications.module';
const DECLARATIONS = [
@@ -29,6 +29,7 @@ const DECLARATIONS = [
imports: [
CommonModule,
SharedModule.withEntryComponents(),
+ SearchModule,
JournalEntitiesModule.withEntryComponents(),
ResearchEntitiesModule.withEntryComponents(),
HomePageRoutingModule,
diff --git a/src/app/shared/search/advanced-search/advanced-search.component.html b/src/app/shared/search/advanced-search/advanced-search.component.html
new file mode 100644
index 0000000000..e03fb40532
--- /dev/null
+++ b/src/app/shared/search/advanced-search/advanced-search.component.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/shared/search/advanced-search/advanced-search.component.scss b/src/app/shared/search/advanced-search/advanced-search.component.scss
new file mode 100644
index 0000000000..13ca767b2b
--- /dev/null
+++ b/src/app/shared/search/advanced-search/advanced-search.component.scss
@@ -0,0 +1 @@
+@import '../search-filters/search-filter/search-filter.component.scss';
\ No newline at end of file
diff --git a/src/app/shared/search/advanced-search/advanced-search.component.spec.ts b/src/app/shared/search/advanced-search/advanced-search.component.spec.ts
new file mode 100644
index 0000000000..a6a7f46491
--- /dev/null
+++ b/src/app/shared/search/advanced-search/advanced-search.component.spec.ts
@@ -0,0 +1,74 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormBuilderService } from '../../../shared/form/builder/form-builder.service';
+import { AdvancedSearchComponent } from './advanced-search.component';
+import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock';
+import { SearchService } from '../../../core/shared/search/search.service';
+import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
+import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub';
+import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service';
+import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { APP_CONFIG } from '../../../../config/app-config.interface';
+import { environment } from '../../../../environments/environment';
+import { RouterStub } from '../../testing/router.stub';
+import { Router } from '@angular/router';
+import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
+import { BrowserOnlyMockPipe } from '../../testing/browser-only-mock.pipe';
+import { RouterTestingModule } from '@angular/router/testing';
+import { TranslateModule } from '@ngx-translate/core';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+describe('AdvancedSearchComponent', () => {
+ let component: AdvancedSearchComponent;
+ let fixture: ComponentFixture;
+ let builderService: FormBuilderService = getMockFormBuilderService();
+ let searchService: SearchService;
+ let router;
+ const searchServiceStub = {
+ /* eslint-disable no-empty,@typescript-eslint/no-empty-function */
+ getClearFiltersQueryParams: () => {
+ },
+ getSearchLink: () => {
+ },
+ getConfigurationSearchConfig: () => { },
+ /* eslint-enable no-empty, @typescript-eslint/no-empty-function */
+ };
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [AdvancedSearchComponent, BrowserOnlyMockPipe],
+ imports: [FormsModule, RouterTestingModule, TranslateModule.forRoot(), BrowserAnimationsModule, ReactiveFormsModule],
+ providers: [
+ FormBuilder,
+ { provide: APP_CONFIG, useValue: environment },
+ { provide: FormBuilderService, useValue: builderService },
+ { provide: Router, useValue: new RouterStub() },
+ { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
+ { provide: RemoteDataBuildService, useValue: {} },
+ { provide: SearchService, useValue: searchServiceStub },
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ }).overrideComponent(AdvancedSearchComponent, {
+ set: { changeDetection: ChangeDetectionStrategy.Default }
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AdvancedSearchComponent);
+ component = fixture.componentInstance;
+ router = TestBed.inject(Router);
+ fixture.detectChanges();
+ });
+ describe('when the getSearchLink method is called', () => {
+ const data = { filter: 'title', textsearch: 'demo', operator: 'equals' };
+ it('should call navigate on the router with the right searchlink and parameters when the filter is provided with a valid operator', () => {
+ component.advSearchForm.get('textsearch').patchValue('1');
+ component.advSearchForm.get('filter').patchValue('1');
+ component.advSearchForm.get('operator').patchValue('1');
+
+ component.onSubmit(data);
+ expect(router.navigate).toHaveBeenCalledWith([undefined], {
+ queryParams: { ['f.' + data.filter]: data.textsearch + ',' + data.operator },
+ queryParamsHandling: 'merge'
+ });
+
+ });
+ });
+});
diff --git a/src/app/shared/search/advanced-search/advanced-search.component.ts b/src/app/shared/search/advanced-search/advanced-search.component.ts
new file mode 100644
index 0000000000..ca6a7c9e28
--- /dev/null
+++ b/src/app/shared/search/advanced-search/advanced-search.component.ts
@@ -0,0 +1,115 @@
+import { Component, Inject, Input, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { slide } from '../../animations/slide';
+import { FormBuilder } from '@angular/forms';
+import { FormControl, FormGroup, Validators } from '@angular/forms';
+import { SearchService } from '../../../core/shared/search/search.service';
+import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
+import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
+import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
+@Component({
+ selector: 'ds-advanced-search',
+ templateUrl: './advanced-search.component.html',
+ styleUrls: ['./advanced-search.component.scss'],
+ animations: [slide],
+})
+ /**
+ * This component represents the part of the search sidebar that contains advanced filters.
+ */
+export class AdvancedSearchComponent implements OnInit {
+ /**
+ * True when the search component should show results on the current page
+ */
+ @Input() inPlaceSearch;
+
+
+ /**
+ * Link to the search page
+ */
+ notab: boolean;
+
+ closed: boolean;
+ collapsedSearch = false;
+ focusBox = false;
+
+ advSearchForm: FormGroup;
+ constructor(
+ @Inject(APP_CONFIG) protected appConfig: AppConfig,
+ private formBuilder: FormBuilder,
+ protected searchService: SearchService,
+ protected router: Router,
+ @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) {
+ }
+
+ ngOnInit(): void {
+
+ this.advSearchForm = this.formBuilder.group({
+ textsearch: new FormControl('', {
+ validators: [Validators.required],
+ }),
+ filter: new FormControl('title', {
+ validators: [Validators.required],
+ }),
+ operator: new FormControl('equals',
+ { validators: [Validators.required], }),
+
+ });
+ this.collapsedSearch = this.isCollapsed();
+
+ }
+
+ get textsearch() {
+ return this.advSearchForm.get('textsearch');
+ }
+
+ get filter() {
+ return this.advSearchForm.get('filter');
+ }
+
+ get operator() {
+ return this.advSearchForm.get('operator');
+ }
+ paramName(filter) {
+ return 'f.' + filter;
+ }
+ onSubmit(data) {
+ if (this.advSearchForm.valid) {
+ let queryParams = { [this.paramName(data.filter)]: data.textsearch + ',' + data.operator };
+ if (!this.inPlaceSearch) {
+ this.router.navigate([this.searchService.getSearchLink()], { queryParams: queryParams, queryParamsHandling: 'merge' });
+ } else {
+ if (!this.router.url.includes('?')) {
+ this.router.navigateByUrl(this.router.url + '?f.' + data.filter + '=' + data.textsearch + ',' + data.operator);
+ } else {
+ this.router.navigateByUrl(this.router.url + '&f.' + data.filter + '=' + data.textsearch + ',' + data.operator);
+ }
+ }
+
+ this.advSearchForm.reset({ operator: data.operator, filter: data.filter, textsearch: '' });
+ }
+ }
+ startSlide(event: any): void {
+ if (event.toState === 'collapsed') {
+ this.closed = true;
+ }
+ if (event.fromState === 'collapsed') {
+ this.notab = false;
+ }
+ }
+ finishSlide(event: any): void {
+ if (event.fromState === 'collapsed') {
+ this.closed = false;
+ }
+ if (event.toState === 'collapsed') {
+ this.notab = true;
+ }
+ }
+ toggle() {
+ this.collapsedSearch = !this.collapsedSearch;
+ }
+ private isCollapsed(): boolean {
+ return !this.collapsedSearch;
+ }
+
+}
+
diff --git a/src/app/shared/search/search-filters/search-filter/search-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-filter.component.ts
index 67e8906bb5..64b5ba23e2 100644
--- a/src/app/shared/search/search-filters/search-filter/search-filter.component.ts
+++ b/src/app/shared/search/search-filters/search-filter/search-filter.component.ts
@@ -157,11 +157,20 @@ export class SearchFilterComponent implements OnInit {
}
get regionId(): string {
- return `search-filter-region-${this.sequenceId}`;
+ if (this.inPlaceSearch) {
+ return `search-filter-region-${this.sequenceId}`;
+ } else {
+ return `search-filter-region-home-${this.sequenceId}`;
+ }
+
}
get toggleId(): string {
- return `search-filter-toggle-${this.sequenceId}`;
+ if (this.inPlaceSearch) {
+ return `search-filter-toggle-${this.sequenceId}`;
+ } else {
+ return `search-filter-toggle-home-${this.sequenceId}`;
+ }
}
/**
diff --git a/src/app/shared/search/search-filters/search-filters.component.html b/src/app/shared/search/search-filters/search-filters.component.html
index c006d80c44..3f70a759bc 100644
--- a/src/app/shared/search/search-filters/search-filters.component.html
+++ b/src/app/shared/search/search-filters/search-filters.component.html
@@ -1,7 +1,10 @@
-
{{"search.filters.head" | translate}}
+
{{filterLabel+'.filters.head' | translate}}
+
{{filterLabel+'.filters.head' | translate}}
+ {{"search.filters.reset" | translate}}
diff --git a/src/app/shared/search/search-filters/search-filters.component.spec.ts b/src/app/shared/search/search-filters/search-filters.component.spec.ts
index 522459b603..246b9fb8b7 100644
--- a/src/app/shared/search/search-filters/search-filters.component.spec.ts
+++ b/src/app/shared/search/search-filters/search-filters.component.spec.ts
@@ -9,6 +9,8 @@ import { SearchFiltersComponent } from './search-filters.component';
import { SearchService } from '../../../core/shared/search/search.service';
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub';
+import { APP_CONFIG } from 'src/config/app-config.interface';
+import { environment } from 'src/environments/environment';
describe('SearchFiltersComponent', () => {
let comp: SearchFiltersComponent;
@@ -20,7 +22,8 @@ describe('SearchFiltersComponent', () => {
getClearFiltersQueryParams: () => {
},
getSearchLink: () => {
- }
+ },
+ getConfigurationSearchConfig: () => { },
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */
};
@@ -37,6 +40,7 @@ describe('SearchFiltersComponent', () => {
{ provide: SearchService, useValue: searchServiceStub },
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
{ provide: SearchFilterService, useValue: searchFiltersStub },
+ { provide: APP_CONFIG, useValue: environment },
],
schemas: [NO_ERRORS_SCHEMA]
diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts
index 766939226d..51065f827f 100644
--- a/src/app/shared/search/search-filters/search-filters.component.ts
+++ b/src/app/shared/search/search-filters/search-filters.component.ts
@@ -3,7 +3,7 @@ import { Router } from '@angular/router';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
-
+import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
import { SearchService } from '../../../core/shared/search/search.service';
import { RemoteData } from '../../../core/data/remote-data';
import { SearchFilterConfig } from '../models/search-filter-config.model';
@@ -12,6 +12,7 @@ import { SearchFilterService } from '../../../core/shared/search/search-filter.s
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
import { currentPath } from '../../utils/route.utils';
import { hasValue } from '../../empty.util';
+import { PaginatedSearchOptions } from '../models/paginated-search-options.model';
@Component({
selector: 'ds-search-filters',
@@ -28,7 +29,7 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
* An observable containing configuration about which filters are shown and how they are shown
*/
@Input() filters: Observable>;
-
+ @Input() searchOptions: PaginatedSearchOptions;
/**
* List of all filters that are currently active with their value set to null.
* Used to reset all filters at once
@@ -61,6 +62,7 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
searchLink: string;
subs = [];
+ filterLabel = 'search';
/**
* Initialize instance variables
@@ -70,6 +72,7 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
* @param {SearchConfigurationService} searchConfigService
*/
constructor(
+ @Inject(APP_CONFIG) protected appConfig: AppConfig,
private searchService: SearchService,
private filterService: SearchFilterService,
private router: Router,
@@ -77,6 +80,9 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
+ if (!this.inPlaceSearch) {
+ this.filterLabel = 'discover';
+ }
this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => {
Object.keys(filters).forEach((f) => filters[f] = null);
return filters;
diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html
index 529f1de887..f14854593e 100644
--- a/src/app/shared/search/search-sidebar/search-sidebar.component.html
+++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html
@@ -23,7 +23,7 @@
[filters]="filters"
[refreshFilters]="refreshFilters"
[inPlaceSearch]="inPlaceSearch">
-