From 8d971412fcdb07d0a7e3110a16c5ebfc29978ecf Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Mon, 20 May 2024 18:25:30 +0200 Subject: [PATCH 1/3] [DSC-1520] subtype identfier configuration --- .../identifier/identifier.component.html | 13 +++-- .../identifier/identifier.component.scss | 20 +++++++ .../identifier/identifier.component.ts | 52 ++++++++++++++++++- src/config/app-config.interface.ts | 2 + src/config/default-app-config.ts | 10 ++++ .../identifier-subtypes-config.interface.ts | 15 ++++++ src/styles/_custom_variables.scss | 2 + 7 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 src/config/identifier-subtypes-config.interface.ts 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..dc1e0aea3aa 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, @@ -102,5 +129,28 @@ export class IdentifierComponent extends RenderingTypeValueModelComponent implem ngOnInit(): void { this.identifier = this.getIdentifierFromValue(); + this.getSubtypeValue(); + } + + /** + * Retrieves the subtype value for the identifier component. + * 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 getSubtypeValue() { + if (isNotEmpty(this.identifierSubtypeConfig)) { + const subtypeVal = this.identifierSubtypeConfig.find((subtype) => subtype.name === this.renderingSubType); + if (hasNoValue(subtypeVal)) { + return; + } + + this.iconPosition = subtypeVal.iconPosition; + this.subTypeIcon = subtypeVal.iconPosition !== IdentifierSubtypesIconPositionEnum.NONE ? subtypeVal?.icon : ''; + this.iconLink = subtypeVal?.link; + } } + + // TODO: Check the internal | external link + // TODO: UNIT TESTs + } diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 29e52de242f..7c17c106c10 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'; interface AppConfig extends Config { ui: UIServerConfig; @@ -74,6 +75,7 @@ interface AppConfig extends Config { mirador: MiradorConfig; loader: LoaderConfig; metaTags: MetaTagsConfig; + identifierSubtypes: IdentifierSubtypesConfig[]; } /** diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 87e0d915f7f..bfc4b0786c2 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'; export class DefaultAppConfig implements AppConfig { production = false; @@ -803,4 +804,13 @@ export class DefaultAppConfig implements AppConfig { 'It is compliant with and supports key international standards, facilitating interoperability and data transfer.\n' + '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' + } + ]; } 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/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; } From f60b64132be0d8d56844c9885699a656b280f93f Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 21 May 2024 08:53:58 +0200 Subject: [PATCH 2/3] [DSC-1520] fix --- src/environments/environment.test.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 89c9e1e4122..c55db9ca325 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, @@ -587,5 +588,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' + } + ] }; From d7b4e8a24d858990e7588755d2d81435fe27b5c0 Mon Sep 17 00:00:00 2001 From: Alisa Ismailati Date: Tue, 21 May 2024 10:46:18 +0200 Subject: [PATCH 3/3] [DSC-1520] handle subtype identifier link --- .../identifier/identifier.component.ts | 72 +++++++++++++------ 1 file changed, 52 insertions(+), 20 deletions(-) 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 dc1e0aea3aa..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 @@ -107,50 +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.getSubtypeValue(); + this.setIconDetails(); } /** - * Retrieves the subtype value for the identifier component. + * 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 getSubtypeValue() { + 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); - if (hasNoValue(subtypeVal)) { - return; - } - - this.iconPosition = subtypeVal.iconPosition; - this.subTypeIcon = subtypeVal.iconPosition !== IdentifierSubtypesIconPositionEnum.NONE ? subtypeVal?.icon : ''; - this.iconLink = subtypeVal?.link; + return subtypeVal; } } - // TODO: Check the internal | external link - // TODO: UNIT TESTs - + /** + * 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); + } } + +