From 96b7a40d7c021be5e92610b1f34478f1384cf22d Mon Sep 17 00:00:00 2001 From: Felipe Mosqueira Date: Tue, 28 May 2024 11:59:44 -0300 Subject: [PATCH 1/5] =?UTF-8?q?10611=20extiendo=20la=20clase=20head-tag.se?= =?UTF-8?q?rvice.ts,=20modifico=20algunos=20m=C3=A9todos=20y=20agrego=20lo?= =?UTF-8?q?s=20que=20faltaban;=20dejo=20preparado=20el=20m=C3=A9todo=20par?= =?UTF-8?q?a=20los=20links=20a=20PDFs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app-routing-paths.ts | 7 + .../core/metadata/head-tag-sedici.service.ts | 491 ++++++++++++++++++ src/app/core/metadata/head-tag.service.ts | 2 +- src/app/init.service.ts | 7 +- src/modules/app/browser-init.service.ts | 5 +- src/modules/app/server-init.service.ts | 5 +- 6 files changed, 509 insertions(+), 8 deletions(-) create mode 100644 src/app/core/metadata/head-tag-sedici.service.ts diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index 7d202f16e9c..5e0d22fdcd1 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -1,3 +1,4 @@ +import { Observable, from } from 'rxjs'; import { getCollectionPageRoute } from './collection-page/collection-page-routing-paths'; import { getCommunityPageRoute } from './community-page/community-page-routing-paths'; import { Collection } from './core/shared/collection.model'; @@ -22,6 +23,12 @@ export function getBitstreamModuleRoute() { return `/${BITSTREAM_MODULE_PATH}`; } +export function getBitstreamRoute(item, bitstream): Observable { + return from(fetch(bitstream._links.self.href) + .then(response => response.json()) + .then(data => `/${LEGACY_BITSTREAM_MODULE_PATH}/handle/${item.handle}/${bitstream.name}?sequence=${data.sequenceId}`)); +} + export function getBitstreamDownloadRoute(bitstream): string { return new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString(); } diff --git a/src/app/core/metadata/head-tag-sedici.service.ts b/src/app/core/metadata/head-tag-sedici.service.ts new file mode 100644 index 00000000000..a4b0df0aa8e --- /dev/null +++ b/src/app/core/metadata/head-tag-sedici.service.ts @@ -0,0 +1,491 @@ +import { + Inject, + Injectable, +} from '@angular/core'; +import { + Meta, + MetaDefinition, + Title, +} from '@angular/platform-browser'; +import { + Router, +} from '@angular/router'; +import { + Store, +} from '@ngrx/store'; +import { TranslateService } from '@ngx-translate/core'; + +import { + APP_CONFIG, + AppConfig, +} from '../../../config/app-config.interface'; +import { DSONameService } from '../breadcrumbs/dso-name.service'; +import { CoreState } from '../core-state.model'; +import { BundleDataService } from '../data/bundle-data.service'; +import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service'; +import { RootDataService } from '../data/root-data.service'; +import { HardRedirectService } from '../services/hard-redirect.service'; +import { HeadTagService } from './head-tag.service'; +import { Item } from '../shared/item.model'; +import { followLink } from 'src/app/shared/utils/follow-link-config.model'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../shared/operators'; +import { Bundle } from '../shared/bundle.model'; +import { map, switchMap, take, of as observableOf, concat as observableConcat, mergeMap, filter, EMPTY, Observable } from 'rxjs'; +import { Bitstream } from '../shared/bitstream.model'; +import { RemoteData } from '../data/remote-data'; +import { hasValue, isNotEmpty } from 'src/app/shared/empty.util'; +import { getDownloadableBitstream } from '../shared/bitstream.operators'; +import { getBitstreamRoute } from 'src/app/app-routing-paths'; +import { PaginatedList } from '../data/paginated-list.model'; +import { URLCombiner } from '../url-combiner/url-combiner'; +import { BitstreamFormat } from '../shared/bitstream-format.model'; +import { MetadataValue } from '../shared/metadata.models'; +import { LinkDefinition } from '../services/link-head.service'; +import { LinkHeadService } from '../services/link-head.service'; + +@Injectable({ + providedIn: 'root', +}) +export class HeadTagSEDICIService extends HeadTagService { + + constructor( + protected router: Router, + protected translate: TranslateService, + protected meta: Meta, + protected title: Title, + protected dsoNameService: DSONameService, + protected bundleDataService: BundleDataService, + protected rootService: RootDataService, + protected store: Store, + protected hardRedirectService: HardRedirectService, + @Inject(APP_CONFIG) protected appConfig: AppConfig, + protected authorizationService: AuthorizationDataService, + protected linkService: LinkHeadService + ) { + super(router, translate, meta, title, dsoNameService, bundleDataService, rootService, store, hardRedirectService, appConfig, authorizationService); + } + + protected setDSOMetaTags(): void { + + let link: LinkDefinition = { rel:"schema.DCTERMS", href:"http://purl.org/dc/terms/" }; + this.linkService.addTag(link); + link = { rel:"schema.DC", href:"http://purl.org/dc/elements/1.1/" }; + this.linkService.addTag(link); + + this.setIdentifierURITags(); + this.setIdentifierTags(); + this.setTitleTags(); + this.setTitleAlternativeTags(); + this.setCreatorTags(); + this.setIssuedTags(); + this.setAvailableTags(); + this.setCreatedTags(); + this.setAbstractTags(); + this.setDescriptionTags(); + this.setFormatTags(); + this.setExtentTags(); + this.setLanguageTags(); + this.setSubjectTags(); + this.setTypeTags(); + this.setLicenseTags(); + this.setRightsTags(); + this.setProvenanceTags(); + this.setContributorTags(); + this.setIsVersionOfTags(); + this.setPublisherTags(); + this.setDateTags(); + this.setRelationTags(); + this.setSourceTags(); + this.setIsPartOfTags(); + + super.setDSOMetaTags(); + if (this.isDissertation()) { + this.setCitationDissertationInstitutionTag(); + } + if (this.isTechReport()) { + this.setCitationTechnicalReportInstitutionTag(); + } + this.setCitationDOITag(); + this.setCitationJournalTitleTag(); + this.setCitationVolumeAndIssueTag(); + this.setCitationConferenceTitleTag(); + this.setCitationOnlineDateTag(); + + /** + * Consideraciones: + this.setCitationPdfUrlTag(); // PARCHE PDF + + this.setTitleTag(); y this.setDescriptionTag(); // NO DEBERÍAN LLAMARSE + */ + } + + // CITATION TAGS + + /** + * Add to the + */ + protected setCitationTitleTag(): void { + const value = this.getMetaTagValue('dc.title'); + this.addMetaTag('citation_title', value); + } + + protected setCitationAuthorTags(): void { + const values: string[] = this.getMetaTagValues(['sedici.creator.person', 'sedici.creator.corporate', 'sedici.contributor.compiler', 'sedici.creator.interprete', 'sedici.contributor.editor']); + this.addMetaTags('citation_author', values); + } + + protected setCitationPublicationDateTag(): void { + const value = this.getFirstMetaTagValue(['dc.date.issued', 'sedici.date.exposure', 'dc.date.created']); + this.addMetaTag('citation_publication_date', value); + } + + protected setCitationISSNTag(): void { + const value = this.getMetaTagValue('sedici.identifier.issn'); + this.addMetaTag('citation_issn', value); + } + + protected setCitationISBNTag(): void { + const value = this.getMetaTagValue('sedici.identifier.isbn'); + this.addMetaTag('citation_isbn', value); + } + + /** + * Add to the + */ + protected setCitationDissertationInstitutionTag(): void { + const value = this.getMetaTagValue('thesis.degree.grantor'); + this.addMetaTag('citation_dissertation_institution', value); + } + + /** + * Add to the + */ + protected setCitationTechnicalReportInstitutionTag(): void { + const value = this.getFirstMetaTagValue(['dc.publisher', 'mods.originInfo.place']); + this.addMetaTag('citation_technical_report_institution', value); + } + + /** + * Add dc.publisher to the . The tag name depends on the item type. + */ + protected setCitationPublisherTag(): void { + const value = this.getMetaTagValue('dc.publisher'); + this.addMetaTag('citation_publisher', value); + } + + protected setCitationKeywordsTag(): void { + const value = this.getMultipleMetaTagValuesAndCombine(['dc.subject', 'sedici.subject.materias']); + this.addMetaTag('citation_keywords', value); + } + + // PARCHE PDF + // setCitationPdfUrlTag(): void { + // if (this.currentObject.value instanceof Item) { + // const item = this.currentObject.value as Item; + + // // Retrieve the ORIGINAL bundle for the item + // this.bundleDataService.findByItemAndName( + // item, + // 'ORIGINAL', + // true, + // true, + // followLink('primaryBitstream'), + // followLink('bitstreams', { + // findListOptions: { + // // limit the number of bitstreams used to find the citation pdf url to the number + // // shown by default on an item page + // elementsPerPage: this.appConfig.item.bitstream.pageSize, + // }, + // }, followLink('format')), + // ).pipe( + // getFirstSucceededRemoteDataPayload(), + // switchMap((bundle: Bundle) => + // // First try the primary bitstream + // bundle.primaryBitstream.pipe( + // getFirstCompletedRemoteData(), + // map((rd: RemoteData) => { + // if (hasValue(rd.payload)) { + // return rd.payload; + // } else { + // return null; + // } + // }), + // getDownloadableBitstream(this.authorizationService), + // // return the bundle as well so we can use it again if there's no primary bitstream + // map((bitstream: Bitstream) => [bundle, bitstream]), + // ), + // ), + // switchMap(([bundle, primaryBitstream]: [Bundle, Bitstream]) => { + // if (hasValue(primaryBitstream)) { + // // If there was a downloadable primary bitstream, emit its link + // return getBitstreamRoute(item, primaryBitstream); + // } else { + // // Otherwise consider the regular bitstreams in the bundle + // return bundle.bitstreams.pipe( + // getFirstCompletedRemoteData(), + // switchMap((bitstreamRd: RemoteData>) => { + // if (hasValue(bitstreamRd.payload) && bitstreamRd.payload.totalElements === 1) { + // // If there's only one bitstream in the bundle, emit its link if its downloadable + // return this.getBitLinkIfDownloadable(bitstreamRd.payload.page[0], bitstreamRd); + // } else { + // // Otherwise check all bitstreams to see if one matches the format whitelist + // return this.getFirstAllowedFormatBitstreamLink(bitstreamRd); + // } + // }), + // ); + // } + // }), + // take(1), + // ).subscribe((link: string) => { + // // Use the found link to set the tag + // this.addMetaTag( + // 'citation_pdf_url', + // new URLCombiner(this.hardRedirectService.getCurrentOrigin(), link).toString(), + // ); + // }); + // } + // } + + // getBitLinkIfDownloadable(bitstream: Bitstream, bitstreamRd: RemoteData>): Observable { + // return observableOf(bitstream).pipe( + // getDownloadableBitstream(this.authorizationService), + // switchMap((bit: Bitstream) => { + // if (hasValue(bit)) { + // const item = this.currentObject.value as Item; + // return getBitstreamRoute(item, bit); + // } else { + // // Otherwise check all bitstreams to see if one matches the format whitelist + // return this.getFirstAllowedFormatBitstreamLink(bitstreamRd); + // } + // }), + // ); + // } + + // protected getFirstAllowedFormatBitstreamLink(bitstreamRd: RemoteData>): Observable { + // const item = this.currentObject.value as Item; + // if (hasValue(bitstreamRd.payload) && isNotEmpty(bitstreamRd.payload.page)) { + // // Retrieve the formats of all bitstreams in the page sequentially + // return observableConcat( + // ...bitstreamRd.payload.page.map((bitstream: Bitstream) => bitstream.format.pipe( + // getFirstSucceededRemoteDataPayload(), + // // Keep the original bitstream, because it, not the format, is what we'll need + // // for the link at the end + // map((format: BitstreamFormat) => [bitstream, format]), + // )), + // ).pipe( + // // Verify that the bitstream is downloadable + // mergeMap(([bitstream, format]: [Bitstream, BitstreamFormat]) => observableOf(bitstream).pipe( + // getDownloadableBitstream(this.authorizationService), + // map((bit: Bitstream) => [bit, format]), + // )), + // // Filter out only pairs with whitelisted formats and non-null bitstreams, null from download check + // filter(([bitstream, format]: [Bitstream, BitstreamFormat]) => + // hasValue(format) && hasValue(bitstream) && this.CITATION_PDF_URL_MIMETYPES.includes(format.mimetype)), + // // We only need 1 + // take(1), + // // Emit the link of the match + // // tap((v) => console.log('result', v)), + // mergeMap(([bitstream ]: [Bitstream, BitstreamFormat]) => getBitstreamRoute(item, bitstream)), + // ); + // } else { + // return EMPTY; + // } + // } + + /** + * Add to the + */ + protected setCitationConferenceTitleTag(): void { + const value = this.getMetaTagValue('sedici.relation.event'); + this.addMetaTag('citation_conference_title', value); + } + + /** + * Add to the + */ + protected setCitationOnlineDateTag(): void { + const value = this.getMetaTagValue('dc.date.available'); + this.addMetaTag('citation_online_date', value); + } + + /** + * Add to the + */ + protected setCitationJournalTitleTag(): void { + const value = this.getMetaTagValue('sedici.relation.journalTitle'); + this.addMetaTag('citation_journal_title', value); + } + + /** + * Add to the + */ + protected setCitationVolumeAndIssueTag(): void { + const value = this.getMetaTagValue('sedici.relation.journalVolumeAndIssue'); + this.addMetaTag('citation_volume', value); + } + + /** + * Add to the + */ + protected setCitationDOITag(): void { + const value = this.getMetaTagValue('sedici.identifier.doi'); + this.addMetaTag('citation_doi', value); + } + + protected hasSubtype(value: string): boolean { + return this.currentObject.value.hasMetadata('sedici.subtype', { value: value, ignoreCase: true }); + } + + protected isDissertation(): boolean { + return this.hasType('Tesis'); + } + + protected isTechReport(): boolean { + return this.hasSubtype('Reporte tecnico'); + } + + protected getMultipleMetaTagValuesAndCombine(keys: string[]): string { + const values = keys.reduce((acc, key) => acc.concat(this.getMetaTagValues([key])), []); + return values.join('; '); + } + + // DC/DCTERMS TAGS + + protected setIdentifierURITags(): void { + this.setMetaTags('DC.identifier', ['sedici.identifier.uri', 'dc.identifier.uri'], 'DCTERMS.URI'); + } + + protected setIdentifierTags(): void { + this.setMetaTags('DC.identifier', ['sedici.identifier.doi', 'sedici.identifier.other', 'sedici.identifier.isbn', 'sedici.identifier.issn', 'sedici.identifier.expediente']); + } + + protected setTitleTags(): void { + this.setMetaTags('DC.title', ['dc.title']); + } + + protected setTitleAlternativeTags(): void { + this.setMetaTags('DCTERMS.alternative', ['dc.title.alternative', 'sedici.title.subtitle']); + } + + protected setCreatorTags(): void { + this.setMetaTags('DC.creator', ['sedici.creator.person', 'sedici.creator.corporate', 'sedici.contributor.editor', 'sedici.contributor.compiler']); + } + + protected setIssuedTags(): void { + this.setMetaTags('DCTERMS.issued', ['dc.date.issued'], 'DCTERMS.W3CDTF'); + } + + protected setAvailableTags(): void { + this.setMetaTags('DCTERMS.available', ['dc.date.available'], 'DCTERMS.W3CDTF'); + } + + protected setCreatedTags(): void { + this.setMetaTags('DCTERMS.created', ['dc.date.accessioned'], 'DCTERMS.W3CDTF'); + } + + protected setAbstractTags(): void { + this.setMetaTags('DCTERMS.abstract', ['dc.description.abstract']); + } + + protected setDescriptionTags(): void { + this.setMetaTags('DC.description', ['sedici.description.note']); + } + + protected setFormatTags(): void { + this.setMetaTags('DC.format', ['dc.format']); + } + + protected setExtentTags(): void { + this.setMetaTags('DCTERMS.extent', ['dc.format.extent']); + } + + protected setLanguageTags(): void { + this.setMetaTags('DC.language', ['dc.language'], 'DCTERMS.RFC1766'); + } + + protected setSubjectTags(): void { + this.setMetaTags('DC.subject', ['dc.subject', 'sedici.subject.materias']); + } + + protected setTypeTags(): void { + const meta = this.getMetaTag(['dc.type', 'sedici.subtype']); + this.setMetaTags('DC.type', ['dc.type', 'sedici.subtype']); + } + + protected setLicenseTags(): void { + this.setMetaTags('DCTERMS.license', ['sedici.rights.license']); + } + + protected setRightsTags(): void { + this.setMetaTags('DC.rights', ['sedici.rights.uri'], 'DCTERMS.URI'); + } + + protected setProvenanceTags(): void { + this.setMetaTags('DCTERMS.provenance', ['dc.description.provenance']); + } + + protected setContributorTags(): void { + this.setMetaTags('DC.contributor', ['sedici.contributor.director', 'sedici.contributor.codirector', 'sedici.contributor.colaborator', 'sedici.contributor.translator', 'sedici.contributor.juror', 'sedici.contributor.inscriber']); + } + + protected setIsVersionOfTags(): void { + this.setMetaTags('DCTERMS.isVersionOf', ['dc.relation.isVersionOf']); + } + + protected setPublisherTags(): void { + this.setMetaTags('DC.publisher', ['dc.publisher']); + } + + protected setDateTags(): void { + this.setMetaTags('DC.date', ['sedici.date.exposure']); + } + + protected setRelationTags(): void { + this.setMetaTags('DC.relation', ['dc.relation']); + } + + protected setSourceTags(): void { + this.setMetaTags('DC.source', ['dc.source']); + } + + protected setIsPartOfTags(): void { + this.setMetaTags('DCTERMS.isPartOf', ['sedici.relation.event', 'dc.relation.ispartof', 'sedici.relation.journalTitle', 'sedici.relation.journalVolumeAndIssue']); + } + + protected setMetaTags(name: string, keys: string[], scheme?: string): void { + const meta = this.getMetaTag(keys); + const values = meta.map((m) => m.value); + const langs = meta.map((m) => m.language); + this.addMetaTagsWithSchemeAndLang(name, values, langs, scheme); + } + + protected getMetaTag(keys: string[]): MetadataValue[] { + return this.currentObject.value.allMetadata(keys); + } + + protected addMetaTagWithSchemeAndLang(name: string, content: string, lang?: string, scheme?: string): void { + if (content) { + const tag: MetaDefinition = { name, content }; + + if (lang) { + tag['xml:lang'] = lang; + tag['lang'] = lang; + } + + if (scheme) { + tag['scheme'] = scheme; + } + + this.meta.addTag(tag); + this.storeTag(name); + } + } + + protected addMetaTagsWithSchemeAndLang(name: string, content: string[], lang?: string[], scheme?: string): void { + for (let i = 0; i < content.length; i++) { + this.addMetaTagWithSchemeAndLang(name, content[i], lang ? lang[i] : undefined, scheme); + } + } + + +} \ No newline at end of file diff --git a/src/app/core/metadata/head-tag.service.ts b/src/app/core/metadata/head-tag.service.ts index 270e5fde723..cf554557382 100644 --- a/src/app/core/metadata/head-tag.service.ts +++ b/src/app/core/metadata/head-tag.service.ts @@ -93,7 +93,7 @@ const tagsInUseSelector = }) export class HeadTagService { - private currentObject: BehaviorSubject = new BehaviorSubject(undefined); + protected currentObject: BehaviorSubject = new BehaviorSubject(undefined); /** * When generating the citation_pdf_url meta tag for Items with more than one Bitstream (and no primary Bitstream), diff --git a/src/app/init.service.ts b/src/app/init.service.ts index d99d2d4c6b9..f5f1dbdff21 100644 --- a/src/app/init.service.ts +++ b/src/app/init.service.ts @@ -39,6 +39,7 @@ import { isAuthenticationBlocking } from './core/auth/selectors'; import { LAZY_DATA_SERVICES } from './core/data-services-map'; import { LocaleService } from './core/locale/locale.service'; import { HeadTagService } from './core/metadata/head-tag.service'; +import { HeadTagSEDICIService } from './core/metadata/head-tag-sedici.service'; import { CorrelationIdService } from './correlation-id/correlation-id.service'; import { dsDynamicFormControlMapFn } from './shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-map-fn'; import { MenuService } from './shared/menu/menu.service'; @@ -70,7 +71,7 @@ export abstract class InitService { protected translate: TranslateService, protected localeService: LocaleService, protected angulartics2DSpace: Angulartics2DSpace, - protected headTagService: HeadTagService, + protected headTagSEDICIService: HeadTagSEDICIService, protected breadcrumbsService: BreadcrumbsService, protected themeService: ThemeService, protected menuService: MenuService, @@ -207,13 +208,13 @@ export abstract class InitService { /** * Start route-listening subscriptions - * - {@link HeadTagService.listenForRouteChange} + * - {@link HeadTagSEDICIService.listenForRouteChange} * - {@link BreadcrumbsService.listenForRouteChanges} * - {@link ThemeService.listenForRouteChanges} * @protected */ protected initRouteListeners(): void { - this.headTagService.listenForRouteChange(); + this.headTagSEDICIService.listenForRouteChange(); this.breadcrumbsService.listenForRouteChanges(); this.themeService.listenForRouteChanges(); this.menuService.listenForRouteChanges(); diff --git a/src/modules/app/browser-init.service.ts b/src/modules/app/browser-init.service.ts index 014a8f5daad..9b89cc2c672 100644 --- a/src/modules/app/browser-init.service.ts +++ b/src/modules/app/browser-init.service.ts @@ -35,6 +35,7 @@ import { coreSelector } from '../../app/core/core.selectors'; import { RootDataService } from '../../app/core/data/root-data.service'; import { LocaleService } from '../../app/core/locale/locale.service'; import { HeadTagService } from '../../app/core/metadata/head-tag.service'; +import { HeadTagSEDICIService } from 'src/app/core/metadata/head-tag-sedici.service'; import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service'; import { InitService } from '../../app/init.service'; import { KlaroService } from '../../app/shared/cookies/klaro.service'; @@ -73,7 +74,7 @@ export class BrowserInitService extends InitService { protected localeService: LocaleService, protected angulartics2DSpace: Angulartics2DSpace, protected googleAnalyticsService: GoogleAnalyticsService, - protected headTagService: HeadTagService, + protected headTagSEDICIService: HeadTagSEDICIService, protected breadcrumbsService: BreadcrumbsService, protected klaroService: KlaroService, protected authService: AuthService, @@ -89,7 +90,7 @@ export class BrowserInitService extends InitService { translate, localeService, angulartics2DSpace, - headTagService, + headTagSEDICIService, breadcrumbsService, themeService, menuService, diff --git a/src/modules/app/server-init.service.ts b/src/modules/app/server-init.service.ts index e22feec09a2..1cd3c6ad99e 100644 --- a/src/modules/app/server-init.service.ts +++ b/src/modules/app/server-init.service.ts @@ -19,6 +19,7 @@ import { AppState } from '../../app/app.reducer'; import { BreadcrumbsService } from '../../app/breadcrumbs/breadcrumbs.service'; import { LocaleService } from '../../app/core/locale/locale.service'; import { HeadTagService } from '../../app/core/metadata/head-tag.service'; +import { HeadTagSEDICIService } from 'src/app/core/metadata/head-tag-sedici.service'; import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service'; import { InitService } from '../../app/init.service'; import { MenuService } from '../../app/shared/menu/menu.service'; @@ -44,7 +45,7 @@ export class ServerInitService extends InitService { protected translate: TranslateService, protected localeService: LocaleService, protected angulartics2DSpace: Angulartics2DSpace, - protected headTagService: HeadTagService, + protected headTagSEDICIService: HeadTagSEDICIService, protected breadcrumbsService: BreadcrumbsService, protected themeService: ThemeService, protected menuService: MenuService, @@ -56,7 +57,7 @@ export class ServerInitService extends InitService { translate, localeService, angulartics2DSpace, - headTagService, + headTagSEDICIService, breadcrumbsService, themeService, menuService, From 03a5f0bb14c20b4c812d9c24f1539c037ff36108 Mon Sep 17 00:00:00 2001 From: Felipe Mosqueira Date: Mon, 10 Jun 2024 09:02:16 -0300 Subject: [PATCH 2/5] Agrego el campo dc.format.medium al metaTag de DC.format --- src/app/core/metadata/head-tag-sedici.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/core/metadata/head-tag-sedici.service.ts b/src/app/core/metadata/head-tag-sedici.service.ts index a4b0df0aa8e..a651fa84bb9 100644 --- a/src/app/core/metadata/head-tag-sedici.service.ts +++ b/src/app/core/metadata/head-tag-sedici.service.ts @@ -392,7 +392,7 @@ export class HeadTagSEDICIService extends HeadTagService { } protected setFormatTags(): void { - this.setMetaTags('DC.format', ['dc.format']); + this.setMetaTags('DC.format', ['dc.format', 'dc.format.medium']); } protected setExtentTags(): void { From 45a872ea71932fd86c3efc19952338738c09e953 Mon Sep 17 00:00:00 2001 From: Felipe Mosqueira Date: Tue, 28 May 2024 11:59:44 -0300 Subject: [PATCH 3/5] =?UTF-8?q?10611=20extiendo=20la=20clase=20head-tag.se?= =?UTF-8?q?rvice.ts,=20modifico=20algunos=20m=C3=A9todos=20y=20agrego=20lo?= =?UTF-8?q?s=20que=20faltaban;=20dejo=20preparado=20el=20m=C3=A9todo=20par?= =?UTF-8?q?a=20los=20links=20a=20PDFs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/app-routing-paths.ts | 7 + .../core/metadata/head-tag-sedici.service.ts | 491 ++++++++++++++++++ src/app/core/metadata/head-tag.service.ts | 2 +- src/app/init.service.ts | 7 +- src/modules/app/browser-init.service.ts | 5 +- src/modules/app/server-init.service.ts | 5 +- 6 files changed, 509 insertions(+), 8 deletions(-) create mode 100644 src/app/core/metadata/head-tag-sedici.service.ts diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index 7d202f16e9c..5e0d22fdcd1 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -1,3 +1,4 @@ +import { Observable, from } from 'rxjs'; import { getCollectionPageRoute } from './collection-page/collection-page-routing-paths'; import { getCommunityPageRoute } from './community-page/community-page-routing-paths'; import { Collection } from './core/shared/collection.model'; @@ -22,6 +23,12 @@ export function getBitstreamModuleRoute() { return `/${BITSTREAM_MODULE_PATH}`; } +export function getBitstreamRoute(item, bitstream): Observable { + return from(fetch(bitstream._links.self.href) + .then(response => response.json()) + .then(data => `/${LEGACY_BITSTREAM_MODULE_PATH}/handle/${item.handle}/${bitstream.name}?sequence=${data.sequenceId}`)); +} + export function getBitstreamDownloadRoute(bitstream): string { return new URLCombiner(getBitstreamModuleRoute(), bitstream.uuid, 'download').toString(); } diff --git a/src/app/core/metadata/head-tag-sedici.service.ts b/src/app/core/metadata/head-tag-sedici.service.ts new file mode 100644 index 00000000000..a4b0df0aa8e --- /dev/null +++ b/src/app/core/metadata/head-tag-sedici.service.ts @@ -0,0 +1,491 @@ +import { + Inject, + Injectable, +} from '@angular/core'; +import { + Meta, + MetaDefinition, + Title, +} from '@angular/platform-browser'; +import { + Router, +} from '@angular/router'; +import { + Store, +} from '@ngrx/store'; +import { TranslateService } from '@ngx-translate/core'; + +import { + APP_CONFIG, + AppConfig, +} from '../../../config/app-config.interface'; +import { DSONameService } from '../breadcrumbs/dso-name.service'; +import { CoreState } from '../core-state.model'; +import { BundleDataService } from '../data/bundle-data.service'; +import { AuthorizationDataService } from '../data/feature-authorization/authorization-data.service'; +import { RootDataService } from '../data/root-data.service'; +import { HardRedirectService } from '../services/hard-redirect.service'; +import { HeadTagService } from './head-tag.service'; +import { Item } from '../shared/item.model'; +import { followLink } from 'src/app/shared/utils/follow-link-config.model'; +import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../shared/operators'; +import { Bundle } from '../shared/bundle.model'; +import { map, switchMap, take, of as observableOf, concat as observableConcat, mergeMap, filter, EMPTY, Observable } from 'rxjs'; +import { Bitstream } from '../shared/bitstream.model'; +import { RemoteData } from '../data/remote-data'; +import { hasValue, isNotEmpty } from 'src/app/shared/empty.util'; +import { getDownloadableBitstream } from '../shared/bitstream.operators'; +import { getBitstreamRoute } from 'src/app/app-routing-paths'; +import { PaginatedList } from '../data/paginated-list.model'; +import { URLCombiner } from '../url-combiner/url-combiner'; +import { BitstreamFormat } from '../shared/bitstream-format.model'; +import { MetadataValue } from '../shared/metadata.models'; +import { LinkDefinition } from '../services/link-head.service'; +import { LinkHeadService } from '../services/link-head.service'; + +@Injectable({ + providedIn: 'root', +}) +export class HeadTagSEDICIService extends HeadTagService { + + constructor( + protected router: Router, + protected translate: TranslateService, + protected meta: Meta, + protected title: Title, + protected dsoNameService: DSONameService, + protected bundleDataService: BundleDataService, + protected rootService: RootDataService, + protected store: Store, + protected hardRedirectService: HardRedirectService, + @Inject(APP_CONFIG) protected appConfig: AppConfig, + protected authorizationService: AuthorizationDataService, + protected linkService: LinkHeadService + ) { + super(router, translate, meta, title, dsoNameService, bundleDataService, rootService, store, hardRedirectService, appConfig, authorizationService); + } + + protected setDSOMetaTags(): void { + + let link: LinkDefinition = { rel:"schema.DCTERMS", href:"http://purl.org/dc/terms/" }; + this.linkService.addTag(link); + link = { rel:"schema.DC", href:"http://purl.org/dc/elements/1.1/" }; + this.linkService.addTag(link); + + this.setIdentifierURITags(); + this.setIdentifierTags(); + this.setTitleTags(); + this.setTitleAlternativeTags(); + this.setCreatorTags(); + this.setIssuedTags(); + this.setAvailableTags(); + this.setCreatedTags(); + this.setAbstractTags(); + this.setDescriptionTags(); + this.setFormatTags(); + this.setExtentTags(); + this.setLanguageTags(); + this.setSubjectTags(); + this.setTypeTags(); + this.setLicenseTags(); + this.setRightsTags(); + this.setProvenanceTags(); + this.setContributorTags(); + this.setIsVersionOfTags(); + this.setPublisherTags(); + this.setDateTags(); + this.setRelationTags(); + this.setSourceTags(); + this.setIsPartOfTags(); + + super.setDSOMetaTags(); + if (this.isDissertation()) { + this.setCitationDissertationInstitutionTag(); + } + if (this.isTechReport()) { + this.setCitationTechnicalReportInstitutionTag(); + } + this.setCitationDOITag(); + this.setCitationJournalTitleTag(); + this.setCitationVolumeAndIssueTag(); + this.setCitationConferenceTitleTag(); + this.setCitationOnlineDateTag(); + + /** + * Consideraciones: + this.setCitationPdfUrlTag(); // PARCHE PDF + + this.setTitleTag(); y this.setDescriptionTag(); // NO DEBERÍAN LLAMARSE + */ + } + + // CITATION TAGS + + /** + * Add to the + */ + protected setCitationTitleTag(): void { + const value = this.getMetaTagValue('dc.title'); + this.addMetaTag('citation_title', value); + } + + protected setCitationAuthorTags(): void { + const values: string[] = this.getMetaTagValues(['sedici.creator.person', 'sedici.creator.corporate', 'sedici.contributor.compiler', 'sedici.creator.interprete', 'sedici.contributor.editor']); + this.addMetaTags('citation_author', values); + } + + protected setCitationPublicationDateTag(): void { + const value = this.getFirstMetaTagValue(['dc.date.issued', 'sedici.date.exposure', 'dc.date.created']); + this.addMetaTag('citation_publication_date', value); + } + + protected setCitationISSNTag(): void { + const value = this.getMetaTagValue('sedici.identifier.issn'); + this.addMetaTag('citation_issn', value); + } + + protected setCitationISBNTag(): void { + const value = this.getMetaTagValue('sedici.identifier.isbn'); + this.addMetaTag('citation_isbn', value); + } + + /** + * Add to the + */ + protected setCitationDissertationInstitutionTag(): void { + const value = this.getMetaTagValue('thesis.degree.grantor'); + this.addMetaTag('citation_dissertation_institution', value); + } + + /** + * Add to the + */ + protected setCitationTechnicalReportInstitutionTag(): void { + const value = this.getFirstMetaTagValue(['dc.publisher', 'mods.originInfo.place']); + this.addMetaTag('citation_technical_report_institution', value); + } + + /** + * Add dc.publisher to the . The tag name depends on the item type. + */ + protected setCitationPublisherTag(): void { + const value = this.getMetaTagValue('dc.publisher'); + this.addMetaTag('citation_publisher', value); + } + + protected setCitationKeywordsTag(): void { + const value = this.getMultipleMetaTagValuesAndCombine(['dc.subject', 'sedici.subject.materias']); + this.addMetaTag('citation_keywords', value); + } + + // PARCHE PDF + // setCitationPdfUrlTag(): void { + // if (this.currentObject.value instanceof Item) { + // const item = this.currentObject.value as Item; + + // // Retrieve the ORIGINAL bundle for the item + // this.bundleDataService.findByItemAndName( + // item, + // 'ORIGINAL', + // true, + // true, + // followLink('primaryBitstream'), + // followLink('bitstreams', { + // findListOptions: { + // // limit the number of bitstreams used to find the citation pdf url to the number + // // shown by default on an item page + // elementsPerPage: this.appConfig.item.bitstream.pageSize, + // }, + // }, followLink('format')), + // ).pipe( + // getFirstSucceededRemoteDataPayload(), + // switchMap((bundle: Bundle) => + // // First try the primary bitstream + // bundle.primaryBitstream.pipe( + // getFirstCompletedRemoteData(), + // map((rd: RemoteData) => { + // if (hasValue(rd.payload)) { + // return rd.payload; + // } else { + // return null; + // } + // }), + // getDownloadableBitstream(this.authorizationService), + // // return the bundle as well so we can use it again if there's no primary bitstream + // map((bitstream: Bitstream) => [bundle, bitstream]), + // ), + // ), + // switchMap(([bundle, primaryBitstream]: [Bundle, Bitstream]) => { + // if (hasValue(primaryBitstream)) { + // // If there was a downloadable primary bitstream, emit its link + // return getBitstreamRoute(item, primaryBitstream); + // } else { + // // Otherwise consider the regular bitstreams in the bundle + // return bundle.bitstreams.pipe( + // getFirstCompletedRemoteData(), + // switchMap((bitstreamRd: RemoteData>) => { + // if (hasValue(bitstreamRd.payload) && bitstreamRd.payload.totalElements === 1) { + // // If there's only one bitstream in the bundle, emit its link if its downloadable + // return this.getBitLinkIfDownloadable(bitstreamRd.payload.page[0], bitstreamRd); + // } else { + // // Otherwise check all bitstreams to see if one matches the format whitelist + // return this.getFirstAllowedFormatBitstreamLink(bitstreamRd); + // } + // }), + // ); + // } + // }), + // take(1), + // ).subscribe((link: string) => { + // // Use the found link to set the tag + // this.addMetaTag( + // 'citation_pdf_url', + // new URLCombiner(this.hardRedirectService.getCurrentOrigin(), link).toString(), + // ); + // }); + // } + // } + + // getBitLinkIfDownloadable(bitstream: Bitstream, bitstreamRd: RemoteData>): Observable { + // return observableOf(bitstream).pipe( + // getDownloadableBitstream(this.authorizationService), + // switchMap((bit: Bitstream) => { + // if (hasValue(bit)) { + // const item = this.currentObject.value as Item; + // return getBitstreamRoute(item, bit); + // } else { + // // Otherwise check all bitstreams to see if one matches the format whitelist + // return this.getFirstAllowedFormatBitstreamLink(bitstreamRd); + // } + // }), + // ); + // } + + // protected getFirstAllowedFormatBitstreamLink(bitstreamRd: RemoteData>): Observable { + // const item = this.currentObject.value as Item; + // if (hasValue(bitstreamRd.payload) && isNotEmpty(bitstreamRd.payload.page)) { + // // Retrieve the formats of all bitstreams in the page sequentially + // return observableConcat( + // ...bitstreamRd.payload.page.map((bitstream: Bitstream) => bitstream.format.pipe( + // getFirstSucceededRemoteDataPayload(), + // // Keep the original bitstream, because it, not the format, is what we'll need + // // for the link at the end + // map((format: BitstreamFormat) => [bitstream, format]), + // )), + // ).pipe( + // // Verify that the bitstream is downloadable + // mergeMap(([bitstream, format]: [Bitstream, BitstreamFormat]) => observableOf(bitstream).pipe( + // getDownloadableBitstream(this.authorizationService), + // map((bit: Bitstream) => [bit, format]), + // )), + // // Filter out only pairs with whitelisted formats and non-null bitstreams, null from download check + // filter(([bitstream, format]: [Bitstream, BitstreamFormat]) => + // hasValue(format) && hasValue(bitstream) && this.CITATION_PDF_URL_MIMETYPES.includes(format.mimetype)), + // // We only need 1 + // take(1), + // // Emit the link of the match + // // tap((v) => console.log('result', v)), + // mergeMap(([bitstream ]: [Bitstream, BitstreamFormat]) => getBitstreamRoute(item, bitstream)), + // ); + // } else { + // return EMPTY; + // } + // } + + /** + * Add to the + */ + protected setCitationConferenceTitleTag(): void { + const value = this.getMetaTagValue('sedici.relation.event'); + this.addMetaTag('citation_conference_title', value); + } + + /** + * Add to the + */ + protected setCitationOnlineDateTag(): void { + const value = this.getMetaTagValue('dc.date.available'); + this.addMetaTag('citation_online_date', value); + } + + /** + * Add to the + */ + protected setCitationJournalTitleTag(): void { + const value = this.getMetaTagValue('sedici.relation.journalTitle'); + this.addMetaTag('citation_journal_title', value); + } + + /** + * Add to the + */ + protected setCitationVolumeAndIssueTag(): void { + const value = this.getMetaTagValue('sedici.relation.journalVolumeAndIssue'); + this.addMetaTag('citation_volume', value); + } + + /** + * Add to the + */ + protected setCitationDOITag(): void { + const value = this.getMetaTagValue('sedici.identifier.doi'); + this.addMetaTag('citation_doi', value); + } + + protected hasSubtype(value: string): boolean { + return this.currentObject.value.hasMetadata('sedici.subtype', { value: value, ignoreCase: true }); + } + + protected isDissertation(): boolean { + return this.hasType('Tesis'); + } + + protected isTechReport(): boolean { + return this.hasSubtype('Reporte tecnico'); + } + + protected getMultipleMetaTagValuesAndCombine(keys: string[]): string { + const values = keys.reduce((acc, key) => acc.concat(this.getMetaTagValues([key])), []); + return values.join('; '); + } + + // DC/DCTERMS TAGS + + protected setIdentifierURITags(): void { + this.setMetaTags('DC.identifier', ['sedici.identifier.uri', 'dc.identifier.uri'], 'DCTERMS.URI'); + } + + protected setIdentifierTags(): void { + this.setMetaTags('DC.identifier', ['sedici.identifier.doi', 'sedici.identifier.other', 'sedici.identifier.isbn', 'sedici.identifier.issn', 'sedici.identifier.expediente']); + } + + protected setTitleTags(): void { + this.setMetaTags('DC.title', ['dc.title']); + } + + protected setTitleAlternativeTags(): void { + this.setMetaTags('DCTERMS.alternative', ['dc.title.alternative', 'sedici.title.subtitle']); + } + + protected setCreatorTags(): void { + this.setMetaTags('DC.creator', ['sedici.creator.person', 'sedici.creator.corporate', 'sedici.contributor.editor', 'sedici.contributor.compiler']); + } + + protected setIssuedTags(): void { + this.setMetaTags('DCTERMS.issued', ['dc.date.issued'], 'DCTERMS.W3CDTF'); + } + + protected setAvailableTags(): void { + this.setMetaTags('DCTERMS.available', ['dc.date.available'], 'DCTERMS.W3CDTF'); + } + + protected setCreatedTags(): void { + this.setMetaTags('DCTERMS.created', ['dc.date.accessioned'], 'DCTERMS.W3CDTF'); + } + + protected setAbstractTags(): void { + this.setMetaTags('DCTERMS.abstract', ['dc.description.abstract']); + } + + protected setDescriptionTags(): void { + this.setMetaTags('DC.description', ['sedici.description.note']); + } + + protected setFormatTags(): void { + this.setMetaTags('DC.format', ['dc.format']); + } + + protected setExtentTags(): void { + this.setMetaTags('DCTERMS.extent', ['dc.format.extent']); + } + + protected setLanguageTags(): void { + this.setMetaTags('DC.language', ['dc.language'], 'DCTERMS.RFC1766'); + } + + protected setSubjectTags(): void { + this.setMetaTags('DC.subject', ['dc.subject', 'sedici.subject.materias']); + } + + protected setTypeTags(): void { + const meta = this.getMetaTag(['dc.type', 'sedici.subtype']); + this.setMetaTags('DC.type', ['dc.type', 'sedici.subtype']); + } + + protected setLicenseTags(): void { + this.setMetaTags('DCTERMS.license', ['sedici.rights.license']); + } + + protected setRightsTags(): void { + this.setMetaTags('DC.rights', ['sedici.rights.uri'], 'DCTERMS.URI'); + } + + protected setProvenanceTags(): void { + this.setMetaTags('DCTERMS.provenance', ['dc.description.provenance']); + } + + protected setContributorTags(): void { + this.setMetaTags('DC.contributor', ['sedici.contributor.director', 'sedici.contributor.codirector', 'sedici.contributor.colaborator', 'sedici.contributor.translator', 'sedici.contributor.juror', 'sedici.contributor.inscriber']); + } + + protected setIsVersionOfTags(): void { + this.setMetaTags('DCTERMS.isVersionOf', ['dc.relation.isVersionOf']); + } + + protected setPublisherTags(): void { + this.setMetaTags('DC.publisher', ['dc.publisher']); + } + + protected setDateTags(): void { + this.setMetaTags('DC.date', ['sedici.date.exposure']); + } + + protected setRelationTags(): void { + this.setMetaTags('DC.relation', ['dc.relation']); + } + + protected setSourceTags(): void { + this.setMetaTags('DC.source', ['dc.source']); + } + + protected setIsPartOfTags(): void { + this.setMetaTags('DCTERMS.isPartOf', ['sedici.relation.event', 'dc.relation.ispartof', 'sedici.relation.journalTitle', 'sedici.relation.journalVolumeAndIssue']); + } + + protected setMetaTags(name: string, keys: string[], scheme?: string): void { + const meta = this.getMetaTag(keys); + const values = meta.map((m) => m.value); + const langs = meta.map((m) => m.language); + this.addMetaTagsWithSchemeAndLang(name, values, langs, scheme); + } + + protected getMetaTag(keys: string[]): MetadataValue[] { + return this.currentObject.value.allMetadata(keys); + } + + protected addMetaTagWithSchemeAndLang(name: string, content: string, lang?: string, scheme?: string): void { + if (content) { + const tag: MetaDefinition = { name, content }; + + if (lang) { + tag['xml:lang'] = lang; + tag['lang'] = lang; + } + + if (scheme) { + tag['scheme'] = scheme; + } + + this.meta.addTag(tag); + this.storeTag(name); + } + } + + protected addMetaTagsWithSchemeAndLang(name: string, content: string[], lang?: string[], scheme?: string): void { + for (let i = 0; i < content.length; i++) { + this.addMetaTagWithSchemeAndLang(name, content[i], lang ? lang[i] : undefined, scheme); + } + } + + +} \ No newline at end of file diff --git a/src/app/core/metadata/head-tag.service.ts b/src/app/core/metadata/head-tag.service.ts index 270e5fde723..cf554557382 100644 --- a/src/app/core/metadata/head-tag.service.ts +++ b/src/app/core/metadata/head-tag.service.ts @@ -93,7 +93,7 @@ const tagsInUseSelector = }) export class HeadTagService { - private currentObject: BehaviorSubject = new BehaviorSubject(undefined); + protected currentObject: BehaviorSubject = new BehaviorSubject(undefined); /** * When generating the citation_pdf_url meta tag for Items with more than one Bitstream (and no primary Bitstream), diff --git a/src/app/init.service.ts b/src/app/init.service.ts index d99d2d4c6b9..f5f1dbdff21 100644 --- a/src/app/init.service.ts +++ b/src/app/init.service.ts @@ -39,6 +39,7 @@ import { isAuthenticationBlocking } from './core/auth/selectors'; import { LAZY_DATA_SERVICES } from './core/data-services-map'; import { LocaleService } from './core/locale/locale.service'; import { HeadTagService } from './core/metadata/head-tag.service'; +import { HeadTagSEDICIService } from './core/metadata/head-tag-sedici.service'; import { CorrelationIdService } from './correlation-id/correlation-id.service'; import { dsDynamicFormControlMapFn } from './shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-map-fn'; import { MenuService } from './shared/menu/menu.service'; @@ -70,7 +71,7 @@ export abstract class InitService { protected translate: TranslateService, protected localeService: LocaleService, protected angulartics2DSpace: Angulartics2DSpace, - protected headTagService: HeadTagService, + protected headTagSEDICIService: HeadTagSEDICIService, protected breadcrumbsService: BreadcrumbsService, protected themeService: ThemeService, protected menuService: MenuService, @@ -207,13 +208,13 @@ export abstract class InitService { /** * Start route-listening subscriptions - * - {@link HeadTagService.listenForRouteChange} + * - {@link HeadTagSEDICIService.listenForRouteChange} * - {@link BreadcrumbsService.listenForRouteChanges} * - {@link ThemeService.listenForRouteChanges} * @protected */ protected initRouteListeners(): void { - this.headTagService.listenForRouteChange(); + this.headTagSEDICIService.listenForRouteChange(); this.breadcrumbsService.listenForRouteChanges(); this.themeService.listenForRouteChanges(); this.menuService.listenForRouteChanges(); diff --git a/src/modules/app/browser-init.service.ts b/src/modules/app/browser-init.service.ts index 014a8f5daad..9b89cc2c672 100644 --- a/src/modules/app/browser-init.service.ts +++ b/src/modules/app/browser-init.service.ts @@ -35,6 +35,7 @@ import { coreSelector } from '../../app/core/core.selectors'; import { RootDataService } from '../../app/core/data/root-data.service'; import { LocaleService } from '../../app/core/locale/locale.service'; import { HeadTagService } from '../../app/core/metadata/head-tag.service'; +import { HeadTagSEDICIService } from 'src/app/core/metadata/head-tag-sedici.service'; import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service'; import { InitService } from '../../app/init.service'; import { KlaroService } from '../../app/shared/cookies/klaro.service'; @@ -73,7 +74,7 @@ export class BrowserInitService extends InitService { protected localeService: LocaleService, protected angulartics2DSpace: Angulartics2DSpace, protected googleAnalyticsService: GoogleAnalyticsService, - protected headTagService: HeadTagService, + protected headTagSEDICIService: HeadTagSEDICIService, protected breadcrumbsService: BreadcrumbsService, protected klaroService: KlaroService, protected authService: AuthService, @@ -89,7 +90,7 @@ export class BrowserInitService extends InitService { translate, localeService, angulartics2DSpace, - headTagService, + headTagSEDICIService, breadcrumbsService, themeService, menuService, diff --git a/src/modules/app/server-init.service.ts b/src/modules/app/server-init.service.ts index e22feec09a2..1cd3c6ad99e 100644 --- a/src/modules/app/server-init.service.ts +++ b/src/modules/app/server-init.service.ts @@ -19,6 +19,7 @@ import { AppState } from '../../app/app.reducer'; import { BreadcrumbsService } from '../../app/breadcrumbs/breadcrumbs.service'; import { LocaleService } from '../../app/core/locale/locale.service'; import { HeadTagService } from '../../app/core/metadata/head-tag.service'; +import { HeadTagSEDICIService } from 'src/app/core/metadata/head-tag-sedici.service'; import { CorrelationIdService } from '../../app/correlation-id/correlation-id.service'; import { InitService } from '../../app/init.service'; import { MenuService } from '../../app/shared/menu/menu.service'; @@ -44,7 +45,7 @@ export class ServerInitService extends InitService { protected translate: TranslateService, protected localeService: LocaleService, protected angulartics2DSpace: Angulartics2DSpace, - protected headTagService: HeadTagService, + protected headTagSEDICIService: HeadTagSEDICIService, protected breadcrumbsService: BreadcrumbsService, protected themeService: ThemeService, protected menuService: MenuService, @@ -56,7 +57,7 @@ export class ServerInitService extends InitService { translate, localeService, angulartics2DSpace, - headTagService, + headTagSEDICIService, breadcrumbsService, themeService, menuService, From 4a54030fb1508e703ea46761fff3380ab25f234a Mon Sep 17 00:00:00 2001 From: Felipe Mosqueira Date: Mon, 10 Jun 2024 09:02:16 -0300 Subject: [PATCH 4/5] Agrego el campo dc.format.medium al metaTag de DC.format --- src/app/core/metadata/head-tag-sedici.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/core/metadata/head-tag-sedici.service.ts b/src/app/core/metadata/head-tag-sedici.service.ts index a4b0df0aa8e..a651fa84bb9 100644 --- a/src/app/core/metadata/head-tag-sedici.service.ts +++ b/src/app/core/metadata/head-tag-sedici.service.ts @@ -392,7 +392,7 @@ export class HeadTagSEDICIService extends HeadTagService { } protected setFormatTags(): void { - this.setMetaTags('DC.format', ['dc.format']); + this.setMetaTags('DC.format', ['dc.format', 'dc.format.medium']); } protected setExtentTags(): void { From 250db61ee3f8a2ef3d9508f9c7631decd385c085 Mon Sep 17 00:00:00 2001 From: Felipe Mosqueira Date: Fri, 5 Jul 2024 09:57:44 -0300 Subject: [PATCH 5/5] 10611 citation_pdf_url y cambios menores --- src/app/app-routing-paths.ts | 6 +- .../legacy-bitstream-url-redirect.guard.ts | 2 +- src/app/core/data/bitstream-data.service.ts | 2 +- .../core/metadata/head-tag-sedici.service.ts | 247 ++++++++++-------- src/app/core/metadata/head-tag.service.ts | 2 +- 5 files changed, 139 insertions(+), 120 deletions(-) diff --git a/src/app/app-routing-paths.ts b/src/app/app-routing-paths.ts index 5e0d22fdcd1..a4f80232b9b 100644 --- a/src/app/app-routing-paths.ts +++ b/src/app/app-routing-paths.ts @@ -24,9 +24,13 @@ export function getBitstreamModuleRoute() { } export function getBitstreamRoute(item, bitstream): Observable { + const name = (bitstream.metadata["dc.description"] && bitstream.metadata["dc.description"][0] ? bitstream.metadata["dc.description"][0].value : item._name) + .split('') + .map(char => ['!', '#', '$', '%', '&', '(', ')', '*', '+', ',', '/', ':', ';', '=', '?', '@', '[', ']', ' ', '\''].includes(char) ? '_' : char) + .join(''); return from(fetch(bitstream._links.self.href) .then(response => response.json()) - .then(data => `/${LEGACY_BITSTREAM_MODULE_PATH}/handle/${item.handle}/${bitstream.name}?sequence=${data.sequenceId}`)); + .then(data => `/${LEGACY_BITSTREAM_MODULE_PATH}/handle/${item.handle}/${name}.pdf?sequence=${data.sequenceId}`)); } export function getBitstreamDownloadRoute(bitstream): string { diff --git a/src/app/bitstream-page/legacy-bitstream-url-redirect.guard.ts b/src/app/bitstream-page/legacy-bitstream-url-redirect.guard.ts index 78403ed7e3f..419492b4af4 100644 --- a/src/app/bitstream-page/legacy-bitstream-url-redirect.guard.ts +++ b/src/app/bitstream-page/legacy-bitstream-url-redirect.guard.ts @@ -37,7 +37,7 @@ export const legacyBitstreamURLRedirectGuard: CanActivateFn = ( const filename = route.params.filename; let sequenceId = route.params.sequence_id; if (hasNoValue(sequenceId)) { - sequenceId = route.queryParams.sequenceId; + sequenceId = route.queryParams.sequence; } return bitstreamDataService.findByItemHandle( `${prefix}/${suffix}`, diff --git a/src/app/core/data/bitstream-data.service.ts b/src/app/core/data/bitstream-data.service.ts index 81d1d74535b..e9048f76123 100644 --- a/src/app/core/data/bitstream-data.service.ts +++ b/src/app/core/data/bitstream-data.service.ts @@ -195,7 +195,7 @@ export class BitstreamDataService extends IdentifiableDataService imp const searchParams = []; searchParams.push(new RequestParam('handle', handle)); if (hasValue(sequenceId)) { - searchParams.push(new RequestParam('sequenceId', sequenceId)); + searchParams.push(new RequestParam('sequence', sequenceId)); } if (hasValue(filename)) { searchParams.push(new RequestParam('filename', filename)); diff --git a/src/app/core/metadata/head-tag-sedici.service.ts b/src/app/core/metadata/head-tag-sedici.service.ts index a651fa84bb9..fc37534eef2 100644 --- a/src/app/core/metadata/head-tag-sedici.service.ts +++ b/src/app/core/metadata/head-tag-sedici.service.ts @@ -89,7 +89,7 @@ export class HeadTagSEDICIService extends HeadTagService { this.setTypeTags(); this.setLicenseTags(); this.setRightsTags(); - this.setProvenanceTags(); + // this.setProvenanceTags(); this.setContributorTags(); this.setIsVersionOfTags(); this.setPublisherTags(); @@ -129,21 +129,33 @@ export class HeadTagSEDICIService extends HeadTagService { this.addMetaTag('citation_title', value); } + /** + * Add to the + */ protected setCitationAuthorTags(): void { const values: string[] = this.getMetaTagValues(['sedici.creator.person', 'sedici.creator.corporate', 'sedici.contributor.compiler', 'sedici.creator.interprete', 'sedici.contributor.editor']); this.addMetaTags('citation_author', values); } + /** + * Add to the + */ protected setCitationPublicationDateTag(): void { const value = this.getFirstMetaTagValue(['dc.date.issued', 'sedici.date.exposure', 'dc.date.created']); this.addMetaTag('citation_publication_date', value); } + /** + * Add to the + */ protected setCitationISSNTag(): void { const value = this.getMetaTagValue('sedici.identifier.issn'); this.addMetaTag('citation_issn', value); } + /** + * Add to the + */ protected setCitationISBNTag(): void { const value = this.getMetaTagValue('sedici.identifier.isbn'); this.addMetaTag('citation_isbn', value); @@ -166,131 +178,134 @@ export class HeadTagSEDICIService extends HeadTagService { } /** - * Add dc.publisher to the . The tag name depends on the item type. + * Add to the */ protected setCitationPublisherTag(): void { const value = this.getMetaTagValue('dc.publisher'); this.addMetaTag('citation_publisher', value); } + /** + * Add to the + */ protected setCitationKeywordsTag(): void { const value = this.getMultipleMetaTagValuesAndCombine(['dc.subject', 'sedici.subject.materias']); this.addMetaTag('citation_keywords', value); } // PARCHE PDF - // setCitationPdfUrlTag(): void { - // if (this.currentObject.value instanceof Item) { - // const item = this.currentObject.value as Item; - - // // Retrieve the ORIGINAL bundle for the item - // this.bundleDataService.findByItemAndName( - // item, - // 'ORIGINAL', - // true, - // true, - // followLink('primaryBitstream'), - // followLink('bitstreams', { - // findListOptions: { - // // limit the number of bitstreams used to find the citation pdf url to the number - // // shown by default on an item page - // elementsPerPage: this.appConfig.item.bitstream.pageSize, - // }, - // }, followLink('format')), - // ).pipe( - // getFirstSucceededRemoteDataPayload(), - // switchMap((bundle: Bundle) => - // // First try the primary bitstream - // bundle.primaryBitstream.pipe( - // getFirstCompletedRemoteData(), - // map((rd: RemoteData) => { - // if (hasValue(rd.payload)) { - // return rd.payload; - // } else { - // return null; - // } - // }), - // getDownloadableBitstream(this.authorizationService), - // // return the bundle as well so we can use it again if there's no primary bitstream - // map((bitstream: Bitstream) => [bundle, bitstream]), - // ), - // ), - // switchMap(([bundle, primaryBitstream]: [Bundle, Bitstream]) => { - // if (hasValue(primaryBitstream)) { - // // If there was a downloadable primary bitstream, emit its link - // return getBitstreamRoute(item, primaryBitstream); - // } else { - // // Otherwise consider the regular bitstreams in the bundle - // return bundle.bitstreams.pipe( - // getFirstCompletedRemoteData(), - // switchMap((bitstreamRd: RemoteData>) => { - // if (hasValue(bitstreamRd.payload) && bitstreamRd.payload.totalElements === 1) { - // // If there's only one bitstream in the bundle, emit its link if its downloadable - // return this.getBitLinkIfDownloadable(bitstreamRd.payload.page[0], bitstreamRd); - // } else { - // // Otherwise check all bitstreams to see if one matches the format whitelist - // return this.getFirstAllowedFormatBitstreamLink(bitstreamRd); - // } - // }), - // ); - // } - // }), - // take(1), - // ).subscribe((link: string) => { - // // Use the found link to set the tag - // this.addMetaTag( - // 'citation_pdf_url', - // new URLCombiner(this.hardRedirectService.getCurrentOrigin(), link).toString(), - // ); - // }); - // } - // } - - // getBitLinkIfDownloadable(bitstream: Bitstream, bitstreamRd: RemoteData>): Observable { - // return observableOf(bitstream).pipe( - // getDownloadableBitstream(this.authorizationService), - // switchMap((bit: Bitstream) => { - // if (hasValue(bit)) { - // const item = this.currentObject.value as Item; - // return getBitstreamRoute(item, bit); - // } else { - // // Otherwise check all bitstreams to see if one matches the format whitelist - // return this.getFirstAllowedFormatBitstreamLink(bitstreamRd); - // } - // }), - // ); - // } + setCitationPdfUrlTag(): void { + if (this.currentObject.value instanceof Item) { + const item = this.currentObject.value as Item; + + // Retrieve the ORIGINAL bundle for the item + this.bundleDataService.findByItemAndName( + item, + 'ORIGINAL', + true, + true, + followLink('primaryBitstream'), + followLink('bitstreams', { + findListOptions: { + // limit the number of bitstreams used to find the citation pdf url to the number + // shown by default on an item page + elementsPerPage: this.appConfig.item.bitstream.pageSize, + }, + }, followLink('format')), + ).pipe( + getFirstSucceededRemoteDataPayload(), + switchMap((bundle: Bundle) => + // First try the primary bitstream + bundle.primaryBitstream.pipe( + getFirstCompletedRemoteData(), + map((rd: RemoteData) => { + if (hasValue(rd.payload)) { + return rd.payload; + } else { + return null; + } + }), + getDownloadableBitstream(this.authorizationService), + // return the bundle as well so we can use it again if there's no primary bitstream + map((bitstream: Bitstream) => [bundle, bitstream]), + ), + ), + switchMap(([bundle, primaryBitstream]: [Bundle, Bitstream]) => { + if (hasValue(primaryBitstream)) { + // If there was a downloadable primary bitstream, emit its link + return getBitstreamRoute(item, primaryBitstream); + } else { + // Otherwise consider the regular bitstreams in the bundle + return bundle.bitstreams.pipe( + getFirstCompletedRemoteData(), + switchMap((bitstreamRd: RemoteData>) => { + if (hasValue(bitstreamRd.payload) && bitstreamRd.payload.totalElements === 1) { + // If there's only one bitstream in the bundle, emit its link if its downloadable + return this.getBitLinkIfDownloadable(bitstreamRd.payload.page[0], bitstreamRd); + } else { + // Otherwise check all bitstreams to see if one matches the format whitelist + return this.getFirstAllowedFormatBitstreamLink(bitstreamRd); + } + }), + ); + } + }), + take(1), + ).subscribe((link: string) => { + // Use the found link to set the tag + this.addMetaTag( + 'citation_pdf_url', + new URLCombiner(this.hardRedirectService.getCurrentOrigin(), link).toString(), + ); + }); + } + } - // protected getFirstAllowedFormatBitstreamLink(bitstreamRd: RemoteData>): Observable { - // const item = this.currentObject.value as Item; - // if (hasValue(bitstreamRd.payload) && isNotEmpty(bitstreamRd.payload.page)) { - // // Retrieve the formats of all bitstreams in the page sequentially - // return observableConcat( - // ...bitstreamRd.payload.page.map((bitstream: Bitstream) => bitstream.format.pipe( - // getFirstSucceededRemoteDataPayload(), - // // Keep the original bitstream, because it, not the format, is what we'll need - // // for the link at the end - // map((format: BitstreamFormat) => [bitstream, format]), - // )), - // ).pipe( - // // Verify that the bitstream is downloadable - // mergeMap(([bitstream, format]: [Bitstream, BitstreamFormat]) => observableOf(bitstream).pipe( - // getDownloadableBitstream(this.authorizationService), - // map((bit: Bitstream) => [bit, format]), - // )), - // // Filter out only pairs with whitelisted formats and non-null bitstreams, null from download check - // filter(([bitstream, format]: [Bitstream, BitstreamFormat]) => - // hasValue(format) && hasValue(bitstream) && this.CITATION_PDF_URL_MIMETYPES.includes(format.mimetype)), - // // We only need 1 - // take(1), - // // Emit the link of the match - // // tap((v) => console.log('result', v)), - // mergeMap(([bitstream ]: [Bitstream, BitstreamFormat]) => getBitstreamRoute(item, bitstream)), - // ); - // } else { - // return EMPTY; - // } - // } + getBitLinkIfDownloadable(bitstream: Bitstream, bitstreamRd: RemoteData>): Observable { + return observableOf(bitstream).pipe( + getDownloadableBitstream(this.authorizationService), + switchMap((bit: Bitstream) => { + if (hasValue(bit)) { + const item = this.currentObject.value as Item; + return getBitstreamRoute(item, bit); + } else { + // Otherwise check all bitstreams to see if one matches the format whitelist + return this.getFirstAllowedFormatBitstreamLink(bitstreamRd); + } + }), + ); + } + + protected getFirstAllowedFormatBitstreamLink(bitstreamRd: RemoteData>): Observable { + const item = this.currentObject.value as Item; + if (hasValue(bitstreamRd.payload) && isNotEmpty(bitstreamRd.payload.page)) { + // Retrieve the formats of all bitstreams in the page sequentially + return observableConcat( + ...bitstreamRd.payload.page.map((bitstream: Bitstream) => bitstream.format.pipe( + getFirstSucceededRemoteDataPayload(), + // Keep the original bitstream, because it, not the format, is what we'll need + // for the link at the end + map((format: BitstreamFormat) => [bitstream, format]), + )), + ).pipe( + // Verify that the bitstream is downloadable + mergeMap(([bitstream, format]: [Bitstream, BitstreamFormat]) => observableOf(bitstream).pipe( + getDownloadableBitstream(this.authorizationService), + map((bit: Bitstream) => [bit, format]), + )), + // Filter out only pairs with whitelisted formats and non-null bitstreams, null from download check + filter(([bitstream, format]: [Bitstream, BitstreamFormat]) => + hasValue(format) && hasValue(bitstream) && this.CITATION_PDF_URL_MIMETYPES.includes(format.mimetype)), + // We only need 1 + take(1), + // Emit the link of the match + // tap((v) => console.log('result', v)), + mergeMap(([bitstream ]: [Bitstream, BitstreamFormat]) => getBitstreamRoute(item, bitstream)), + ); + } else { + return EMPTY; + } + } /** * Add to the @@ -420,9 +435,9 @@ export class HeadTagSEDICIService extends HeadTagService { this.setMetaTags('DC.rights', ['sedici.rights.uri'], 'DCTERMS.URI'); } - protected setProvenanceTags(): void { - this.setMetaTags('DCTERMS.provenance', ['dc.description.provenance']); - } + // protected setProvenanceTags(): void { + // this.setMetaTags('DCTERMS.provenance', ['dc.description.provenance']); + // } protected setContributorTags(): void { this.setMetaTags('DC.contributor', ['sedici.contributor.director', 'sedici.contributor.codirector', 'sedici.contributor.colaborator', 'sedici.contributor.translator', 'sedici.contributor.juror', 'sedici.contributor.inscriber']); diff --git a/src/app/core/metadata/head-tag.service.ts b/src/app/core/metadata/head-tag.service.ts index cf554557382..efc5ecbfb8c 100644 --- a/src/app/core/metadata/head-tag.service.ts +++ b/src/app/core/metadata/head-tag.service.ts @@ -101,7 +101,7 @@ export class HeadTagService { * See {@linkcode getFirstAllowedFormatBitstreamLink} * @private */ - private readonly CITATION_PDF_URL_MIMETYPES = [ + protected readonly CITATION_PDF_URL_MIMETYPES = [ 'application/pdf', // .pdf 'application/postscript', // .ps 'application/msword', // .doc