From 7657918b9fd93f31a9d1565998681e5830189dc1 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 5 Feb 2026 12:40:15 +0100 Subject: [PATCH 1/7] [DURACOM-445] add new property for authority relations --- config/config.example.yml | 3 ++- src/config/default-app-config.ts | 3 +++ src/config/item-config.interface.ts | 2 ++ src/environments/environment.test.ts | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/config/config.example.yml b/config/config.example.yml index 6fdfa94add5..802b925fb90 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -393,7 +393,8 @@ item: pageSize: 5 # Show the bitstream access status label on the item page showAccessStatuses: false - + # Enable authority based relations in item page + showAuthorithyRelations: false # Community Page Config community: # Default tab to be shown when browsing a Community. Valid values are: comcols, search, or browse_ diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 4840c93e1ba..e0ef3192297 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -346,6 +346,9 @@ export class DefaultAppConfig implements AppConfig { // Show the bitstream access status label showAccessStatuses: false, }, + // If true, the search result in item page will display relations based on authority. + // If false,the search result in item page will display default DSpace relations. + showAuthorithyRelations: false, }; // Community Page Config diff --git a/src/config/item-config.interface.ts b/src/config/item-config.interface.ts index 1b629bf8726..659a9819bb0 100644 --- a/src/config/item-config.interface.ts +++ b/src/config/item-config.interface.ts @@ -15,4 +15,6 @@ export interface ItemConfig extends Config { // Show the bitstream access status label showAccessStatuses: boolean; } + + showAuthorithyRelations: boolean; } diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index a93c1fe9389..3aac08d7cbe 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -290,6 +290,7 @@ export const environment: BuildConfig = { // Show the bitstream access status label showAccessStatuses: false, }, + showAuthorithyRelations: false, }, community: { defaultBrowseTab: 'search', From 5ac23e6ea5ba923effadbbc89f1a1061fb14e716 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 12 Feb 2026 17:03:23 +0100 Subject: [PATCH 2/7] [DURACOM-445] add authority based search relations component --- .../item-pages/person/person.component.html | 13 +++- .../item-pages/person/person.component.ts | 2 + .../item-types/shared/item.component.ts | 6 ++ ...ity-related-entities-search.component.html | 10 +++ ...-related-entities-search.component.spec.ts | 77 +++++++++++++++++++ ...ority-related-entities-search.component.ts | 42 ++++++++++ src/app/shared/listable.module.ts | 2 + src/config/default-app-config.ts | 2 +- .../item-pages/person/person.component.ts | 2 + 9 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.html create mode 100644 src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.spec.ts create mode 100644 src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.ts 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 e64cffd7bb8..532c64ccdd4 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 @@ -58,12 +58,19 @@
- + } @else { + - + + }
diff --git a/src/app/entity-groups/research-entities/item-pages/person/person.component.ts b/src/app/entity-groups/research-entities/item-pages/person/person.component.ts index 450350a6dab..bb8d79ca138 100644 --- a/src/app/entity-groups/research-entities/item-pages/person/person.component.ts +++ b/src/app/entity-groups/research-entities/item-pages/person/person.component.ts @@ -7,6 +7,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { GenericItemPageFieldComponent } from '../../../../item-page/simple/field-components/specific-field/generic/generic-item-page-field.component'; import { ThemedItemPageTitleFieldComponent } from '../../../../item-page/simple/field-components/specific-field/title/themed-item-page-field.component'; import { ItemComponent } from '../../../../item-page/simple/item-types/shared/item.component'; +import { AuthorityRelatedEntitiesSearchComponent } from '../../../../item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component'; import { TabbedRelatedEntitiesSearchComponent } from '../../../../item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component'; import { RelatedItemsComponent } from '../../../../item-page/simple/related-items/related-items-component'; import { DsoEditMenuComponent } from '../../../../shared/dso-page/dso-edit-menu/dso-edit-menu.component'; @@ -22,6 +23,7 @@ import { ThemedThumbnailComponent } from '../../../../thumbnail/themed-thumbnail templateUrl: './person.component.html', imports: [ AsyncPipe, + AuthorityRelatedEntitiesSearchComponent, DsoEditMenuComponent, GenericItemPageFieldComponent, MetadataFieldWrapperComponent, diff --git a/src/app/item-page/simple/item-types/shared/item.component.ts b/src/app/item-page/simple/item-types/shared/item.component.ts index 2c4c50eb532..965a18ef199 100644 --- a/src/app/item-page/simple/item-types/shared/item.component.ts +++ b/src/app/item-page/simple/item-types/shared/item.component.ts @@ -79,10 +79,16 @@ export class ItemComponent implements OnInit { */ geospatialItemPageFieldsEnabled = false; + /** + * Flag to check whether to use the default relations or the authority based ones + */ + areAuthorityRelationsEnabled: boolean; + constructor(protected routeService: RouteService, protected router: Router) { this.mediaViewer = environment.mediaViewer; this.geospatialItemPageFieldsEnabled = environment.geospatialMapViewer.enableItemPageFields; + this.areAuthorityRelationsEnabled = environment.item.showAuthorithyRelations; } /** diff --git a/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.html b/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.html new file mode 100644 index 00000000000..df8269a26ac --- /dev/null +++ b/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.html @@ -0,0 +1,10 @@ +@if (configuration) { + +} + + diff --git a/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.spec.ts b/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.spec.ts new file mode 100644 index 00000000000..30be4c9d68b --- /dev/null +++ b/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.spec.ts @@ -0,0 +1,77 @@ +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { + ComponentFixture, + TestBed, + waitForAsync, +} from '@angular/core/testing'; +import { Item } from '@dspace/core/shared/item.model'; +import { TranslateModule } from '@ngx-translate/core'; + +import { ThemedConfigurationSearchPageComponent } from '../../../../search-page/themed-configuration-search-page.component'; +import { AuthorityRelatedEntitiesSearchComponent } from './authority-related-entities-search.component'; + + +describe('AuthorityRelatedEntitiesSearchComponent', () => { + let component: AuthorityRelatedEntitiesSearchComponent; + let fixture: ComponentFixture; + + const mockItem = { + id: 'test-id-123', + } as Item; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), AuthorityRelatedEntitiesSearchComponent], + providers: [], + schemas: [NO_ERRORS_SCHEMA], + }) + .overrideComponent(AuthorityRelatedEntitiesSearchComponent, { + remove: { + imports: [ + ThemedConfigurationSearchPageComponent, + ], + }, + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AuthorityRelatedEntitiesSearchComponent); + component = fixture.componentInstance; + component.item = mockItem; + component.configuration = 'relations-configuration'; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should set searchFilter on init', () => { + component.item = mockItem; + component.ngOnInit(); + + expect(component.searchFilter).toBe('scope=test-id-123'); + }); + + it('should render configuration search page when configuration is provided', () => { + component.item = mockItem; + component.configuration = 'test-config'; + + fixture.detectChanges(); + + const searchPage = fixture.nativeElement.querySelector('ds-configuration-search-page'); + expect(searchPage).toBeTruthy(); + }); + + it('should NOT render configuration search page when configuration is missing', () => { + component.item = mockItem; + component.configuration = undefined; + + fixture.detectChanges(); + + const searchPage = fixture.nativeElement.querySelector('ds-configuration-search-page'); + expect(searchPage).toBeFalsy(); + }); + +}); diff --git a/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.ts b/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.ts new file mode 100644 index 00000000000..abf9c786b15 --- /dev/null +++ b/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.ts @@ -0,0 +1,42 @@ +import { + Component, + Input, + OnInit, +} from '@angular/core'; +import { Item } from '@dspace/core/shared/item.model'; + +import { ThemedConfigurationSearchPageComponent } from '../../../../search-page/themed-configuration-search-page.component'; + +@Component({ + selector: 'ds-authority-related-entities-search', + templateUrl: './authority-related-entities-search.component.html', + imports: [ + ThemedConfigurationSearchPageComponent, + ], +}) +/** + * A component to show related items as search results, based on authority value + */ +export class AuthorityRelatedEntitiesSearchComponent implements OnInit { + /** + * Filter used for set scope in discovery invocation + */ + searchFilter: string; + /** + * Name of configuration for this box + */ + @Input() configuration: string; + /** + * flag for enable/disable search bar + */ + @Input() searchEnabled = true; + + + @Input() item: Item; + + + ngOnInit() { + this.searchFilter = `scope=${this.item.id}`; + } + +} diff --git a/src/app/shared/listable.module.ts b/src/app/shared/listable.module.ts index cf417129e6b..c0eb9344e78 100644 --- a/src/app/shared/listable.module.ts +++ b/src/app/shared/listable.module.ts @@ -71,6 +71,7 @@ import { ItemPageUriFieldComponent } from '../item-page/simple/field-components/ import { PublicationComponent } from '../item-page/simple/item-types/publication/publication.component'; import { UntypedItemComponent } from '../item-page/simple/item-types/untyped-item/untyped-item.component'; import { ThemedMetadataRepresentationListComponent } from '../item-page/simple/metadata-representation-list/themed-metadata-representation-list.component'; +import { AuthorityRelatedEntitiesSearchComponent } from '../item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component'; import { TabbedRelatedEntitiesSearchComponent } from '../item-page/simple/related-entities/tabbed-related-entities-search/tabbed-related-entities-search.component'; import { RelatedItemsComponent } from '../item-page/simple/related-items/related-items-component'; import { ThemedThumbnailComponent } from '../thumbnail/themed-thumbnail.component'; @@ -243,6 +244,7 @@ const ENTRY_COMPONENTS = [ ItemActionsComponent, PersonInputSuggestionsComponent, TabbedRelatedEntitiesSearchComponent, + AuthorityRelatedEntitiesSearchComponent, WorkspaceItemAdminWorkflowActionsComponent, WorkflowItemAdminWorkflowActionsComponent, FormsModule, diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index e0ef3192297..8f90c1b8b7d 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -348,7 +348,7 @@ export class DefaultAppConfig implements AppConfig { }, // If true, the search result in item page will display relations based on authority. // If false,the search result in item page will display default DSpace relations. - showAuthorithyRelations: false, + showAuthorithyRelations: true, }; // Community Page Config diff --git a/src/themes/custom/app/entity-groups/research-entities/item-pages/person/person.component.ts b/src/themes/custom/app/entity-groups/research-entities/item-pages/person/person.component.ts index b7e21671c36..5bb6ae3a686 100644 --- a/src/themes/custom/app/entity-groups/research-entities/item-pages/person/person.component.ts +++ b/src/themes/custom/app/entity-groups/research-entities/item-pages/person/person.component.ts @@ -14,6 +14,7 @@ import { ThemedResultsBackButtonComponent } from 'src/app/shared/results-back-bu import { ThemedThumbnailComponent } from 'src/app/thumbnail/themed-thumbnail.component'; import { PersonComponent as BaseComponent } from '../../../../../../../app/entity-groups/research-entities/item-pages/person/person.component'; +import { AuthorityRelatedEntitiesSearchComponent } from '../../../../../../../app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component'; import { listableObjectComponent } from '../../../../../../../app/shared/object-collection/shared/listable-object/listable-object.decorator'; @listableObjectComponent('Person', ViewMode.StandalonePage, Context.Any, 'custom') @@ -25,6 +26,7 @@ import { listableObjectComponent } from '../../../../../../../app/shared/object- templateUrl: '../../../../../../../app/entity-groups/research-entities/item-pages/person/person.component.html', imports: [ AsyncPipe, + AuthorityRelatedEntitiesSearchComponent, DsoEditMenuComponent, GenericItemPageFieldComponent, MetadataFieldWrapperComponent, From 23ecd1ed11ee02eb688266163b9e7b65d7e9f692 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Fri, 13 Feb 2026 16:22:11 +0100 Subject: [PATCH 3/7] [DURACOM-445] add nav tabs to authority-related-entities-search, fix old module import --- .../item-pages/person/person.component.html | 2 +- ...ity-related-entities-search.component.html | 45 ++++++++++++++++--- ...-related-entities-search.component.spec.ts | 30 +++++++++++-- ...ority-related-entities-search.component.ts | 32 ++++++++----- ...bed-related-entities-search.component.html | 2 +- ...abbed-related-entities-search.component.ts | 18 ++++++-- src/assets/i18n/en.json5 | 8 ++++ 7 files changed, 109 insertions(+), 28 deletions(-) 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 532c64ccdd4..efb92a35621 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 @@ -61,7 +61,7 @@ @if (areAuthorityRelationsEnabled) { } @else { +@if (configurations.length > 1) { + + +
} +@if (configurations.length === 1) { + + +} diff --git a/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.spec.ts b/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.spec.ts index 30be4c9d68b..acb3ecde6e9 100644 --- a/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.spec.ts +++ b/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.spec.ts @@ -4,8 +4,14 @@ import { TestBed, waitForAsync, } from '@angular/core/testing'; +import { + ActivatedRoute, + Router, +} from '@angular/router'; import { Item } from '@dspace/core/shared/item.model'; +import { RouterMock } from '@dspace/core/testing/router.mock'; import { TranslateModule } from '@ngx-translate/core'; +import { of } from 'rxjs'; import { ThemedConfigurationSearchPageComponent } from '../../../../search-page/themed-configuration-search-page.component'; import { AuthorityRelatedEntitiesSearchComponent } from './authority-related-entities-search.component'; @@ -18,11 +24,27 @@ describe('AuthorityRelatedEntitiesSearchComponent', () => { const mockItem = { id: 'test-id-123', } as Item; + const router = new RouterMock(); + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), AuthorityRelatedEntitiesSearchComponent], - providers: [], + providers: [ + { + provide: ActivatedRoute, + useValue: { + queryParams: of({ tab: 'relations-configuration' }), + snapshot: { + queryParams: { + scope: 'collection-uuid', + query: 'test', + }, + }, + }, + }, + { provide: Router, useValue: router }, + ], schemas: [NO_ERRORS_SCHEMA], }) .overrideComponent(AuthorityRelatedEntitiesSearchComponent, { @@ -39,7 +61,7 @@ describe('AuthorityRelatedEntitiesSearchComponent', () => { fixture = TestBed.createComponent(AuthorityRelatedEntitiesSearchComponent); component = fixture.componentInstance; component.item = mockItem; - component.configuration = 'relations-configuration'; + component.configurations = ['relations-configuration']; fixture.detectChanges(); }); @@ -56,7 +78,7 @@ describe('AuthorityRelatedEntitiesSearchComponent', () => { it('should render configuration search page when configuration is provided', () => { component.item = mockItem; - component.configuration = 'test-config'; + component.configurations = ['test-config']; fixture.detectChanges(); @@ -66,7 +88,7 @@ describe('AuthorityRelatedEntitiesSearchComponent', () => { it('should NOT render configuration search page when configuration is missing', () => { component.item = mockItem; - component.configuration = undefined; + component.configurations = []; fixture.detectChanges(); diff --git a/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.ts b/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.ts index abf9c786b15..9450edb9ba3 100644 --- a/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.ts +++ b/src/app/item-page/simple/related-entities/authority-related-entities-search/authority-related-entities-search.component.ts @@ -1,42 +1,52 @@ +import { AsyncPipe } from '@angular/common'; import { Component, Input, OnInit, } from '@angular/core'; -import { Item } from '@dspace/core/shared/item.model'; +import { + NgbNav, + NgbNavContent, + NgbNavItem, + NgbNavLink, + NgbNavOutlet, +} from '@ng-bootstrap/ng-bootstrap'; +import { TranslateModule } from '@ngx-translate/core'; import { ThemedConfigurationSearchPageComponent } from '../../../../search-page/themed-configuration-search-page.component'; +import { TabbedRelatedEntitiesSearchComponent } from '../tabbed-related-entities-search/tabbed-related-entities-search.component'; @Component({ selector: 'ds-authority-related-entities-search', templateUrl: './authority-related-entities-search.component.html', imports: [ + AsyncPipe, + NgbNav, + NgbNavContent, + NgbNavItem, + NgbNavLink, + NgbNavOutlet, ThemedConfigurationSearchPageComponent, + TranslateModule, ], }) /** * A component to show related items as search results, based on authority value */ -export class AuthorityRelatedEntitiesSearchComponent implements OnInit { +export class AuthorityRelatedEntitiesSearchComponent extends TabbedRelatedEntitiesSearchComponent implements OnInit { /** * Filter used for set scope in discovery invocation */ searchFilter: string; /** - * Name of configuration for this box - */ - @Input() configuration: string; - /** - * flag for enable/disable search bar + * Discovery configurations for search page */ - @Input() searchEnabled = true; + @Input() configurations: string[] = []; - @Input() item: Item; - ngOnInit() { + super.ngOnInit(); this.searchFilter = `scope=${this.item.id}`; } - } 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 index 147650b11aa..901077dbfb1 100644 --- 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 @@ -1,6 +1,6 @@ @if (relationTypes.length > 1) {