[TLC-249] Register DOI operation and button in item status page

This commit is contained in:
Kim Shepherd
2022-08-25 09:25:27 +12:00
parent a7586ca07a
commit 5f6e20eaa4
19 changed files with 549 additions and 20 deletions

View File

@@ -32,4 +32,5 @@ export enum FeatureID {
CanSynchronizeWithORCID = 'canSynchronizeWithORCID',
CanSubmit = 'canSubmit',
CanEditItem = 'canEditItem',
CanRegisterDOI = 'canRegisterDOI',
}

View File

@@ -0,0 +1,62 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { dataService } from '../cache/builders/build-decorators';
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
import { ObjectCacheService } from '../cache/object-cache.service';
import { HALEndpointService } from '../shared/hal-endpoint.service';
import { DataService } from './data.service';
import { RequestService } from './request.service';
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
import { CoreState } from '../core-state.model';
import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model';
import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type';
import { Observable } from 'rxjs';
import { RemoteData } from './remote-data';
import { Item } from '../shared/item.model';
import {IDENTIFIERS} from '../../shared/object-list/identifier-data/identifier-data.resource-type';
import {IdentifierData} from '../../shared/object-list/identifier-data/identifier-data.model';
import {getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload} from '../shared/operators';
import {map, startWith} from 'rxjs/operators';
import {ConfigurationProperty} from '../shared/configuration-property.model';
import {ConfigurationDataService} from './configuration-data.service';
@Injectable()
@dataService(IDENTIFIERS)
export class IdentifierDataService extends DataService<IdentifierData> {
protected linkPath = 'identifiers';
constructor(
protected comparator: DefaultChangeAnalyzer<IdentifierData>,
protected halService: HALEndpointService,
protected http: HttpClient,
protected notificationsService: NotificationsService,
protected objectCache: ObjectCacheService,
protected rdbService: RemoteDataBuildService,
protected requestService: RequestService,
protected store: Store<CoreState>,
private configurationService: ConfigurationDataService,
) {
super();
}
/**
* Returns {@link RemoteData} of {@link IdentifierData} representing identifiers for this item
* @param item Item we are querying
*/
getIdentifierDataFor(item: Item): Observable<RemoteData<IdentifierData>> {
return this.findByHref(item._links.identifiers.href, false, true);
}
/**
* Should we allow registration of new DOIs via the item status page?
*/
public getIdentifierRegistrationConfiguration(): Observable<string[]> {
return this.configurationService.findByPropertyName('identifiers.item-status.register').pipe(
getFirstCompletedRemoteData(),
map((propertyRD: RemoteData<ConfigurationProperty>) => propertyRD.hasSucceeded ? propertyRD.payload.values : [])
);
}
}

View File

@@ -232,6 +232,36 @@ export abstract class BaseItemDataService extends IdentifiableDataService<Item>
return this.rdbService.buildFromRequestUUID(requestId);
}
/**
* Get the endpoint for an item's bundles
* @param itemId
*/
public getIdentifiersEndpoint(itemId: string): Observable<string> {
return this.halService.getEndpoint(this.linkPath).pipe(
switchMap((url: string) => this.halService.getEndpoint('identifiers', `${url}/${itemId}`))
);
}
/**
* Register a DOI for a given item
* @param itemId
*/
public registerDOI(itemId: string): Observable<RemoteData<any>> {
const requestId = this.requestService.generateRequestId();
const hrefObs = this.getIdentifiersEndpoint(itemId);
hrefObs.pipe(
take(1)
).subscribe((href) => {
const options: HttpOptions = Object.create({});
let headers = new HttpHeaders();
headers = headers.append('Content-Type', 'application/json');
options.headers = headers;
const request = new PostRequest(requestId, href, JSON.stringify({}), options);
this.requestService.send(request);
});
return this.rdbService.buildFromRequestUUID(requestId);
}
/**
* Get the endpoint to move the item
* @param itemId

View File

@@ -24,6 +24,8 @@ import { Bitstream } from './bitstream.model';
import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type';
import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model';
import { HandleObject } from './handle-object.model';
import { IDENTIFIERS } from '../../shared/object-list/identifier-data/identifier-data.resource-type';
import { IdentifierData } from '../../shared/object-list/identifier-data/identifier-data.model';
/**
* Class representing a DSpace Item
@@ -76,6 +78,7 @@ export class Item extends DSpaceObject implements ChildHALResource, HandleObject
version: HALLink;
thumbnail: HALLink;
accessStatus: HALLink;
identifiers: HALLink;
self: HALLink;
};
@@ -121,6 +124,13 @@ export class Item extends DSpaceObject implements ChildHALResource, HandleObject
@link(ACCESS_STATUS)
accessStatus?: Observable<RemoteData<AccessStatusObject>>;
/**
* The identifier data for this Item
* Will be undefined unless the identifiers {@link HALLink} has been resolved.
*/
@link(IDENTIFIERS)
identifiers?: Observable<RemoteData<IdentifierData>>;
/**
* Method that returns as which type of object this object should be rendered
*/

View File

@@ -34,6 +34,9 @@ import { ItemAuthorizationsComponent } from './item-authorizations/item-authoriz
import { ObjectValuesPipe } from '../../shared/utils/object-values-pipe';
import { ResourcePoliciesModule } from '../../shared/resource-policies/resource-policies.module';
import { ItemVersionsModule } from '../versions/item-versions.module';
import { IdentifierDataService } from '../../core/data/identifier-data.service';
import { IdentifierDataComponent } from '../../shared/object-list/identifier-data/identifier-data.component';
import { ItemRegisterDoiComponent } from './item-register-doi/item-registerdoi.component';
import { DsoSharedModule } from '../../dso-shared/dso-shared.module';
@@ -76,10 +79,13 @@ import { DsoSharedModule } from '../../dso-shared/dso-shared.module';
ItemMoveComponent,
ItemEditBitstreamDragHandleComponent,
VirtualMetadataComponent,
ItemAuthorizationsComponent
ItemAuthorizationsComponent,
IdentifierDataComponent,
ItemRegisterDoiComponent
],
providers: [
BundleDataService,
IdentifierDataService,
ObjectValuesPipe
],
})

View File

@@ -5,3 +5,4 @@ export const ITEM_EDIT_PUBLIC_PATH = 'public';
export const ITEM_EDIT_DELETE_PATH = 'delete';
export const ITEM_EDIT_MOVE_PATH = 'move';
export const ITEM_EDIT_AUTHORIZATIONS_PATH = 'authorizations';
export const ITEM_EDIT_REGISTER_DOI_PATH = 'registerdoi';

View File

@@ -10,6 +10,7 @@ import { ItemStatusComponent } from './item-status/item-status.component';
import { ItemBitstreamsComponent } from './item-bitstreams/item-bitstreams.component';
import { ItemCollectionMapperComponent } from './item-collection-mapper/item-collection-mapper.component';
import { ItemMoveComponent } from './item-move/item-move.component';
import { ItemRegisterDoiComponent } from './item-register-doi/item-registerdoi.component'
import { ItemRelationshipsComponent } from './item-relationships/item-relationships.component';
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
import { ItemVersionHistoryComponent } from './item-version-history/item-version-history.component';
@@ -26,7 +27,8 @@ import {
ITEM_EDIT_PRIVATE_PATH,
ITEM_EDIT_PUBLIC_PATH,
ITEM_EDIT_REINSTATE_PATH,
ITEM_EDIT_WITHDRAW_PATH
ITEM_EDIT_WITHDRAW_PATH,
ITEM_EDIT_REGISTER_DOI_PATH
} from './edit-item-page.routing-paths';
import { ItemPageReinstateGuard } from './item-page-reinstate.guard';
import { ItemPageWithdrawGuard } from './item-page-withdraw.guard';
@@ -38,6 +40,7 @@ import { ItemPageRelationshipsGuard } from './item-page-relationships.guard';
import { ItemPageVersionHistoryGuard } from './item-page-version-history.guard';
import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.guard';
import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component';
import { ItemPageRegisterDoiGuard } from './item-page-registerdoi.guard';
/**
* Routing module that handles the routing for the Edit Item page administrator functionality
@@ -142,6 +145,12 @@ import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metada
component: ItemMoveComponent,
data: { title: 'item.edit.move.title' },
},
{
path: ITEM_EDIT_REGISTER_DOI_PATH,
component: ItemRegisterDoiComponent,
canActivate: [ItemPageRegisterDoiGuard],
data: { title: 'item.edit.registerdoi.title' },
},
{
path: ITEM_EDIT_AUTHORIZATIONS_PATH,
children: [
@@ -186,6 +195,7 @@ import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metada
ItemPageRelationshipsGuard,
ItemPageVersionHistoryGuard,
ItemPageCollectionMapperGuard,
ItemPageRegisterDoiGuard,
]
})
export class EditItemPageRoutingModule {

View File

@@ -0,0 +1,31 @@
import { Injectable } from '@angular/core';
import { DsoPageSingleFeatureGuard } from '../../core/data/feature-authorization/feature-authorization-guard/dso-page-single-feature.guard';
import { Item } from '../../core/shared/item.model';
import { ItemPageResolver } from '../item-page.resolver';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
import { Observable, of as observableOf } from 'rxjs';
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { AuthService } from '../../core/auth/auth.service';
@Injectable({
providedIn: 'root'
})
/**
* Guard for preventing unauthorized access to certain {@link Item} pages requiring DOI registration rights
*/
export class ItemPageRegisterDoiGuard extends DsoPageSingleFeatureGuard<Item> {
constructor(protected resolver: ItemPageResolver,
protected authorizationService: AuthorizationDataService,
protected router: Router,
protected authService: AuthService) {
super(resolver, authorizationService, router, authService);
}
/**
* Check DOI registration authorization rights
*/
getFeatureID(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID> {
return observableOf(FeatureID.CanRegisterDOI);
}
}

View File

@@ -27,6 +27,6 @@ export class ItemPageStatusGuard extends DsoPageSomeFeatureGuard<Item> {
* Check authorization rights
*/
getFeatureIDs(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<FeatureID[]> {
return observableOf([FeatureID.CanManageMappings, FeatureID.WithdrawItem, FeatureID.ReinstateItem, FeatureID.CanManagePolicies, FeatureID.CanMakePrivate, FeatureID.CanDelete, FeatureID.CanMove]);
return observableOf([FeatureID.CanManageMappings, FeatureID.WithdrawItem, FeatureID.ReinstateItem, FeatureID.CanManagePolicies, FeatureID.CanMakePrivate, FeatureID.CanDelete, FeatureID.CanMove, FeatureID.CanRegisterDOI]);
}
}

View File

@@ -0,0 +1,24 @@
<div class="container">
<div class="row">
<div class="col-12">
<h2>{{headerMessage | translate: {id: item.handle} }}</h2>
<p>{{descriptionMessage | translate}}</p>
<div *ngFor="let identifier of (identifiers$ | async)" class="w-100 p">
<div *ngIf="(identifier.identifierType=='doi')">
<p class="float-left">{{doiToUpdateMessage | translate}}: {{identifier.value}}
({{"item.edit.identifiers.doi.status."+identifier.identifierStatus|translate}})
</p>
</div>
</div>
<ds-modify-item-overview [item]="item"></ds-modify-item-overview>
<div class="space-children-mr">
<button (click)="performAction()" class="btn btn-outline-secondary perform-action">{{confirmMessage | translate}}
</button>
<button [routerLink]="[itemPageRoute, 'edit']" class="btn btn-outline-secondary cancel">
{{cancelMessage| translate}}
</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,87 @@
import { Component } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { AbstractSimpleItemActionComponent } from '../simple-item-action/abstract-simple-item-action.component';
import { RemoteData } from '../../../core/data/remote-data';
import { Item } from '../../../core/shared/item.model';
import { NotificationsService } from '../../../shared/notifications/notifications.service';
import { ItemDataService } from '../../../core/data/item-data.service';
import { getFirstCompletedRemoteData, getFirstSucceededRemoteData } from '../../../core/shared/operators';
import { first, map } from 'rxjs/operators';
import { hasValue } from '../../../shared/empty.util';
import { Observable } from 'rxjs';
import {getItemEditRoute, getItemPageRoute} from '../../item-page-routing-paths';
import { IdentifierDataService } from '../../../core/data/identifier-data.service';
import {Identifier} from '../../../shared/object-list/identifier-data/identifier.model';
@Component({
selector: 'ds-item-registerdoi',
templateUrl: './item-registerdoi-component.html'
})
/**
* Component responsible for rendering the Item Registe DOI page
*/
export class ItemRegisterDoiComponent extends AbstractSimpleItemActionComponent {
protected messageKey = 'registerdoi';
doiToUpdateMessage = 'item.edit.' + this.messageKey + '.to-update';
identifiers$: Observable<Identifier[]>;
processing: boolean = false;
constructor(protected route: ActivatedRoute,
protected router: Router,
protected notificationsService: NotificationsService,
protected itemDataService: ItemDataService,
protected translateService: TranslateService,
protected identifierDataService: IdentifierDataService) {
super(route, router, notificationsService, itemDataService, translateService);
}
ngOnInit(): void {
this.itemRD$ = this.route.data.pipe(
map((data) => data.dso),
getFirstSucceededRemoteData()
)as Observable<RemoteData<Item>>;
this.itemRD$.pipe(first()).subscribe((rd) => {
this.item = rd.payload;
this.itemPageRoute = getItemPageRoute(this.item);
this.identifiers$ = this.identifierDataService.getIdentifierDataFor(this.item).pipe(
map((identifierRD) => {
if (identifierRD.statusCode !== 401 && hasValue(identifierRD.payload)) {
return identifierRD.payload.identifiers;
} else {
return null;
}
}),
);
}
);
this.confirmMessage = 'item.edit.' + this.messageKey + '.confirm';
this.cancelMessage = 'item.edit.' + this.messageKey + '.cancel';
this.headerMessage = 'item.edit.' + this.messageKey + '.header';
this.descriptionMessage = 'item.edit.' + this.messageKey + '.description';
}
/**
* Perform the register DOI action to the item
*/
performAction() {
this.registerDoi();
}
registerDoi() {
this.processing = true;
this.itemDataService.registerDOI(this.item.id).pipe(getFirstCompletedRemoteData()).subscribe(
(response: RemoteData<Item>) => {
this.processing = false;
this.router.navigateByUrl(getItemEditRoute(this.item));
}
);
}
}

View File

@@ -8,6 +8,17 @@
{{statusData[statusKey]}}
</div>
</div>
<div *ngFor="let identifier of (identifiers$ | async)" class="w-100">
<div *ngIf="(identifier.identifierType=='doi')">
<div class="col-3 float-left status-label">
{{identifier.identifierType.toLocaleUpperCase()}}
</div>
<div class="col-9 float-left status-label">{{identifier.value}}
({{"item.edit.identifiers.doi.status."+identifier.identifierStatus|translate}})</div>
</div>
</div>
<div class="col-3 float-left status-label">
{{'item.edit.tabs.status.labels.itemPage' | translate}}:
</div>
@@ -18,4 +29,5 @@
<div *ngFor="let operation of (operations$ | async)" class="w-100" [ngClass]="{'pt-3': operation}">
<ds-item-operation *ngIf="operation" [operation]="operation"></ds-item-operation>
</div>
</div>

View File

@@ -3,14 +3,23 @@ import { fadeIn, fadeInOut } from '../../../shared/animations/fade';
import { Item } from '../../../core/shared/item.model';
import { ActivatedRoute } from '@angular/router';
import { ItemOperation } from '../item-operation/itemOperation.model';
import { distinctUntilChanged, first, map, mergeMap, toArray } from 'rxjs/operators';
import { BehaviorSubject, Observable, from as observableFrom } from 'rxjs';
import {distinctUntilChanged, first, map, mergeMap, startWith, switchMap, toArray} from 'rxjs/operators';
import {BehaviorSubject, Observable, from as observableFrom, Subscription, combineLatest, of} from 'rxjs';
import { RemoteData } from '../../../core/data/remote-data';
import { getItemEditRoute, getItemPageRoute } from '../../item-page-routing-paths';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { hasValue } from '../../../shared/empty.util';
import { getAllSucceededRemoteDataPayload } from '../../../core/shared/operators';
import {
getAllSucceededRemoteDataPayload,
getFirstCompletedRemoteData, getFirstSucceededRemoteData,
getFirstSucceededRemoteDataPayload
} from '../../../core/shared/operators';
import {IdentifierDataService} from '../../../core/data/identifier-data.service';
import {IdentifierData} from '../../../shared/object-list/identifier-data/identifier-data.model';
import {Identifier} from '../../../shared/object-list/identifier-data/identifier.model';
import {ConfigurationProperty} from '../../../core/shared/configuration-property.model';
import {ConfigurationDataService} from '../../../core/data/configuration-data.service';
@Component({
selector: 'ds-item-status',
@@ -51,15 +60,32 @@ export class ItemStatusComponent implements OnInit {
*/
actionsKeys;
/**
* Identifiers (handles, DOIs)
*/
identifiers$: Observable<Identifier[]>;
/**
* Configuration and state variables regarding DOIs
*/
public subs: Subscription[] = [];
/**
* Route to the item's page
*/
itemPageRoute$: Observable<string>;
constructor(private route: ActivatedRoute,
private authorizationService: AuthorizationDataService) {
private authorizationService: AuthorizationDataService,
private identifierDataService: IdentifierDataService,
private configurationService: ConfigurationDataService,
) {
}
/**
* Initialise component
*/
ngOnInit(): void {
this.itemRD$ = this.route.parent.data.pipe(map((data) => data.dso));
this.itemRD$.pipe(
@@ -72,6 +98,35 @@ export class ItemStatusComponent implements OnInit {
lastModified: item.lastModified
});
this.statusDataKeys = Object.keys(this.statusData);
// Observable for item identifiers (retrieved from embedded link)
this.identifiers$ = this.identifierDataService.getIdentifierDataFor(item).pipe(
map((identifierRD) => {
if (identifierRD.statusCode !== 401 && hasValue(identifierRD.payload)) {
return identifierRD.payload.identifiers;
} else {
return null;
}
}),
);
// Observable for configuration determining whether the Register DOI feature is enabled
let registerConfigEnabled$: Observable<boolean> = this.configurationService.findByPropertyName('identifiers.item-status.register').pipe(
map((enabled: RemoteData<ConfigurationProperty>) => {
let show: boolean = false;
if (enabled.hasSucceeded) {
if (enabled.payload !== undefined && enabled.payload !== null) {
if (enabled.payload.values !== undefined) {
enabled.payload.values.forEach((value) => {
show = true;
});
}
}
}
return show;
})
);
/*
The key is used to build messages
i18n example: 'item.edit.tabs.status.buttons.<key>.label'
@@ -92,10 +147,45 @@ export class ItemStatusComponent implements OnInit {
}
operations.push(new ItemOperation('delete', this.getCurrentUrl(item) + '/delete', FeatureID.CanDelete, true));
operations.push(new ItemOperation('move', this.getCurrentUrl(item) + '/move', FeatureID.CanMove, true));
this.operations$.next(operations);
observableFrom(operations).pipe(
// Observable that reads identifiers and their status and, and config properties, and decides
// if we're allowed to show a Register DOI feature
let showRegister$: Observable<boolean> = combineLatest([this.identifiers$, registerConfigEnabled$]).pipe(
distinctUntilChanged(),
map(([identifiers, enabled]) => {
let no_doi: boolean = true;
let pending: boolean = false;
if (identifiers !== undefined && identifiers !== null) {
identifiers.forEach((identifier: Identifier) => {
if (hasValue(identifier) && identifier.identifierType == 'doi') {
// The item has some kind of DOI
no_doi = false;
if (identifier.identifierStatus == '10' || identifier.identifierStatus == '11'
|| identifier.identifierStatus == null) {
// The item's DOI is pending, minted or null.
// It isn't registered, reserved, queued for registration or reservation or update, deleted
// or queued for deletion.
pending = true;
}
}
});
}
// If there is no DOI, or a pending/minted/null DOI, and the config is enabled, return true
return ((pending || no_doi) && enabled);
})
)
// Subscribe to changes from the showRegister check and rebuild operations list accordingly
this.subs.push(showRegister$.subscribe((show) => {
// Copy the static array first so we don't keep appending to it
let tmp_operations = [...operations];
if (show) {
// Push the new Register DOI item operation
tmp_operations.push(new ItemOperation('registerDOI', this.getCurrentUrl(item) + '/registerdoi', FeatureID.CanRegisterDOI));
}
// Check authorisations and merge into new operations list
observableFrom(tmp_operations).pipe(
mergeMap((operation) => {
if (hasValue(operation.featureID)) {
return this.authorizationService.isAuthorized(operation.featureID, item.self).pipe(
@@ -108,11 +198,15 @@ export class ItemStatusComponent implements OnInit {
}),
toArray()
).subscribe((ops) => this.operations$.next(ops));
}));
});
this.itemPageRoute$ = this.itemRD$.pipe(
getAllSucceededRemoteDataPayload(),
map((item) => getItemPageRoute(item))
);
}
/**
@@ -127,4 +221,10 @@ export class ItemStatusComponent implements OnInit {
return hasValue(operation) ? operation.operationKey : undefined;
}
ngOnDestroy(): void {
this.subs
.filter((subscription) => hasValue(subscription))
.forEach((subscription) => subscription.unsubscribe());
}
}

View File

@@ -0,0 +1,5 @@
<ng-container>
<div *ngIf="identifiers$ | async as identifiers">
<span class="">{{ identifiers[0].value | translate }}</span>
</div>
</ng-container>

View File

@@ -0,0 +1,56 @@
import { Component, Input } from '@angular/core';
import { catchError, map } from 'rxjs/operators';
import { Observable, of as observableOf } from 'rxjs';
import { hasValue } from '../../empty.util';
import { environment } from 'src/environments/environment';
import { Item } from 'src/app/core/shared/item.model';
import { AccessStatusDataService } from 'src/app/core/data/access-status-data.service';
import {IdentifierData} from './identifier-data.model';
import {IdentifierDataService} from '../../../core/data/identifier-data.service';
@Component({
selector: 'ds-identifier-data',
templateUrl: './identifier-data.component.html'
})
/**
* Component rendering the access status of an item as a badge
*/
export class IdentifierDataComponent {
@Input() item: Item;
identifiers$: Observable<IdentifierData>;
/**
* Whether to show the access status badge or not
*/
showAccessStatus: boolean;
/**
* Initialize instance variables
*
* @param {IdentifierDataService} identifierDataService
*/
constructor(private identifierDataService: IdentifierDataService) { }
ngOnInit(): void {
if (this.item == null) {
// Do not show the badge if the feature is inactive or if the item is null.
return;
}
if (this.item.identifiers == null) {
// In case the access status has not been loaded, do it individually.
this.item.identifiers = this.identifierDataService.getIdentifierDataFor(this.item);
}
this.identifiers$ = this.item.identifiers.pipe(
map((identifierRD) => {
if (identifierRD.statusCode !== 401 && hasValue(identifierRD.payload)) {
return identifierRD.payload;
} else {
return null;
}
}),
// EMpty array if none
//map((identifiers: IdentifierData) => hasValue(identifiers.identifiers) ? identifiers.identifiers : [])
);
}
}

View File

@@ -0,0 +1,33 @@
import { autoserialize, deserialize } from 'cerialize';
import { typedObject } from 'src/app/core/cache/builders/build-decorators';
import { CacheableObject } from 'src/app/core/cache/cacheable-object.model';
import { HALLink } from 'src/app/core/shared/hal-link.model';
import { ResourceType } from 'src/app/core/shared/resource-type';
import { excludeFromEquals } from 'src/app/core/utilities/equals.decorators';
import { IDENTIFIERS } from './identifier-data.resource-type';
import {Identifier} from './identifier.model';
@typedObject
export class IdentifierData implements CacheableObject {
static type = IDENTIFIERS;
/**
* The type for this IdentifierData
*/
@excludeFromEquals
@autoserialize
type: ResourceType;
/**
* The
*/
@autoserialize
identifiers: Identifier[];
/**
* The {@link HALLink}s for this IdentifierData
*/
@deserialize
_links: {
self: HALLink;
};
}

View File

@@ -0,0 +1,9 @@
import { ResourceType } from 'src/app/core/shared/resource-type';
/**
* The resource type for Identifiers
*
* Needs to be in a separate file to prevent circular
* dependencies in webpack.
*/
export const IDENTIFIERS = new ResourceType('identifiers');

View File

@@ -0,0 +1,12 @@
import {autoserialize} from 'cerialize';
export class Identifier {
@autoserialize
value: string;
@autoserialize
identifierType: string;
@autoserialize
identifierStatus: string;
@autoserialize
type: string;
}

View File

@@ -1918,6 +1918,46 @@
"item.edit.tabs.item-mapper.title": "Item Edit - Collection Mapper",
"item.edit.identifiers.doi.status.1": "Queued for registration",
"item.edit.identifiers.doi.status.2": "Queued for reservation",
"item.edit.identifiers.doi.status.3": "Registered",
"item.edit.identifiers.doi.status.4": "Reserved",
"item.edit.identifiers.doi.status.5": "Reserved",
"item.edit.identifiers.doi.status.6": "Registered",
"item.edit.identifiers.doi.status.7": "Queued for registration",
"item.edit.identifiers.doi.status.8": "Queued for deletion",
"item.edit.identifiers.doi.status.9": "Deleted",
"item.edit.identifiers.doi.status.10": "Pending approval",
"item.edit.identifiers.doi.status.11": "Minted, not registered",
"item.edit.tabs.status.buttons.registerDOI.label": "Register a new or pending identifier",
"item.edit.tabs.status.buttons.registerDOI.button": "Register DOI...",
"item.edit.registerdoi.header": "Register a new or pending DOI",
"item.edit.registerdoi.description": "Review any pending identifiers and item metadata below and click Confirm to proceed with DOI registration, or Cancel to back out",
"item.edit.registerdoi.confirm": "Confirm",
"item.edit.registerdoi.cancel": "Cancel",
"item.edit.registerdoi.success": "DOI registered successfully. Refresh Item Status page to see new DOI details.",
"item.edit.registerdoi.error": "Error registering DOI",
"item.edit.registerdoi.to-update": "The following DOI has already been minted and will be queued for registration online",
"item.edit.item-mapper.buttons.add": "Map item to selected collections",
"item.edit.item-mapper.buttons.remove": "Remove item's mapping for selected collections",