diff --git a/resources/i18n/en.json5 b/resources/i18n/en.json5
index 5b3747caf6..e484ce4926 100644
--- a/resources/i18n/en.json5
+++ b/resources/i18n/en.json5
@@ -826,6 +826,14 @@
"item.page.related-items.view-less": "View less",
+ "item.page.relationships.isAuthorOfPublication": "Publications",
+
+ "item.page.relationships.isJournalOfPublication": "Publications",
+
+ "item.page.relationships.isOrgUnitOfPerson": "Authors",
+
+ "item.page.relationships.isOrgUnitOfProject": "Research Projects",
+
"item.page.subject": "Keywords",
"item.page.uri": "URI",
@@ -1276,6 +1284,8 @@
"project.page.titleprefix": "Research Project: ",
+ "project.search.results.head": "Project Search Results",
+
"publication.listelement.badge": "Publication",
diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts
index 28460f567a..940868a0c6 100644
--- a/src/app/+item-page/item-page.module.ts
+++ b/src/app/+item-page/item-page.module.ts
@@ -26,6 +26,7 @@ import { MetadataRepresentationListComponent } from './simple/metadata-represent
import { RelatedEntitiesSearchComponent } from './simple/related-entities/related-entities-search/related-entities-search.component';
import { MetadataValuesComponent } from './field-components/metadata-values/metadata-values.component';
import { MetadataFieldWrapperComponent } from './field-components/metadata-field-wrapper/metadata-field-wrapper.component';
+import { TabbedRelatedEntitiesSearchComponent } from './simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component';
import { StatisticsModule } from '../statistics/statistics.module';
@NgModule({
@@ -55,7 +56,8 @@ import { StatisticsModule } from '../statistics/statistics.module';
ItemComponent,
GenericItemPageFieldComponent,
MetadataRepresentationListComponent,
- RelatedEntitiesSearchComponent
+ RelatedEntitiesSearchComponent,
+ TabbedRelatedEntitiesSearchComponent
],
exports: [
ItemComponent,
@@ -65,7 +67,8 @@ import { StatisticsModule } from '../statistics/statistics.module';
RelatedEntitiesSearchComponent,
RelatedItemsComponent,
MetadataRepresentationListComponent,
- ItemPageTitleFieldComponent
+ ItemPageTitleFieldComponent,
+ TabbedRelatedEntitiesSearchComponent
],
entryComponents: [
PublicationComponent
diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html
index c7e1f77264..75f3b7aaad 100644
--- a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html
+++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html
@@ -1,6 +1,7 @@
-
-
+
diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts
index 711e1b9d3d..7eeddf8e70 100644
--- a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts
+++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts
@@ -16,7 +16,7 @@ describe('RelatedEntitiesSearchComponent', () => {
id: 'id1'
});
const mockRelationType = 'publicationsOfAuthor';
- const mockRelationEntityType = 'publication';
+ const mockConfiguration = 'publication';
const mockFilter= `f.${mockRelationType}=${mockItem.id}`;
const fixedFilterServiceStub = {
getFilterByRelation: () => mockFilter
@@ -39,7 +39,7 @@ describe('RelatedEntitiesSearchComponent', () => {
fixedFilterService = (comp as any).fixedFilterService;
comp.relationType = mockRelationType;
comp.item = mockItem;
- comp.relationEntityType = mockRelationEntityType;
+ comp.configuration = mockConfiguration;
fixture.detectChanges();
});
@@ -49,7 +49,7 @@ describe('RelatedEntitiesSearchComponent', () => {
it('should create a configuration$', () => {
comp.configuration$.subscribe((configuration) => {
- expect(configuration).toEqual(mockRelationEntityType);
+ expect(configuration).toEqual(mockConfiguration);
})
});
diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts
index 4c0b127925..8f65cb9858 100644
--- a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts
+++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts
@@ -22,18 +22,16 @@ export class RelatedEntitiesSearchComponent implements OnInit {
*/
@Input() relationType: string;
+ /**
+ * An optional configuration to use for the search options
+ */
+ @Input() configuration: string;
+
/**
* The item to render relationships for
*/
@Input() item: Item;
- /**
- * The entity type of the relationship items to be displayed
- * e.g. 'publication'
- * This determines the title of the search results (if search is enabled)
- */
- @Input() relationEntityType: string;
-
/**
* Whether or not the search bar and title should be displayed (defaults to true)
* @type {boolean}
@@ -56,8 +54,8 @@ export class RelatedEntitiesSearchComponent implements OnInit {
if (isNotEmpty(this.relationType) && isNotEmpty(this.item)) {
this.fixedFilter = this.fixedFilterService.getFilterByRelation(this.relationType, this.item.id);
}
- if (isNotEmpty(this.relationEntityType)) {
- this.configuration$ = of(this.relationEntityType);
+ if (isNotEmpty(this.configuration)) {
+ this.configuration$ = of(this.configuration);
}
}
diff --git a/src/app/+item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.html b/src/app/+item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.html
new file mode 100644
index 0000000000..f9642d2c01
--- /dev/null
+++ b/src/app/+item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.html
@@ -0,0 +1,22 @@
+ 1" [destroyOnHide]="true" #tabs="ngbTabset" [activeId]="activeTab$ | async" (tabChange)="onTabChange($event)">
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/+item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.spec.ts b/src/app/+item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.spec.ts
new file mode 100644
index 0000000000..2d2e682196
--- /dev/null
+++ b/src/app/+item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.spec.ts
@@ -0,0 +1,82 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { Item } from '../../../../core/shared/item.model';
+import { TranslateModule } from '@ngx-translate/core';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { TabbedRelatedEntitiesSearchComponent } from './tabbed-related-entities-search.component';
+import { ActivatedRoute, Router } from '@angular/router';
+import { MockRouter } from '../../../../shared/mocks/mock-router';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { VarDirective } from '../../../../shared/utils/var.directive';
+import { of as observableOf } from 'rxjs';
+
+describe('TabbedRelatedEntitiesSearchComponent', () => {
+ let comp: TabbedRelatedEntitiesSearchComponent;
+ let fixture: ComponentFixture;
+
+ const mockItem = Object.assign(new Item(), {
+ id: 'id1'
+ });
+ const mockRelationType = 'publications';
+ const relationTypes = [
+ {
+ label: mockRelationType,
+ filter: mockRelationType
+ }
+ ];
+
+ const router = new MockRouter();
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [TranslateModule.forRoot(), NoopAnimationsModule, NgbModule.forRoot()],
+ declarations: [TabbedRelatedEntitiesSearchComponent, VarDirective],
+ providers: [
+ {
+ provide: ActivatedRoute,
+ useValue: {
+ queryParams: observableOf({ tab: mockRelationType })
+ },
+ },
+ { provide: Router, useValue: router }
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TabbedRelatedEntitiesSearchComponent);
+ comp = fixture.componentInstance;
+ comp.item = mockItem;
+ comp.relationTypes = relationTypes;
+ fixture.detectChanges();
+ });
+
+ it('should initialize the activeTab depending on the current query parameters', () => {
+ comp.activeTab$.subscribe((activeTab) => {
+ expect(activeTab).toEqual(mockRelationType);
+ });
+ });
+
+ describe('onTabChange', () => {
+ const event = {
+ currentId: mockRelationType,
+ nextId: 'nextTab'
+ };
+
+ beforeEach(() => {
+ comp.onTabChange(event);
+ });
+
+ it('should call router natigate with the correct arguments', () => {
+ expect(router.navigate).toHaveBeenCalledWith([], {
+ relativeTo: (comp as any).route,
+ queryParams: {
+ tab: event.nextId
+ },
+ queryParamsHandling: 'merge'
+ });
+ });
+ });
+
+});
diff --git a/src/app/+item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.ts b/src/app/+item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.ts
new file mode 100644
index 0000000000..b01eb70720
--- /dev/null
+++ b/src/app/+item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component.ts
@@ -0,0 +1,76 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { Item } from '../../../../core/shared/item.model';
+import { ActivatedRoute, Router } from '@angular/router';
+import { Observable } from 'rxjs/internal/Observable';
+import { map } from 'rxjs/operators';
+
+@Component({
+ selector: 'ds-tabbed-related-entities-search',
+ templateUrl: './tabbed-related-entities-search.component.html'
+})
+/**
+ * A component to show related items as search results, split into tabs by relationship-type
+ * Related items can be facetted, or queried using an
+ * optional search box.
+ */
+export class TabbedRelatedEntitiesSearchComponent implements OnInit {
+ /**
+ * The types of relationships to fetch items for
+ * e.g. 'isAuthorOfPublication'
+ */
+ @Input() relationTypes: Array<{
+ label: string,
+ filter: string,
+ configuration?: string
+ }>;
+
+ /**
+ * The item to render relationships for
+ */
+ @Input() item: Item;
+
+ /**
+ * Whether or not the search bar and title should be displayed (defaults to true)
+ * @type {boolean}
+ */
+ @Input() searchEnabled = true;
+
+ /**
+ * The ratio of the sidebar's width compared to the search results (1-12) (defaults to 4)
+ * @type {number}
+ */
+ @Input() sideBarWidth = 4;
+
+ /**
+ * The active tab
+ */
+ activeTab$: Observable;
+
+ constructor(private route: ActivatedRoute,
+ private router: Router) {
+ }
+
+ /**
+ * If the url contains a "tab" query parameter, set this tab to be the active tab
+ */
+ ngOnInit(): void {
+ this.activeTab$ = this.route.queryParams.pipe(
+ map((params) => params.tab)
+ );
+ }
+
+ /**
+ * Add a "tab" query parameter to the URL when changing tabs
+ * @param event
+ */
+ onTabChange(event) {
+ this.router.navigate([], {
+ relativeTo: this.route,
+ queryParams: {
+ tab: event.nextId
+ },
+ queryParamsHandling: 'merge'
+ });
+ }
+
+}
diff --git a/src/app/+search-page/configuration-search-page.component.ts b/src/app/+search-page/configuration-search-page.component.ts
index 1296f3a203..34446e8371 100644
--- a/src/app/+search-page/configuration-search-page.component.ts
+++ b/src/app/+search-page/configuration-search-page.component.ts
@@ -35,6 +35,12 @@ export class ConfigurationSearchPageComponent extends SearchComponent implements
*/
@Input() configuration: string;
+ /**
+ * The actual query for the fixed filter.
+ * If empty, the query will be determined by the route parameter called 'filter'
+ */
+ @Input() fixedFilterQuery: string;
+
constructor(protected service: SearchService,
protected sidebarService: SidebarService,
protected windowService: HostWindowService,
@@ -64,7 +70,11 @@ export class ConfigurationSearchPageComponent extends SearchComponent implements
return this.searchConfigService.paginatedSearchOptions.pipe(
map((options: PaginatedSearchOptions) => {
const config = this.configuration || options.configuration;
- return Object.assign(options, { configuration: config });
+ const filter = this.fixedFilterQuery || options.fixedFilter;
+ return Object.assign(options, {
+ configuration: config,
+ fixedFilter: filter
+ });
})
);
}
diff --git a/src/app/+search-page/filtered-search-page.component.spec.ts b/src/app/+search-page/filtered-search-page.component.spec.ts
deleted file mode 100644
index cf1668d8bc..0000000000
--- a/src/app/+search-page/filtered-search-page.component.spec.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { FilteredSearchPageComponent } from './filtered-search-page.component';
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { configureSearchComponentTestingModule } from './search.component.spec';
-import { SearchConfigurationService } from './search-service/search-configuration.service';
-
-describe('FilteredSearchPageComponent', () => {
- let comp: FilteredSearchPageComponent;
- let fixture: ComponentFixture;
- let searchConfigService: SearchConfigurationService;
-
- beforeEach(async(() => {
- configureSearchComponentTestingModule(FilteredSearchPageComponent);
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(FilteredSearchPageComponent);
- comp = fixture.componentInstance;
- searchConfigService = (comp as any).searchConfigService;
- fixture.detectChanges();
- });
-});
diff --git a/src/app/+search-page/filtered-search-page.component.ts b/src/app/+search-page/filtered-search-page.component.ts
deleted file mode 100644
index 19e5c09f71..0000000000
--- a/src/app/+search-page/filtered-search-page.component.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import { HostWindowService } from '../shared/host-window.service';
-import { SearchService } from './search-service/search.service';
-import { SidebarService } from '../shared/sidebar/sidebar.service';
-import { SearchComponent } from './search.component';
-import { ChangeDetectionStrategy, Component, Inject, Input, OnInit } from '@angular/core';
-import { pushInOut } from '../shared/animations/push';
-import { SearchConfigurationService } from './search-service/search-configuration.service';
-import { Observable } from 'rxjs';
-import { PaginatedSearchOptions } from './paginated-search-options.model';
-import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component';
-import { map } from 'rxjs/operators';
-import { RouteService } from '../core/services/route.service';
-
-/**
- * This component renders a simple item page.
- * The route parameter 'id' is used to request the item it represents.
- * All fields of the item that should be displayed, are defined in its template.
- */
-@Component({
- selector: 'ds-filtered-search-page',
- styleUrls: ['./search.component.scss'],
- templateUrl: './search.component.html',
- changeDetection: ChangeDetectionStrategy.OnPush,
- animations: [pushInOut],
- providers: [
- {
- provide: SEARCH_CONFIG_SERVICE,
- useClass: SearchConfigurationService
- }
- ]
-})
-
-export class FilteredSearchPageComponent extends SearchComponent implements OnInit {
- /**
- * The actual query for the fixed filter.
- * If empty, the query will be determined by the route parameter called 'filter'
- */
- @Input() fixedFilterQuery: string;
-
- constructor(protected service: SearchService,
- protected sidebarService: SidebarService,
- protected windowService: HostWindowService,
- @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
- protected routeService: RouteService) {
- super(service, sidebarService, windowService, searchConfigService, routeService);
- }
-
- /**
- * Listening to changes in the paginated search options
- * If something changes, update the search results
- *
- * Listen to changes in the scope
- * If something changes, update the list of scopes for the dropdown
- */
- ngOnInit(): void {
- super.ngOnInit();
- }
-
- /**
- * Get the current paginated search options after updating the fixed filter using the fixedFilterQuery input
- * This is to make sure the fixed filter is included in the paginated search options, as it is not part of any
- * query or route parameters
- * @returns {Observable}
- */
- protected getSearchOptions(): Observable {
- return this.searchConfigService.paginatedSearchOptions.pipe(
- map((options: PaginatedSearchOptions) => {
- const filter = this.fixedFilterQuery || options.fixedFilter;
- return Object.assign(options, { fixedFilter: filter });
- })
- );
- }
-}
diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts
index 8ee81e549b..9eab7d1533 100644
--- a/src/app/+search-page/search-page.module.ts
+++ b/src/app/+search-page/search-page.module.ts
@@ -32,7 +32,6 @@ import { SearchAuthorityFilterComponent } from './search-filters/search-filter/s
import { SearchLabelComponent } from './search-labels/search-label/search-label.component';
import { ConfigurationSearchPageComponent } from './configuration-search-page.component';
import { ConfigurationSearchPageGuard } from './configuration-search-page.guard';
-import { FilteredSearchPageComponent } from './filtered-search-page.component';
import { SearchPageComponent } from './search-page.component';
import { SidebarFilterService } from '../shared/sidebar/filter/sidebar-filter.service';
import { StatisticsModule } from '../statistics/statistics.module';
@@ -64,7 +63,6 @@ const components = [
SearchFacetRangeOptionComponent,
SearchSwitchConfigurationComponent,
SearchAuthorityFilterComponent,
- FilteredSearchPageComponent,
ConfigurationSearchPageComponent,
SearchTrackerComponent,
];
diff --git a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html
index ae6c3a8914..e86ab35e0e 100644
--- a/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html
+++ b/src/app/entity-groups/journal-entities/item-pages/journal/journal.component.html
@@ -36,8 +36,11 @@
-
-
+
+
diff --git a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html
index 4d97868b58..1b23d567f5 100644
--- a/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html
+++ b/src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html
@@ -24,16 +24,6 @@
-
-
-
-
+
+
+
+
diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.html b/src/app/entity-groups/research-entities/item-pages/person/person.component.html
index ff675ab057..97a3cf416e 100644
--- a/src/app/entity-groups/research-entities/item-pages/person/person.component.html
+++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.html
@@ -53,8 +53,11 @@
-
-
+
+
diff --git a/themes/mantis/app/entity-groups/journal-entities/item-pages/journal/journal.component.html b/themes/mantis/app/entity-groups/journal-entities/item-pages/journal/journal.component.html
index 1b8a283da9..089511804e 100644
--- a/themes/mantis/app/entity-groups/journal-entities/item-pages/journal/journal.component.html
+++ b/themes/mantis/app/entity-groups/journal-entities/item-pages/journal/journal.component.html
@@ -61,7 +61,10 @@
{{"item.page.journal.search.title" | translate}}
-
-
+
+
diff --git a/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html b/themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html
similarity index 79%
rename from themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html
rename to themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html
index 15529a1bd5..ee78d9c653 100644
--- a/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.html
+++ b/themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.html
@@ -53,18 +53,6 @@
+
diff --git a/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.scss b/themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.scss
similarity index 86%
rename from themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.scss
rename to themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.scss
index 54651aede0..4a1d2516da 100644
--- a/themes/mantis/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.scss
+++ b/themes/mantis/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.scss
@@ -1,4 +1,4 @@
-@import 'src/app/entity-groups/research-entities/item-pages/orgunit/orgunit.component.scss';
+@import 'src/app/entity-groups/research-entities/item-pages/org-unit/org-unit.component.scss';
:host {
> * {
diff --git a/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html b/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html
index bb5cb1b787..1679f9354d 100644
--- a/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html
+++ b/themes/mantis/app/entity-groups/research-entities/item-pages/person/person.component.html
@@ -79,7 +79,10 @@
{{"item.page.person.search.title" | translate}}
-
-
+
+