Skip to content

Commit 58d6d78

Browse files
FrancescoMolinaroDavide Negretti
authored andcommitted
Merged in DSC-1529-thumbnail-social-network (pull request DSpace#1578)
DSC-1529 thumbnail social network Approved-by: Davide Negretti
2 parents 69da781 + fd0f663 commit 58d6d78

8 files changed

Lines changed: 182 additions & 84 deletions

File tree

src/app/core/metadata/metadata.service.ts

Lines changed: 155 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from
2929
import { RootDataService } from '../data/root-data.service';
3030
import { getBitstreamDownloadRoute } from '../../app-routing-paths';
3131
import { BundleDataService } from '../data/bundle-data.service';
32-
import { followLink } from '../../shared/utils/follow-link-config.model';
33-
import { Bundle } from '../shared/bundle.model';
3432
import { PaginatedList } from '../data/paginated-list.model';
3533
import { URLCombiner } from '../url-combiner/url-combiner';
3634
import { HardRedirectService } from '../services/hard-redirect.service';
@@ -46,6 +44,7 @@ import { SchemaJsonLDService } from './schema-json-ld/schema-json-ld.service';
4644
import { ITEM } from '../shared/item.resource-type';
4745
import { isPlatformServer } from '@angular/common';
4846
import { 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
}

src/assets/i18n/en.json5

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7392,4 +7392,7 @@
73927392
"form.other-information.selection.data-crispj_coinvestigator_affiliation": "Affiliation",
73937393

73947394
"form.other-information.selection.alternative-names": "Select alternative name",
7395+
7396+
"meta.tag.missing.description": "No description available",
7397+
73957398
}

src/assets/i18n/es.json5

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11728,6 +11728,4 @@
1172811728
// "access-control-option-end-date-note": "Select the date until which the related access condition is applied",
1172911729
"access-control-option-end-date-note": "Escoja la fecha hasta la cuál se aplicarán las condiciones de acceso especificadas",
1173011730

11731-
11732-
1173311731
}
65 KB
Loading

src/config/app-config.interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { AttachmentRenderingConfig } from './attachment-rendering.config';
3333
import { SearchResultConfig } from './search-result-config.interface';
3434
import { MiradorConfig } from './mirador-config.interfaces';
3535
import { LoaderConfig } from './loader-config.interfaces';
36+
import { MetaTagsConfig } from './meta-tags.config';
3637

3738
interface AppConfig extends Config {
3839
ui: UIServerConfig;
@@ -72,6 +73,7 @@ interface AppConfig extends Config {
7273
searchResult: SearchResultConfig;
7374
mirador: MiradorConfig;
7475
loader: LoaderConfig;
76+
metaTags: MetaTagsConfig;
7577
}
7678

7779
/**

0 commit comments

Comments
 (0)