diff --git a/src/app/constants.ts b/src/app/constants.ts index 5320bc977..2d0902528 100644 --- a/src/app/constants.ts +++ b/src/app/constants.ts @@ -48,9 +48,12 @@ export const ORCID_URI_REGEXP = // https://regex101.com/r/M1fqZi/1 export const URL_REGEXP = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#%[\]@!\$&'\(\)\*\+\\,;=.>< ]+$/i -// ISSN pattern using same validation as the backend -//https://regex101.com/r/NYZTYS/1 -export const ISSN_REGEXP = /\b\d{4}-?\d{3}[\dXx]\b/ +export const ISSN_PORTAL_URL = 'https://portal.issn.org/resource/ISSN/' +// Escape the URL characters for Regex (handling slashes and dots) +const escapedUrl = ISSN_PORTAL_URL.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +export const ISSN_REGEXP = new RegExp(`^(?:${escapedUrl})?\\d{4}-?\\d{3}[\\dXx]$`); + + /* PROTOCOL REQUIRED*/ // https://regex101.com/r/pSnDC7/1 export const URL_REGEXP_BACKEND = @@ -516,3 +519,23 @@ function isValidIsbn13(isbn: string): boolean { // The checksum must be perfectly divisible by 10. return checksum % 10 === 0 } + + +function normaliseIssn(issn: string): string { + if (issn) { + // 1. Remove the portal URL + issn = issn.replace(new RegExp(ISSN_PORTAL_URL, 'g'), '') + + // 2. Remove ALL non-alphanumeric characters (spaces, hyphens, dots, etc.) + issn = issn.replace(/[^a-zA-Z0-9]/g, '') + + // 3. Force 'X' to uppercase + issn = issn.replace(/x/g, 'X') + + // 4. Format as 0000-000X if valid length + if (issn.length === 8) { + return issn.substring(0, 4) + '-' + issn.substring(4, 8) + } + } + return '' + } \ No newline at end of file diff --git a/src/app/record/components/affiliation-stacks-groups/modals/modal-affiliations/modal-affiliations.component.ts b/src/app/record/components/affiliation-stacks-groups/modals/modal-affiliations/modal-affiliations.component.ts index 48d48d04e..e2e7f69ad 100644 --- a/src/app/record/components/affiliation-stacks-groups/modals/modal-affiliations/modal-affiliations.component.ts +++ b/src/app/record/components/affiliation-stacks-groups/modals/modal-affiliations/modal-affiliations.component.ts @@ -27,6 +27,7 @@ import { RecordCountriesService } from '../../../../../core/record-countries/rec import { first, map, switchMap, tap } from 'rxjs/operators' import { ISSN_REGEXP, + ISSN_PORTAL_URL, MAX_LENGTH_LESS_THAN_ONE_THOUSAND, MAX_LENGTH_LESS_THAN_TWO_THOUSAND, URL_REGEXP, @@ -63,7 +64,6 @@ export class ModalAffiliationsComponent implements OnInit, OnDestroy { issnLabel = $localize`:@@shared.link:Link` endDateLabel = $localize`:@@shared.endDate:End date` - ISSN_PORTAL_URL = 'https://portal.issn.org/resource/ISSN/' private _type: AffiliationType dateLabel: string @@ -394,13 +394,14 @@ export class ModalAffiliationsComponent implements OnInit, OnDestroy { const normalizedIssn = this.normaliseIssn( affiliationForm.get('issn')?.value?.trim() ) - const issnUrl = `${this.ISSN_PORTAL_URL}${normalizedIssn}` + const issnValue = affiliationForm.get('issn')?.value?.trim(); + const issnUrl = `${ISSN_PORTAL_URL}${normalizedIssn}` affiliationToSave.affiliationExternalIdentifiers = [ { errors: [], externalIdentifierId: { errors: [], - value: affiliationForm.get('issn')?.value?.trim(), + value: issnValue && issnValue.startsWith(ISSN_PORTAL_URL)?issnUrl:normalizedIssn, required: true, }, externalIdentifierType: { value: 'issn' }, @@ -744,7 +745,7 @@ export class ModalAffiliationsComponent implements OnInit, OnDestroy { normaliseIssn(issn: string) { if (issn) { // 1. Remove the portal URL - issn = issn.replace(new RegExp(this.ISSN_PORTAL_URL, 'g'), '') + issn = issn.replace(new RegExp(ISSN_PORTAL_URL, 'g'), '') // 2. Remove ALL non-alphanumeric characters (spaces, hyphens, dots, etc.) issn = issn.replace(/[^a-zA-Z0-9]/g, '') diff --git a/src/app/record/components/work-form/work-form/work-form.component.ts b/src/app/record/components/work-form/work-form/work-form.component.ts index cad06b7d1..17b84fd92 100644 --- a/src/app/record/components/work-form/work-form/work-form.component.ts +++ b/src/app/record/components/work-form/work-form/work-form.component.ts @@ -37,6 +37,7 @@ import { MAX_LENGTH_LESS_THAN_TWO_THOUSAND, URL_REGEXP, isValidISBN, + ISSN_REGEXP, } from '../../../../constants' import { Contributor, @@ -258,6 +259,17 @@ export class WorkFormComponent implements OnInit { } } + if (externalIdentifierType === 'issn') { + if (!ISSN_REGEXP.test(control.value)) { + control.markAsTouched() + return of({ + validFormat: true, + resolved: false, + attemptedResolution: false, + }) + } + } + return this._workService .validateWorkIdTypes(externalIdentifierType, control.value) .pipe(