diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.html b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.html index b07b9bac602..561fc8052da 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.html +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.html @@ -1,5 +1,10 @@
- - {{ identifier?.text }} - -
\ No newline at end of file +
+ + source-icon + + + {{ identifier?.text }} + +
+ diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.scss b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.scss index e69de29bb2d..f1496a808b2 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.scss +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.scss @@ -0,0 +1,20 @@ +.source-icon { + height: var(--ds-identifier-sybetype-icon-height); + min-height: 16px; + width: auto; +} + +.identifier-container-LEFT { + flex-direction: row; + .source-icon { + margin-right: 0.2em; + } +} + +.identifier-container-RIGHT { + flex-direction: row-reverse; + justify-content: flex-end; + .source-icon { + margin-left: 0.2em; + } +} diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.ts index 6a83e0c58ac..15a23c6c96e 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/identifier/identifier.component.ts @@ -1,13 +1,15 @@ +import { IdentifierSubtypesConfig, IdentifierSubtypesIconPositionEnum } from './../../../../../../../../config/identifier-subtypes-config.interface'; import { Component, Inject, OnInit } from '@angular/core'; import { FieldRenderingType, MetadataBoxFieldRendering } from '../metadata-box.decorator'; import { ResolverStrategyService } from '../../../../../../services/resolver-strategy.service'; -import { hasValue, isNotEmpty } from '../../../../../../../shared/empty.util'; +import { hasNoValue, hasValue, isNotEmpty } from '../../../../../../../shared/empty.util'; import { MetadataLinkValue } from '../../../../../../models/cris-layout-metadata-link-value.model'; import { RenderingTypeValueModelComponent } from '../rendering-type-value.model'; import { Item } from '../../../../../../../core/shared/item.model'; import { TranslateService } from '@ngx-translate/core'; import { LayoutField } from '../../../../../../../core/layout/models/box.model'; import { MetadataValue } from '../../../../../../../core/shared/metadata.models'; +import { environment } from 'src/environments/environment'; /** * This component renders the identifier metadata fields. @@ -37,6 +39,31 @@ export class IdentifierComponent extends RenderingTypeValueModelComponent implem */ target = '_blank'; + /** + * The identifier subtype configurations + */ + identifierSubtypeConfig: IdentifierSubtypesConfig[] = environment.identifierSubtypes; + + /** + * The icon to display for the identifier subtype + */ + subTypeIcon: string; + + /** + * The position of the icon relative to the identifier + */ + iconPosition: IdentifierSubtypesIconPositionEnum = IdentifierSubtypesIconPositionEnum.NONE; + + /** + * The link to navigate to when the icon is clicked + */ + iconLink = ''; + + /** + * The identifier subtype to render + */ + iconPositionEnum = IdentifierSubtypesIconPositionEnum; + constructor( @Inject('fieldProvider') public fieldProvider: LayoutField, @Inject('itemProvider') public itemProvider: Item, @@ -80,27 +107,82 @@ export class IdentifierComponent extends RenderingTypeValueModelComponent implem return identifier; } + /** + * Create a MetadataLinkValue object with the given href and text + * @param href the href value + * @param text the text value + * @returns MetadataLinkValue object + */ + private createMetadataLinkValue(href: string, text: string): MetadataLinkValue { + text = text.trim() !== '' ? text : href; + return { href, text }; + } + /** * Set href and text of the component based on urn - * and the given metadata value + * and the given metadata value. + * Is handling the case when the urn is configured in the default-app-config + * and the link is pre-configured. * @param metadataValue the metadata value * @param urn URN type (doi, hdl, mailto) */ composeLink(metadataValue: string, urn: string): MetadataLinkValue { + const subtypeValue = this.getIdentifierSubtypeValue(); + + if (hasValue(subtypeValue)) { + const href = this.validateLink(metadataValue) ? metadataValue : `${subtypeValue.link}/${metadataValue}`; + return this.createMetadataLinkValue(href, metadataValue); + } + let value = metadataValue; - const rep = urn + ':'; + const rep = `${urn}:`; if (metadataValue.startsWith(rep)) { value = metadataValue.replace(rep, ''); } const href = this.resolver.getBaseUrl(urn) + value; - const text = isNotEmpty(value) && value !== '' ? value : href; - return { - href, - text - }; + return this.createMetadataLinkValue(href, value); } ngOnInit(): void { this.identifier = this.getIdentifierFromValue(); + this.setIconDetails(); + } + + /** + * Sets the icon details based on the identifier subtype configuration. + * If the identifier subtype is not empty, it searches for the subtype with a matching name to the rendering subtype. + * If a matching subtype is found, it sets the icon position, subtype icon, and icon link based on the subtype's properties. + */ + private setIconDetails() { + const subtypeVal = this.getIdentifierSubtypeValue(); + if (hasNoValue(subtypeVal)) { + return; + } + this.iconPosition = subtypeVal.iconPosition; + this.subTypeIcon = subtypeVal.iconPosition !== IdentifierSubtypesIconPositionEnum.NONE ? subtypeVal?.icon : ''; + this.iconLink = subtypeVal?.link; + } + + /** + * Retrieves the value of the identifier subtype configuration based on the rendering subtype. + * @returns The identifier subtype configuration object. + */ + private getIdentifierSubtypeValue(): IdentifierSubtypesConfig { + if (isNotEmpty(this.identifierSubtypeConfig)) { + const subtypeVal = this.identifierSubtypeConfig.find((subtype) => subtype.name === this.renderingSubType); + return subtypeVal; + } + } + + /** + * Check if the given link is valid + * @param link the link to check + * @returns true if the link is valid, false otherwise + */ + private validateLink(link: string): boolean { + const urlRegex = /^(http|https):\/\/[^ "]+$/; + return urlRegex.test(link); } } + + diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 6ecd95e5c8c..5b21141695d 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -34,6 +34,7 @@ import { SearchResultConfig } from './search-result-config.interface'; import { MiradorConfig } from './mirador-config.interfaces'; import { LoaderConfig } from './loader-config.interfaces'; import { MetaTagsConfig } from './meta-tags.config'; +import { IdentifierSubtypesConfig } from './identifier-subtypes-config.interface'; import { DatadogRumConfig } from './datadog-rum-config.interfaces'; interface AppConfig extends Config { @@ -75,6 +76,7 @@ interface AppConfig extends Config { mirador: MiradorConfig; loader: LoaderConfig; metaTags: MetaTagsConfig; + identifierSubtypes: IdentifierSubtypesConfig[]; datadogRum?: DatadogRumConfig; } diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 2396876f2ea..8ad8c884b71 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -37,6 +37,7 @@ import { SearchResultConfig } from './search-result-config.interface'; import { MiradorConfig } from './mirador-config.interfaces'; import { LoaderConfig } from './loader-config.interfaces'; import { MetaTagsConfig } from './meta-tags.config'; +import { IdentifierSubtypesConfig, IdentifierSubtypesIconPositionEnum } from './identifier-subtypes-config.interface'; import { DatadogRumConfig } from './datadog-rum-config.interfaces'; export class DefaultAppConfig implements AppConfig { @@ -806,6 +807,14 @@ export class DefaultAppConfig implements AppConfig { 'DSpace-CRIS enables secure, integrated and interoperable research information and data management – in a single solution.' }; + identifierSubtypes: IdentifierSubtypesConfig[] = [ + { + name: 'ror', + icon: 'assets/images/ror.logo.icon.svg', + iconPosition: IdentifierSubtypesIconPositionEnum.LEFT, + link: 'https://ror.org' + } + ]; datadogRum: DatadogRumConfig = { clientToken: undefined, applicationId: undefined, diff --git a/src/config/identifier-subtypes-config.interface.ts b/src/config/identifier-subtypes-config.interface.ts new file mode 100644 index 00000000000..370fd2a1bf2 --- /dev/null +++ b/src/config/identifier-subtypes-config.interface.ts @@ -0,0 +1,15 @@ +/** + * Represents the configuration for identifier subtypes. + */ +export interface IdentifierSubtypesConfig { + name: string; // The name of the identifier subtype + icon: string; // The icon to display for the identifier subtype + iconPosition: IdentifierSubtypesIconPositionEnum; // The position of the icon relative to the identifier + link: string; // The link to navigate to when the icon is clicked +} + +export enum IdentifierSubtypesIconPositionEnum { + NONE = 'NONE', + LEFT = 'LEFT', + RIGHT = 'RIGHT', +} diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index db9fbfbbb8f..364590dab37 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -3,6 +3,7 @@ import { BuildConfig } from 'src/config/build-config.interface'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { AdvancedAttachmentElementType } from '../config/advanced-attachment-rendering.config'; +import { IdentifierSubtypesIconPositionEnum } from 'src/config/identifier-subtypes-config.interface'; export const environment: BuildConfig = { production: false, @@ -588,5 +589,14 @@ export const environment: BuildConfig = { metaTags: { defaultLogo: '/assets/images/dspace-cris-logo.png', defaultDescription: 'DSpace is the most widely used repository software with more than 3000 installations around the world. It is free, open source and completely customisable to fit the needs of any organisation.' - } + }, + + identifierSubtypes: [ + { + name: 'ror', + icon: 'assets/images/ror.logo.icon.svg', + iconPosition: IdentifierSubtypesIconPositionEnum.LEFT, + link: 'https://ror.org' + } + ] }; diff --git a/src/styles/_custom_variables.scss b/src/styles/_custom_variables.scss index 9e6d630739f..bb41e7d7759 100644 --- a/src/styles/_custom_variables.scss +++ b/src/styles/_custom_variables.scss @@ -145,4 +145,6 @@ --ds-comcol-logo-max-width: 500px; --ds-comcol-logo-max-height: 500px; + + --ds-identifier-sybetype-icon-height: 24px; }