@@ -29,8 +29,6 @@ import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from
2929import { RootDataService } from '../data/root-data.service' ;
3030import { getBitstreamDownloadRoute } from '../../app-routing-paths' ;
3131import { BundleDataService } from '../data/bundle-data.service' ;
32- import { followLink } from '../../shared/utils/follow-link-config.model' ;
33- import { Bundle } from '../shared/bundle.model' ;
3432import { PaginatedList } from '../data/paginated-list.model' ;
3533import { URLCombiner } from '../url-combiner/url-combiner' ;
3634import { HardRedirectService } from '../services/hard-redirect.service' ;
@@ -46,6 +44,7 @@ import { SchemaJsonLDService } from './schema-json-ld/schema-json-ld.service';
4644import { ITEM } from '../shared/item.resource-type' ;
4745import { isPlatformServer } from '@angular/common' ;
4846import { Root } from '../data/root.model' ;
47+ import { environment } from '../../../environments/environment' ;
4948
5049/**
5150 * The base selector function to select the metaTag section in the store
@@ -84,6 +83,9 @@ export class MetadataService {
8483 'application/epub+zip' , // .epub
8584 ] ;
8685
86+ private fallbackImagePath = environment . metaTags . defaultLogo ;
87+ private defaultPageDescription = environment . metaTags . defaultDescription ;
88+
8789 constructor (
8890 private router : Router ,
8991 private translate : TranslateService ,
@@ -154,6 +156,10 @@ export class MetadataService {
154156 }
155157 }
156158
159+ if ( ! hasValue ( routeInfo . data . value . dso ) ) {
160+ this . setGenericPageMetaTags ( ) ;
161+ }
162+
157163
158164 }
159165
@@ -165,10 +171,25 @@ export class MetadataService {
165171 }
166172
167173 private setDSOMetaTags ( ) : void {
174+ const openGraphType = this . getOpenGraphType ( ) ;
168175
169176 this . setTitleTag ( ) ;
170177 this . setDescriptionTag ( ) ;
171178
179+ this . setOpenGraphTitleTag ( ) ;
180+ this . setOpenGraphDescriptionTag ( ) ;
181+ this . setOpenGraphImageTag ( ) ;
182+ this . setOpenGraphUrlTag ( ) ;
183+
184+ if ( openGraphType ) {
185+ this . setOpenGraphTypeTag ( openGraphType ) ;
186+ }
187+
188+ this . setTwitterTitleTag ( ) ;
189+ this . setTwitterDescriptionTag ( ) ;
190+ this . setTwitterImageTag ( ) ;
191+ this . setTwitterSummaryCardTag ( ) ;
192+
172193 if ( ! this . isResearchOutput ( ) ) {
173194 return ;
174195 }
@@ -202,31 +223,23 @@ export class MetadataService {
202223 if ( this . isTechReport ( ) ) {
203224 this . setCitationTechnicalReportNumberTag ( ) ;
204225 }
205-
206- this . setOpenGraphTitleTag ( ) ;
207- this . setOpenGraphDescriptionTag ( ) ;
208- this . setOpenGraphImageTag ( ) ;
209-
210- this . setTwitterTitleTag ( ) ;
211- this . setTwitterDescriptionTag ( ) ;
212- this . setTwitterImageTag ( ) ;
213226 }
214227
215228 /**
216229 * Add <meta name="title" ... > to the <head>
217230 */
218- private setTitleTag ( ) : void {
219- const value = this . dsoNameService . getName ( this . currentObject . getValue ( ) ) ;
231+ private setTitleTag ( title ?: string ) : void {
232+ const value = title ?? this . dsoNameService . getName ( this . currentObject . getValue ( ) ) ;
220233 this . addMetaTag ( 'title' , value ) ;
221234 this . title . setTitle ( value ) ;
222235 }
223236
224237 /**
225238 * Add <meta name="description" ... > to the <head>
226239 */
227- private setDescriptionTag ( ) : void {
240+ private setDescriptionTag ( description ?: string ) : void {
228241 // TODO: truncate abstract
229- const value = this . getMetaTagValue ( 'dc.description.abstract' ) ;
242+ const value = description ?? this . getMetaTagValue ( 'dc.description.abstract' ) ;
230243 this . addMetaTag ( 'description' , value ) ;
231244 }
232245
@@ -407,122 +420,125 @@ export class MetadataService {
407420 * Add <meta name="citation_pdf_url" ... > to the <head>
408421 */
409422 private setCitationPdfUrlTag ( ) : void {
410- this . setPrimaryBitstreamInBundleTag ( 'ORIGINAL' , 'citation_pdf_url' ) ;
423+ this . setPrimaryBitstreamInBundleTag ( 'citation_pdf_url' ) ;
424+ }
425+
426+ /**
427+ * Add <meta name="og:type" ... > to the <head>
428+ */
429+ private setOpenGraphTypeTag ( type : string ) : void {
430+ this . addMetaTag ( 'og:type' , type ) ;
411431 }
412432
413433 /**
414434 * Add <meta name="og:title" ... > to the <head>
415435 */
416- private setOpenGraphTitleTag ( ) : void {
417- const value = this . getMetaTagValue ( 'dc.title' ) ;
436+ private setOpenGraphTitleTag ( title ?: string ) : void {
437+ const value = title ?? this . getMetaTagValue ( 'dc.title' ) ;
418438 this . addMetaTag ( 'og:title' , value ) ;
419439 }
420440
421441 /**
422442 * Add <meta name="og:description" ... > to the <head>
423443 */
424- private setOpenGraphDescriptionTag ( ) : void {
444+ private setOpenGraphDescriptionTag ( description ?: string ) : void {
425445 // TODO: truncate abstract
426- const value = this . getMetaTagValue ( 'dc.description.abstract' ) ;
446+ const value = description ?? this . getMetaTagValue ( 'dc.description.abstract' ) ?? this . translate . instant ( 'meta.tag.missing.description ') ;
427447 this . addMetaTag ( 'og:description' , value ) ;
428448 }
429449
430450 /**
431451 * Add <meta name="og:image" ... > to the <head>
432452 */
433453 private setOpenGraphImageTag ( ) : void {
434- this . setPrimaryBitstreamInBundleTag ( 'THUMBNAIL' , ' og:image') ;
454+ this . setPrimaryBitstreamInBundleTag ( 'og:image' ) ;
435455 }
436456
457+ /**
458+ * Add <meta name="og:url" ... > to the <head>
459+ */
460+ private setOpenGraphUrlTag ( url ?: string ) : void {
461+ const value = url ?? this . getMetaTagValue ( 'dc.identifier.uri' ) ;
462+ this . addMetaTag ( 'og:url' , value ) ;
463+ }
464+
437465
438466 /**
439467 * Add <meta name="twitter:title" ... > to the <head>
440468 */
441- private setTwitterTitleTag ( ) : void {
442- const value = this . getMetaTagValue ( 'dc.title' ) ;
469+ private setTwitterTitleTag ( title ?: string ) : void {
470+ const value = title ?? this . getMetaTagValue ( 'dc.title' ) ;
443471 this . addMetaTag ( 'twitter:title' , value ) ;
444472 }
445473
446474 /**
447475 * Add <meta name="twitter:description" ... > to the <head>
448476 */
449- private setTwitterDescriptionTag ( ) : void {
477+ private setTwitterDescriptionTag ( description ?: string ) : void {
450478 // TODO: truncate abstract
451- const value = this . getMetaTagValue ( 'dc.description.abstract' ) ;
479+ const value = description ?? this . getMetaTagValue ( 'dc.description.abstract' ) ?? this . translate . instant ( 'meta.tag.missing.description ') ;
452480 this . addMetaTag ( 'twitter:description' , value ) ;
453481 }
454482
455483 /**
456484 * Add <meta name="twitter:image" ... > to the <head>
457485 */
458486 private setTwitterImageTag ( ) : void {
459- this . setPrimaryBitstreamInBundleTag ( 'THUMBNAIL' , ' twitter:image') ;
487+ this . setPrimaryBitstreamInBundleTag ( 'twitter:image' ) ;
460488 }
461489
462- private setPrimaryBitstreamInBundleTag ( bundleName : string , tag : string ) : void {
490+ /**
491+ * Add <meta name="twitter:card" ... > to the <head>
492+ */
493+ private setTwitterSummaryCardTag ( ) : void {
494+ this . addMetaTag ( 'twitter:card' , 'summary' ) ;
495+ }
496+
497+ /**
498+ * Get bitstream from item thumbnail link
499+ *
500+ * @param item
501+ * @private
502+ */
503+ private getBitstreamFromThumbnail ( item : Item ) : Observable < Bitstream > {
504+ return item . thumbnail . pipe (
505+ getFirstCompletedRemoteData ( ) ,
506+ map ( ( thumbnailRD ) => {
507+ if ( thumbnailRD . hasSucceeded && isNotEmpty ( thumbnailRD . payload ) ) {
508+ return thumbnailRD . payload ;
509+ } else {
510+ return null ;
511+ }
512+ } ) ,
513+ getDownloadableBitstream ( this . authorizationService )
514+ ) ;
515+ }
516+
517+ private setPrimaryBitstreamInBundleTag ( tag : string ) : void {
463518 if ( this . currentObject . value instanceof Item ) {
464519 const item = this . currentObject . value as Item ;
465-
466- // Retrieve the bundle for the item
467- this . bundleDataService . findByItemAndName (
468- item ,
469- bundleName ,
470- true ,
471- true ,
472- followLink ( 'primaryBitstream' ) ,
473- followLink ( 'bitstreams' , {
474- findListOptions : {
475- // limit the number of bitstreams used to find the citation pdf url to the number
476- // shown by default on an item page
477- elementsPerPage : this . appConfig . item . bitstream . pageSize
478- }
479- } , followLink ( 'format' ) ) ,
480- ) . pipe (
481- getFirstSucceededRemoteDataPayload ( ) ,
482- switchMap ( ( bundle : Bundle ) =>
483- // First try the primary bitstream
484- bundle . primaryBitstream . pipe (
485- getFirstCompletedRemoteData ( ) ,
486- map ( ( rd : RemoteData < Bitstream > ) => {
487- if ( hasValue ( rd . payload ) ) {
488- return rd . payload ;
489- } else {
490- return null ;
491- }
492- } ) ,
493- getDownloadableBitstream ( this . authorizationService ) ,
494- // return the bundle as well so we can use it again if there's no primary bitstream
495- map ( ( bitstream : Bitstream ) => [ bundle , bitstream ] )
496- )
497- ) ,
498- switchMap ( ( [ bundle , primaryBitstream ] : [ Bundle , Bitstream ] ) => {
499- if ( hasValue ( primaryBitstream ) ) {
500- // If there was a downloadable primary bitstream, emit its link
501- return [ getBitstreamDownloadRoute ( primaryBitstream ) ] ;
520+ this . getBitstreamFromThumbnail ( item ) . pipe (
521+ map ( ( bitstream ) => {
522+ if ( hasValue ( bitstream ) ) {
523+ return getBitstreamDownloadRoute ( bitstream ) ;
502524 } else {
503- // Otherwise consider the regular bitstreams in the bundle
504- return bundle . bitstreams . pipe (
505- getFirstCompletedRemoteData ( ) ,
506- switchMap ( ( bitstreamRd : RemoteData < PaginatedList < Bitstream > > ) => {
507- if ( hasValue ( bitstreamRd . payload ) && bitstreamRd . payload . totalElements === 1 ) {
508- // If there's only one bitstream in the bundle, emit its link if its downloadable
509- return this . getBitLinkIfDownloadable ( bitstreamRd . payload . page [ 0 ] , bitstreamRd ) ;
510- } else {
511- // Otherwise check all bitstreams to see if one matches the format whitelist
512- return this . getFirstAllowedFormatBitstreamLink ( bitstreamRd ) ;
513- }
514- } )
515- ) ;
525+ return null ;
516526 }
517527 } ) ,
518528 take ( 1 )
519- ) . subscribe ( ( link : string ) => {
520- // Use the found link to set the <meta> tag
521- this . addMetaTag (
522- tag ,
523- new URLCombiner ( this . hardRedirectService . getCurrentOrigin ( ) , link ) . toString ( )
524- ) ;
529+ ) . subscribe ( ( link ) => {
530+ if ( hasValue ( link ) ) {
531+ // Use the found link to set the <meta> tag
532+ this . addMetaTag (
533+ tag ,
534+ new URLCombiner ( this . hardRedirectService . getCurrentOrigin ( ) , link ) . toString ( )
535+ ) ;
536+ } else {
537+ this . addFallbackImageToTag ( tag ) ;
538+ }
525539 } ) ;
540+ } else {
541+ this . addFallbackImageToTag ( tag ) ;
526542 }
527543 }
528544
@@ -686,5 +702,62 @@ export class MetadataService {
686702 } ) ;
687703 }
688704
705+ private addFallbackImageToTag ( tag : string ) {
706+ this . addMetaTag (
707+ tag ,
708+ new URLCombiner ( this . hardRedirectService . getCurrentOrigin ( ) , this . fallbackImagePath ) . toString ( )
709+ ) ;
710+ }
711+
712+ private getOpenGraphType ( ) : string {
713+ let type = '' ;
714+ if ( this . currentObject . value instanceof Item ) {
715+ const item = this . currentObject . value as Item ;
716+ switch ( item . entityType ) {
717+ case 'News' :
718+ type = 'article' ;
719+ break ;
720+ case 'Publication' :
721+ type = 'article' ;
722+ break ;
723+ case 'Book' :
724+ type = 'book' ;
725+ break ;
726+ case 'Person' :
727+ type = 'profile' ;
728+ break ;
729+ case 'Audio' :
730+ type = 'music' ;
731+ break ;
732+ case 'Video' :
733+ type = 'video' ;
734+ break ;
735+ default :
736+ break ;
737+ }
738+ }
739+ return type ;
740+ }
689741
742+
743+ private setGenericPageMetaTags ( ) {
744+ const pageDocumentTitle = document . getElementsByTagName ( 'title' ) [ 0 ] . innerText ;
745+ const pageUrl = new URLCombiner ( this . hardRedirectService . getCurrentOrigin ( ) , this . router . url ) . toString ( ) ;
746+ const genericPageOpenGraphType = 'website' ;
747+
748+ this . setTitleTag ( pageDocumentTitle ) ;
749+ this . setDescriptionTag ( this . defaultPageDescription ) ;
750+
751+ this . setOpenGraphTitleTag ( pageDocumentTitle ) ;
752+ this . setOpenGraphDescriptionTag ( this . defaultPageDescription ) ;
753+ this . setOpenGraphUrlTag ( pageUrl ) ;
754+ this . setOpenGraphImageTag ( ) ;
755+ this . setOpenGraphTypeTag ( genericPageOpenGraphType ) ;
756+
757+
758+ this . setTwitterTitleTag ( pageDocumentTitle ) ;
759+ this . setTwitterDescriptionTag ( this . defaultPageDescription ) ;
760+ this . setTwitterImageTag ( ) ;
761+ this . setTwitterSummaryCardTag ( ) ;
762+ }
690763}
0 commit comments