Skip to content

Commit

Permalink
Merge pull request #8 from mlibrary/issue-working7
Browse files Browse the repository at this point in the history
add altmetrics and more.
  • Loading branch information
blancoj authored May 6, 2024
2 parents 77a1d48 + ca86b4d commit c7af4f7
Show file tree
Hide file tree
Showing 35 changed files with 764 additions and 111 deletions.
3 changes: 3 additions & 0 deletions config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ browseBy:
# settings menu.
pageSize: 20

# Show the Altmetric badge in the simple item page.
showAltmetricBadge: true

communityList:
# No. of communities to list per expansion (show more)
pageSize: 20
Expand Down
2 changes: 2 additions & 0 deletions config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ rest:
host: api7.dspace.org
port: 443
nameSpace: /server
item:
showAltmetricBadge: true
16 changes: 15 additions & 1 deletion src/app/core/forward-client-ip/forward-client-ip.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ export class ForwardClientIpInterceptor implements HttpInterceptor {
*/
intercept(httpRequest: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const clientIp = this.req.get('x-forwarded-for') || this.req.connection.remoteAddress;
return next.handle(httpRequest.clone({ setHeaders: { 'X-Forwarded-For': clientIp } }));
// return next.handle(httpRequest.clone({ setHeaders: { 'X-Forwarded-For': clientIp } }));

// This came from here: https://github.com/DSpace/dspace-angular/issues/2902
// UM Change. I was having issues getting the referer, aske Tim about it and
// he pointed me to this. This is not solve the issue, but seems like it would
// be good to have anyway
const headers = { 'X-Forwarded-For': clientIp };

// if the request has a user-agent retain it
const userAgent = this.req.get('user-agent');
if (userAgent) {
headers['User-Agent'] = userAgent;
}

return next.handle(httpRequest.clone({ setHeaders: headers }));
}
}
22 changes: 22 additions & 0 deletions src/app/item-page/item-page.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,17 @@ import {
ThemedFullFileSectionComponent
} from './full/field-components/file-section/themed-full-file-section.component';

import { ItemPageAltmetricFieldComponent } from './simple/field-components/specific-field/metrics/altmetric/item-page-altmetric-field.component';
import { AltmetricDirective } from './simple/field-components/specific-field/metrics/altmetric/item-page-altmetric-field.directive';
import { ItemPageMetricsFieldComponent } from './simple/field-components/specific-field/metrics/item-page-metrics-field.component';

import { ItemPageDimensionsFieldComponent } from './simple/field-components/specific-field/metrics-dimensions/dimensions/item-page-dimensions-field.component';
import { DimensionsDirective } from './simple/field-components/specific-field/metrics-dimensions/dimensions/item-page-dimensions-field.directive';
import { ItemPageMetricsDimensionsFieldComponent } from './simple/field-components/specific-field/metrics-dimensions/item-page-metrics-dimensions-field.component';




const ENTRY_COMPONENTS = [
// put only entry components that use custom decorator
PublicationComponent,
Expand Down Expand Up @@ -103,6 +114,15 @@ const DECLARATIONS = [
ItemAlertsComponent,
ThemedItemAlertsComponent,
BitstreamRequestACopyPageComponent,
ItemPageMetricsFieldComponent,
ItemPageAltmetricFieldComponent,
ItemPageMetricsDimensionsFieldComponent,
ItemPageDimensionsFieldComponent,
];

const DIRECTIVES = [
AltmetricDirective,
DimensionsDirective,
];

@NgModule({
Expand All @@ -124,10 +144,12 @@ const DECLARATIONS = [
],
declarations: [
...DECLARATIONS,
...DIRECTIVES,

],
exports: [
...DECLARATIONS,
...DIRECTIVES,
]
})
export class ItemPageModule {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<span
dsDimensionsData
[item]="item"
class="__dimensions_badge_embed__"
data-hide-zero-citations="true"
data-style="large_rectangle"
></span>


Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { AfterViewInit, Component, EventEmitter, HostListener, Inject, Input, Output } from '@angular/core';
import { ExternalScriptLoaderDimensionsService } from 'src/app/shared/utils/scripts-loader-dimensions/external-script-loader-dimensions.service';
import {
ExternalScriptsNames,
ExternalScriptsStatus,
} from 'src/app/shared/utils/scripts-loader-dimensions/external-script.model';
import { Item } from '../../../../../../core/shared/item.model';
import { APP_CONFIG, AppConfig } from 'src/config/app-config.interface';

@Component({
selector: 'ds-item-page-dimensions-field',
templateUrl: './item-page-dimensions-field.component.html',
})
export class ItemPageDimensionsFieldComponent implements AfterViewInit {
@Input() item: Item;

@Output() widgetLoaded = new EventEmitter<boolean>();

constructor(
@Inject(APP_CONFIG) protected appConfig: AppConfig,
private scriptLoader: ExternalScriptLoaderDimensionsService
) {}

ngAfterViewInit() {
if (!this.appConfig.item.showAltmetricBadge) {
return;
}
this.scriptLoader
.load(ExternalScriptsNames.DIMENSIONS)
.then((data) => this.reloadBadge(data))
.catch((error) => console.error(error));
}

/**
* We ensure that the badge is visible after the script is loaded
* @param data The data returned from the promise
*/
// private reloadBadge(data: any[]) {
// if (data.find((element) => this.isLoaded(element))) {
// const initMethod = '__dimensions_embed_installed__';
// window[initMethod]();
// }
// }

// I got this method from the community. The init method has to be different.
private reloadBadge(data: any[]) {
if (data.find((element) => this.isLoaded(element))) {
const initClass = '__dimensions_embed';
const initMethod = 'addBadges';
window[initClass][initMethod]();
}
}


/**
* Check if the script has been previously loaded in the DOM
* @param element The resolve element from the promise
* @returns true if the script has been already loaded, false if not
*/
private isLoaded(element: any): unknown {
return (
element.script === ExternalScriptsNames.DIMENSIONS &&
element.status === ExternalScriptsStatus.ALREADY_LOADED
);
}

@HostListener('window:altmetric:show', ['$event'])
private onWidgetShow(event: Event) {
this.widgetLoaded.emit(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core';
import { Item } from 'src/app/core/shared/item.model';

/**
* This directive adds the data-* attribute for the Altmetric badge dependening on the first identifier found in the item
*/
@Directive({
selector: '[dsDimensionsData]',
})
export class DimensionsDirective implements OnInit {
@Input() item: Item;

constructor(private renderer: Renderer2, private elementRef: ElementRef) {}

ngOnInit(): void {
const identifier = this.obtainFirstValidID(this.initItemIdentifiers());
if (identifier !== undefined) {
this.renderer.setAttribute(
this.elementRef.nativeElement,
identifier.name,
this.applyRegex(identifier.value, identifier.regex)
);
}
}

/**
* This initialize an array of identifiers founded in the item.
* It search for DOI, Handle, PMID, ISBN, ARXIV and URI.
* Some identifiers may be stored with more text than the ID so this objects has a regex property to clean it
*/
private initItemIdentifiers(): any[] {
return [
{
name: 'data-doi',
value: this.item.firstMetadataValue('dc.identifier.doi'),
regex: /https?:\/\/(dx\.)?doi\.org\//gi,
},
{
name: 'data-handle',
value: this.item.firstMetadataValue('dc.identifier.uri'),
regex: /http?:\/\/hdl\.handle\.net\//gi,
},
{
name: 'data-pmid',
value: this.item.firstMetadataValue('dc.identifier.pmid'),
regex: '',
},
{
name: 'data-isbn',
value: this.item.firstMetadataValue('dc.identifier.isbn'),
regex: '',
},
{
name: 'data-arxiv-id',
value: this.item.firstMetadataValue('dc.identifier.arxiv'),
regex: '',
},
{
name: 'data-uri',
value: this.item.firstMetadataValue('dc.identifier.uri'),
regex: '',
},
];
}

/**
* This function obtains the first valid ID from the item
* @returns Returns first valid identifier (not undefined), undefined otherwise
*/
private obtainFirstValidID(itemIdentifiers: any[]): any {
return itemIdentifiers.find((element) => element.value !== undefined);
}

/**
* Apply the specified regex to clean the metadata and obtain only the ID
* @param value The metadata value
* @param regex The regex to apply
* @returns The result is the ID clean
*/
private applyRegex(value: string, regex: string): string {
return value.replace(regex, '');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="item-page-field">
<ds-metadata-field-wrapper [hideIfNoTextContent]="false">
<div class="simple-view-element">
<!-- <h2 *ngIf="showTitle" class="simple-view-element-header">
{{ "item.page.metrics" | translate }}
</h2> -->
<div class="simple-view-element-body">
<ds-item-page-dimensions-field (widgetLoaded)="someWidgetHasLoaded($event)" [item]="item"></ds-item-page-dimensions-field>
</div>
</div>
</ds-metadata-field-wrapper>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Component, Input } from '@angular/core';
import { ItemPageFieldComponent } from '../item-page-field.component';
import { Item } from 'src/app/core/shared/item.model';

@Component({
selector: 'ds-item-page-metrics-dimensions-field',
templateUrl: './item-page-metrics-dimensions-field.component.html',
styleUrls: [
'../../../../../shared/metadata-field-wrapper/metadata-field-wrapper.component.scss',
],
})
export class ItemPageMetricsDimensionsFieldComponent extends ItemPageFieldComponent {

@Input() item: Item;

public showTitle = false;

public someWidgetHasLoaded(widgetLoaded: boolean) {
if (widgetLoaded) {
this.showTitle = true;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<span
dsAltmetricData
[item]="item"
class="altmetric-embed"
data-style="large_rectangle"
data-hide-no-mentions="true"
data-badge-type="1"
data-badge-popover="right"
data-link-target="_blank"
></span>
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { AfterViewInit, Component, EventEmitter, HostListener, Inject, Input, Output } from '@angular/core';
import { ExternalScriptLoaderService } from 'src/app/shared/utils/scripts-loader/external-script-loader.service';
import {
ExternalScriptsNames,
ExternalScriptsStatus,
} from 'src/app/shared/utils/scripts-loader/external-script.model';
import { Item } from '../../../../../../core/shared/item.model';
import { APP_CONFIG, AppConfig } from 'src/config/app-config.interface';

@Component({
selector: 'ds-item-page-altmetric-field',
templateUrl: './item-page-altmetric-field.component.html',
})
export class ItemPageAltmetricFieldComponent implements AfterViewInit {
@Input() item: Item;

@Output() widgetLoaded = new EventEmitter<boolean>();

constructor(
@Inject(APP_CONFIG) protected appConfig: AppConfig,
private scriptLoader: ExternalScriptLoaderService
) {}

ngAfterViewInit() {
if (!this.appConfig.item.showAltmetricBadge) {
return;
}

this.scriptLoader
.load(ExternalScriptsNames.ALTMETRIC)
.then((data) => this.reloadBadge(data))
.catch((error) => console.error(error));
}

/**
* We ensure that the badge is visible after the script is loaded
* @param data The data returned from the promise
*/
private reloadBadge(data: any[]) {
if (data.find((element) => this.isLoaded(element))) {
const initMethod = '_altmetric_embed_init';
window[initMethod]();
}
}

/**
* Check if the script has been previously loaded in the DOM
* @param element The resolve element from the promise
* @returns true if the script has been already loaded, false if not
*/
private isLoaded(element: any): unknown {
return (
element.script === ExternalScriptsNames.ALTMETRIC &&
element.status === ExternalScriptsStatus.ALREADY_LOADED
);
}

@HostListener('window:altmetric:show', ['$event'])
private onWidgetShow(event: Event) {
this.widgetLoaded.emit(true);
}
}
Loading

0 comments on commit c7af4f7

Please sign in to comment.