Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show DOI in the refbox if the Item has some #540

Merged
merged 1 commit into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<span>{{citationText + ', '}}</span>
<span><i>{{itemNameText + ', '}}</i></span>
<span>{{repositoryNameText + ', '}}</span>
<span><a [href]="identifierURI">{{identifierURI}}</a></span>
<span><a [href]="identifierURI">{{prettifiedIdentifier | async}}</a></span>
</div>
<div class="clarin-ref-box-copy-wrapper" placement="bottom" [ngbTooltip]="'Copied!'" #tooltip="ngbTooltip"
(click)="copyText()">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import { GetRequest } from '../../core/data/request.models';
import { RequestService } from '../../core/data/request.service';
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { BehaviorSubject } from 'rxjs';
import {
DOI_METADATA_FIELD, HANDLE_METADATA_FIELD,
} from '../simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component';
import { ItemIdentifierService } from '../../shared/item-identifier.service';

/**
* If the item has more authors do not add all authors to the citation but add there a shortcut.
Expand Down Expand Up @@ -57,14 +62,23 @@ export class ClarinRefCitationComponent implements OnInit {
* The nam of the organization which provides the repository
*/
repositoryNameText: string;
/**
* BehaviorSubject to store the prettified identifier.
*/
prettifiedIdentifier: BehaviorSubject<string> = new BehaviorSubject<string>(null);
/**
* The item has DOI or not.
*/
hasDoi = false;

constructor(private configurationService: ConfigurationDataService,
private clipboard: Clipboard,
public config: NgbTooltipConfig,
private modalService: NgbModal,
private requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected halService: HALEndpointService,) {
protected halService: HALEndpointService,
private itemIdentifierService: ItemIdentifierService) {
// Configure the tooltip to show on click - `Copied` message
config.triggers = 'click';
}
Expand All @@ -79,10 +93,15 @@ export class ClarinRefCitationComponent implements OnInit {
return textValue !== null;
});

this.hasDoi = this.hasItemDoi();
this.citationText = citationArray.join(', ');
this.itemNameText = this.getTitle();
this.identifierURI = this.getIdentifierUri();
this.getRepositoryName().then(res => {
this.identifierURI = this.getIdentifierUri(this.whichIdentifierMetadataField());
void this.itemIdentifierService.prettifyIdentifier(this.identifierURI, [this.whichIdentifierMetadataField()])
.then((value: string) => {
this.prettifiedIdentifier.next(value);
});
void this.getRepositoryName().then(res => {
this.repositoryNameText = res?.payload?.values?.[0];
});
}
Expand All @@ -104,18 +123,30 @@ export class ClarinRefCitationComponent implements OnInit {
.pipe(getFirstSucceededRemoteData()).toPromise();
}

getIdentifierUri() {
const handleMetadata = this.item.metadata['dc.identifier.uri'];
if (isUndefined(handleMetadata) || isNull(handleMetadata)) {
return null;
}
/**
* Get the identifier URI from the item metadata. If the item has DOI, return the DOI, otherwise return the handle.
*/
getIdentifierUri(identifierMetadataField) {
return this.item.firstMetadataValue(identifierMetadataField);
}

/**
* Check if the item has DOI.
*/
hasItemDoi() {
return this.item?.allMetadata(DOI_METADATA_FIELD)?.length > 0;
}

return handleMetadata?.[0]?.value;
/**
* If the item has DOI, return the DOI metadata field, otherwise return the handle metadata field.
*/
whichIdentifierMetadataField() {
return this.hasDoi ? DOI_METADATA_FIELD : HANDLE_METADATA_FIELD;
}

getHandle() {
// Separate the handle from the full URI
const fullUri = this.getIdentifierUri();
const fullUri = this.getIdentifierUri(this.whichIdentifierMetadataField());
const handleWord = 'handle/';
const startHandleIndex = fullUri.indexOf('handle/') + handleWord.length;
return fullUri.substr(startHandleIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { getFirstSucceededRemoteDataPayload } from '../../../../core/shared/oper
import { map } from 'rxjs/operators';

export const DOI_METADATA_FIELD = 'dc.identifier.doi';
export const HANDLE_METADATA_FIELD = 'dc.identifier.uri';
const SHOW_HANDLE_AND_DOI_PROPERTY_NAME = 'item-page.show-handle-and-doi';
const HANDLE_METADATA_FIELD = 'dc.identifier.uri';

@Component({
selector: 'ds-clarin-generic-item-field',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ClarinIdentifierItemFieldComponent } from './clarin-identifier-item-field.component';
import { ConfigurationDataService } from '../../../../core/data/configuration-data.service';
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import { ConfigurationProperty } from '../../../../core/shared/configuration-property.model';
import { Item } from '../../../../core/shared/item.model';
import { createPaginatedList } from '../../../../shared/testing/utils.test';
import { DOI_METADATA_FIELD } from '../clarin-generic-item-field/clarin-generic-item-field.component';
import { ItemIdentifierService } from '../../../../shared/item-identifier.service';

describe('ClarinIdentifierItemFieldComponent', () => {
let component: ClarinIdentifierItemFieldComponent;
let fixture: ComponentFixture<ClarinIdentifierItemFieldComponent>;
let itemIdentifierService: ItemIdentifierService;

const mockItem: Item = Object.assign(new Item(), {
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
Expand All @@ -25,13 +25,8 @@ describe('ClarinIdentifierItemFieldComponent', () => {
});

beforeEach(async () => {
const configurationServiceSpy = jasmine.createSpyObj('configurationService', {
findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), {
name: 'test',
values: [
true
]
})),
itemIdentifierService = jasmine.createSpyObj('itemIdentifierService', {
prettifyIdentifier: new Promise((res, rej) => { return 'awesome identifier'; }),
});

await TestBed.configureTestingModule({
Expand All @@ -40,7 +35,7 @@ describe('ClarinIdentifierItemFieldComponent', () => {
],
declarations: [ ClarinIdentifierItemFieldComponent ],
providers: [
{ provide: ConfigurationDataService, useValue: configurationServiceSpy }
{ provide: ItemIdentifierService, useValue: itemIdentifierService }
]
})
.compileComponents();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { Item } from '../../../../core/shared/item.model';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { BehaviorSubject } from 'rxjs';
import { Clipboard } from '@angular/cdk/clipboard';
import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
import { ConfigurationDataService } from '../../../../core/data/configuration-data.service';
import { isEmpty, isNotEmpty } from '../../../../shared/empty.util';
import { getFirstCompletedRemoteData } from '../../../../core/shared/operators';
import { map } from 'rxjs/operators';
import { DOI_METADATA_FIELD } from '../clarin-generic-item-field/clarin-generic-item-field.component';
import { ItemIdentifierService } from '../../../../shared/item-identifier.service';

const DEFAULT_DOI_RESOLVER = 'https://doi.org/';
const DOI_RESOLVER_CFG_PROPERTY = 'identifier.doi.resolver';

@Component({
selector: 'ds-clarin-identifier-item-field',
Expand All @@ -34,11 +28,6 @@ export class ClarinIdentifierItemFieldComponent implements OnInit {
*/
@Input() fields: string[];

/**
* The DOI resolver URL. It is a `identifier.doi.resolver` property or the default resolver (constant).
*/
doiResolver: string;

/**
* The identifier of the item. DOI or handle.
*/
Expand All @@ -49,16 +38,18 @@ export class ClarinIdentifierItemFieldComponent implements OnInit {
*/
prettifiedIdentifier: BehaviorSubject<string> = new BehaviorSubject<string>(null);

constructor(private configurationService: ConfigurationDataService,
constructor(private itemIdentifierService: ItemIdentifierService,
private clipboard: Clipboard) {
}

ngOnInit(): void {
this.identifier = this.item?.firstMetadataValue(this.fields);
void this.prettifyIdentifier(this.identifier);
void this.itemIdentifierService.prettifyIdentifier(this.identifier, this.fields)
.then((value: string) => {
this.prettifiedIdentifier.next(value);
});
}


/**
* Copy the metadata value to the clipboard. After clicking on the `Copy` icon the message `Copied` is popped up.
*
Expand All @@ -70,56 +61,4 @@ export class ClarinIdentifierItemFieldComponent implements OnInit {
this.copyButtonRef.close();
}, 700);
}

/**
* Prettify the identifier. If the identifier is DOI, remove the DOI resolver from it.
*
* @param identifier
*/
async prettifyIdentifier(identifier: string) {
if (isEmpty(identifier)) {
this.prettifiedIdentifier.next(null);
return;
}

// Do not prettify the identifier if it is not DOI.
if (!this.fields.includes(DOI_METADATA_FIELD)) {
this.prettifiedIdentifier.next(identifier);
return;
}

// If the DOI resolver is set, use it. It is not set by default.
if (isNotEmpty(this.doiResolver)) {
this.removeDoiResolverFromIdentifier(identifier);
return;
}

// Get the DOI resolver from the configuration.
const cfgDoiResolver = await firstValueFrom(this.loadDoiResolverConfiguration());

// If the configuration is not set, use the default resolver.
this.doiResolver = isEmpty(cfgDoiResolver) ? DEFAULT_DOI_RESOLVER : cfgDoiResolver;

// Remove the DOI resolver from the identifier.
this.removeDoiResolverFromIdentifier(identifier);
}

/**
* Remove the DOI resolver from the identifier.
*
* @param identifier
*/
removeDoiResolverFromIdentifier(identifier: string) {
this.prettifiedIdentifier.next(identifier.replace(this.doiResolver, ''));
}

/**
* Load the DOI resolver from the configuration. It is a `identifier.doi.resolver` property.
*/
loadDoiResolverConfiguration() {
return this.configurationService.findByPropertyName(DOI_RESOLVER_CFG_PROPERTY)
.pipe(
getFirstCompletedRemoteData(),
map((config) => config?.payload?.values?.[0]));
}
}
68 changes: 68 additions & 0 deletions src/app/shared/item-identifier.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Injectable } from '@angular/core';
import { ConfigurationDataService } from '../core/data/configuration-data.service';
import { isEmpty } from './empty.util';
import { firstValueFrom } from 'rxjs';
import {
DOI_METADATA_FIELD
} from '../item-page/simple/field-components/clarin-generic-item-field/clarin-generic-item-field.component';
import { getFirstCompletedRemoteData } from '../core/shared/operators';
import { map } from 'rxjs/operators';

export const DEFAULT_DOI_RESOLVER = 'https://doi.org/';
export const DOI_RESOLVER_CFG_PROPERTY = 'identifier.doi.resolver';

/**
* Service to handle the item identifier. It prettifies the identifier and removes the DOI resolver from it.
*/
@Injectable()
export class ItemIdentifierService {
constructor(private configurationService: ConfigurationDataService) {
}

/**
* Prettify the identifier. If the identifier is DOI, remove the DOI resolver from it.
*
* @param identifier
* @param metadataFields
*/
async prettifyIdentifier(identifier: string, metadataFields: string[] = []) {
let prettifiedIdentifier = identifier;
if (isEmpty(identifier)) {
return prettifiedIdentifier;
}

// Do not prettify the identifier if it is not DOI.
if (!metadataFields?.includes(DOI_METADATA_FIELD)) {
return prettifiedIdentifier;
}

// Get the DOI resolver from the configuration.
const cfgDoiResolver = await firstValueFrom(this.loadDoiResolverConfiguration());

// If the configuration is not set, use the default resolver.
const doiResolver = isEmpty(cfgDoiResolver) ? DEFAULT_DOI_RESOLVER : cfgDoiResolver;

// Remove the DOI resolver from the identifier.
return this.removeDoiResolverFromIdentifier(identifier, doiResolver);
}

/**
* Remove the DOI resolver from the identifier.
*
* @param identifier
* @param doiResolver
*/
removeDoiResolverFromIdentifier(identifier: string, doiResolver: string) {
return identifier.replace(doiResolver, '');
}

/**
* Load the DOI resolver from the configuration. It is a `identifier.doi.resolver` property.
*/
loadDoiResolverConfiguration() {
return this.configurationService.findByPropertyName(DOI_RESOLVER_CFG_PROPERTY)
.pipe(
getFirstCompletedRemoteData(),
map((config) => config?.payload?.values?.[0]));
}
}
4 changes: 3 additions & 1 deletion src/app/shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ import { HtmlContentService } from './html-content.service';
import { ClarinSafeHtmlPipe } from './utils/clarin-safehtml.pipe';
import { ReplacePipe } from './utils/replace.pipe';
import { ClarinDateService } from './clarin-date.service';
import { ItemIdentifierService } from './item-identifier.service';

const MODULES = [
CommonModule,
Expand Down Expand Up @@ -495,7 +496,8 @@ const PROVIDERS = [
MockAdminGuard,
AbstractTrackableComponent,
HtmlContentService,
ClarinDateService
ClarinDateService,
ItemIdentifierService
];

const DIRECTIVES = [
Expand Down
Loading