forked from hazza/dspace-angular
[CST-4499] Version history (WIP) - Missing tests and auth features
This commit is contained in:

committed by
Davide Negretti

parent
adb40d8712
commit
ce399cb764
@@ -8,19 +8,21 @@ import { CoreState } from '../core.reducers';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { DefaultChangeAnalyzer } from './default-change-analyzer.service';
|
||||
import { FindListOptions } from './request.models';
|
||||
import { FindListOptions, PostRequest, RestRequest } from './request.models';
|
||||
import { Observable } from 'rxjs';
|
||||
import { PaginatedSearchOptions } from '../../shared/search/paginated-search-options.model';
|
||||
import { RemoteData } from './remote-data';
|
||||
import { PaginatedList } from './paginated-list.model';
|
||||
import { Version } from '../shared/version.model';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { map, switchMap, take } from 'rxjs/operators';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { VERSION_HISTORY } from '../shared/version-history.resource-type';
|
||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
import { VersionDataService } from './version-data.service';
|
||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||
import { getFirstCompletedRemoteData, sendRequest } from '../shared/operators';
|
||||
|
||||
/**
|
||||
* Service responsible for handling requests related to the VersionHistory object
|
||||
@@ -79,4 +81,20 @@ export class VersionHistoryDataService extends DataService<VersionHistory> {
|
||||
|
||||
return this.versionDataService.findAllByHref(hrefObs, undefined, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
|
||||
}
|
||||
|
||||
createVersion(itemHref: string, summary: string): Observable<RemoteData<Version>> {
|
||||
const requestOptions: HttpOptions = Object.create({});
|
||||
let requestHeaders = new HttpHeaders();
|
||||
requestHeaders = requestHeaders.append('Content-Type', 'text/uri-list');
|
||||
requestOptions.headers = requestHeaders;
|
||||
|
||||
return this.halService.getEndpoint(this.versionsEndpoint).pipe(
|
||||
take(1),
|
||||
map((endpointUrl: string) => (summary?.length > 0) ? `${endpointUrl}?summary=${summary}` : `${endpointUrl}`),
|
||||
map((endpointURL: string) => new PostRequest(this.requestService.generateRequestId(), endpointURL, itemHref, requestOptions)),
|
||||
sendRequest(this.requestService),
|
||||
switchMap((restRequest: RestRequest) => this.rdbService.buildFromRequestUUID(restRequest.uuid)),
|
||||
getFirstCompletedRemoteData()
|
||||
) as Observable<RemoteData<Version>>;
|
||||
}
|
||||
}
|
||||
|
@@ -30,6 +30,18 @@ export class VersionHistory extends DSpaceObject {
|
||||
@autoserialize
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The summary of this Version History
|
||||
*/
|
||||
@autoserialize
|
||||
summary: string;
|
||||
|
||||
/**
|
||||
* The name of the submitter of this Version History
|
||||
*/
|
||||
@autoserialize
|
||||
submitterName: string;
|
||||
|
||||
/**
|
||||
* The list of versions within this history
|
||||
*/
|
||||
|
@@ -22,14 +22,17 @@
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.discard-button" | translate}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<table class="table table-responsive table-striped table-bordered" *ngIf="((updates$ | async)| dsObjectValues).length > 0">
|
||||
<tbody>
|
||||
<table class="table table-responsive table-striped table-bordered"
|
||||
*ngIf="((updates$ | async)| dsObjectValues).length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span id="fieldName">{{'item.edit.metadata.headers.field' | translate}}</span></th>
|
||||
<th><span id="fieldValue">{{'item.edit.metadata.headers.value' | translate}}</span></th>
|
||||
<th class="text-center"><span id="fieldLang">{{'item.edit.metadata.headers.language' | translate}}</span></th>
|
||||
<th class="text-center">{{'item.edit.metadata.headers.edit' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let updateValue of ((updates$ | async)| dsObjectValues); trackBy: trackUpdate"
|
||||
ds-edit-in-place-field
|
||||
[fieldUpdate]="updateValue || {}"
|
||||
|
@@ -1,6 +1,4 @@
|
||||
<div class="mt-4">
|
||||
<ds-alert [content]="'item.edit.tabs.versionhistory.under-construction'" [type]="AlertTypeEnum.Warning"></ds-alert>
|
||||
</div>
|
||||
<div class="mt-2" *ngVar="(itemRD$ | async)?.payload as item">
|
||||
<ds-item-versions *ngIf="item" [item]="item" [displayWhenEmpty]="true" [displayTitle]="false"></ds-item-versions>
|
||||
<ds-item-versions *ngIf="item" [item]="item" [displayWhenEmpty]="true" [displayTitle]="false"
|
||||
[displayActions]="true"></ds-item-versions>
|
||||
</div>
|
||||
|
@@ -30,6 +30,6 @@ export class ItemVersionHistoryComponent {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.itemRD$ = this.route.parent.data.pipe(map((data) => data.dso)).pipe(getFirstSucceededRemoteData()) as Observable<RemoteData<Item>>;
|
||||
this.itemRD$ = this.route.parent.parent.data.pipe(map((data) => data.dso)).pipe(getFirstSucceededRemoteData()) as Observable<RemoteData<Item>>;
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<ds-item-versions-notice [item]="item"></ds-item-versions-notice>
|
||||
<ds-view-tracker [object]="item"></ds-view-tracker>
|
||||
<ds-listable-object-component-loader [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader>
|
||||
<ds-item-versions class="mt-2" [item]="item"></ds-item-versions>
|
||||
<ds-item-versions class="mt-2" [item]="item" [displayActions]="false"></ds-item-versions>
|
||||
</div>
|
||||
</div>
|
||||
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.item' | translate}}"></ds-error>
|
||||
|
@@ -3,6 +3,7 @@
|
||||
{{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-version-button (newVersionEvent)="createNewVersion()" [dso]="object" [tooltipMsg]="'item.page.version'"></ds-dso-page-version-button>
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'publication.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -2,6 +2,14 @@ import { Component, Input, OnInit } from '@angular/core';
|
||||
import { environment } from '../../../../../environments/environment';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { getItemPageRoute } from '../../../item-page-routing-paths';
|
||||
import { ItemVersionsSummaryModalComponent } from '../../../../shared/item/item-versions/item-versions-summary-modal/item-versions-summary-modal.component';
|
||||
import { getFirstCompletedRemoteData } from '../../../../core/shared/operators';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { VersionHistoryDataService } from '../../../../core/data/version-history-data.service';
|
||||
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { VersionDataService } from '../../../../core/data/version-data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item',
|
||||
@@ -20,6 +28,49 @@ export class ItemComponent implements OnInit {
|
||||
|
||||
mediaViewer = environment.mediaViewer;
|
||||
|
||||
constructor(
|
||||
private modalService: NgbModal,
|
||||
private versionHistoryService: VersionHistoryDataService,
|
||||
private notificationsService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
// private itemService: ItemDataService,
|
||||
private versionService: VersionDataService,
|
||||
) {
|
||||
}
|
||||
|
||||
createNewVersion() {
|
||||
const successMessageKey = 'item.version.create.notification.success';
|
||||
const failureMessageKey = 'item.version.create.notification.failure';
|
||||
const item = this.object;
|
||||
|
||||
// Open modal
|
||||
const activeModal = this.modalService.open(ItemVersionsSummaryModalComponent);
|
||||
|
||||
this.versionService.findByHref(item._links.version.href).pipe(getFirstCompletedRemoteData()).subscribe(
|
||||
(res) => {
|
||||
// TODO check serve async?
|
||||
activeModal.componentInstance.firstVersion = res.hasNoContent;
|
||||
activeModal.componentInstance.versionNumber = (res.hasNoContent ? undefined : res.payload.version);
|
||||
}
|
||||
);
|
||||
|
||||
// On modal submit/dismiss
|
||||
activeModal.result.then((modalResult) => {
|
||||
const summary = modalResult;
|
||||
|
||||
const itemHref = item._links.self.href;
|
||||
|
||||
this.versionHistoryService.createVersion(itemHref, summary).pipe(take(1)).subscribe((postResult) => {
|
||||
if (postResult.hasSucceeded) {
|
||||
const newVersionNumber = postResult.payload.version;
|
||||
this.notificationsService.success(null, this.translateService.get(successMessageKey, {version: newVersionNumber}));
|
||||
} else {
|
||||
this.notificationsService.error(null, this.translateService.get(failureMessageKey));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.itemPageRoute = getItemPageRoute(this.object);
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@
|
||||
<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
|
||||
</h2>
|
||||
<div class="pl-2">
|
||||
<ds-dso-page-version-button (newVersionEvent)="createNewVersion()" [dso]="object" [tooltipMsg]="'item.page.version'"></ds-dso-page-version-button>
|
||||
<ds-dso-page-edit-button [pageRoute]="itemPageRoute" [dso]="object" [tooltipMsg]="'item.page.edit'"></ds-dso-page-edit-button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -119,7 +119,7 @@ export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called on clicking the button "New Submition", It opens a dialog for
|
||||
* Method called on clicking the button "New Submission", It opens a dialog for
|
||||
* select a collection.
|
||||
*/
|
||||
openDialog() {
|
||||
|
@@ -0,0 +1,7 @@
|
||||
<button *ngIf="isAuthorized$ | async"
|
||||
class="edit-button btn btn-dark btn-sm"
|
||||
(click)="createNewVersion()"
|
||||
[ngbTooltip]="tooltipMsg | translate"
|
||||
role="button" [title]="tooltipMsg |translate" [attr.aria-label]="tooltipMsg |translate">
|
||||
<i class="fas fa-code-branch fa-fw"></i>
|
||||
</button>
|
@@ -0,0 +1,3 @@
|
||||
.btn-dark {
|
||||
background-color: var(--ds-admin-sidebar-bg);
|
||||
}
|
@@ -0,0 +1,76 @@
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { DsoPageVersionButtonComponent } from './dso-page-version-button.component';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
describe('DsoPageEditButtonComponent', () => {
|
||||
let component: DsoPageVersionButtonComponent;
|
||||
let fixture: ComponentFixture<DsoPageVersionButtonComponent>;
|
||||
|
||||
let authorizationService: AuthorizationDataService;
|
||||
let dso: DSpaceObject;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
dso = Object.assign(new Item(), {
|
||||
id: 'test-item',
|
||||
_links: {
|
||||
self: { href: 'test-item-selflink' }
|
||||
}
|
||||
});
|
||||
authorizationService = jasmine.createSpyObj('authorizationService', {
|
||||
isAuthorized: observableOf(true)
|
||||
});
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DsoPageVersionButtonComponent],
|
||||
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NgbModule],
|
||||
providers: [
|
||||
{ provide: AuthorizationDataService, useValue: authorizationService }
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DsoPageVersionButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.dso = dso;
|
||||
component.pageRoute = 'test';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should check the authorization of the current user', () => {
|
||||
expect(authorizationService.isAuthorized).toHaveBeenCalledWith(FeatureID.CanEditMetadata, dso.self);
|
||||
});
|
||||
|
||||
describe('when the user is authorized', () => {
|
||||
beforeEach(() => {
|
||||
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(true));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should render a link', () => {
|
||||
const link = fixture.debugElement.query(By.css('a'));
|
||||
expect(link).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user is not authorized', () => {
|
||||
beforeEach(() => {
|
||||
(authorizationService.isAuthorized as jasmine.Spy).and.returnValue(observableOf(false));
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should not render a link', () => {
|
||||
const link = fixture.debugElement.query(By.css('a'));
|
||||
expect(link).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,52 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
|
||||
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-dso-page-version-button',
|
||||
templateUrl: './dso-page-version-button.component.html',
|
||||
styleUrls: ['./dso-page-version-button.component.scss']
|
||||
})
|
||||
/**
|
||||
* Display a button linking to the edit page of a DSpaceObject
|
||||
*/
|
||||
export class DsoPageVersionButtonComponent implements OnInit {
|
||||
/**
|
||||
* The DSpaceObject to display a button to the edit page for
|
||||
*/
|
||||
@Input() dso: DSpaceObject;
|
||||
|
||||
/**
|
||||
* A message for the tooltip on the button
|
||||
* Supports i18n keys
|
||||
*/
|
||||
@Input() tooltipMsg: string;
|
||||
|
||||
/**
|
||||
* Emits an event that triggers the creation of the new version
|
||||
*/
|
||||
@Output() newVersionEvent = new EventEmitter();
|
||||
|
||||
/**
|
||||
* Whether or not the current user is authorized to create a new version of the DSpaceObject
|
||||
*/
|
||||
isAuthorized$: Observable<boolean>;
|
||||
|
||||
constructor(protected authorizationService: AuthorizationDataService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new version for the current item
|
||||
*/
|
||||
createNewVersion() {
|
||||
this.newVersionEvent.emit();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// TODO show if user can view history
|
||||
this.isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanEditMetadata, this.dso.self);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
<div>
|
||||
<div class="modal-header">{{'item.version.delete.modal.header' | translate}}
|
||||
<button type="button" class="close" (click)="onModalClose()" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="pb-2">{{ "item.version.delete.modal.text" | translate : {version: versionNumber} }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-outline-secondary btn-sm"
|
||||
(click)="onModalClose()"
|
||||
title="{{'item.version.delete.modal.button.cancel.tooltip' | translate}}">
|
||||
<i class="fas fa-times fa-fw"></i> {{'item.version.delete.modal.button.cancel' | translate}}
|
||||
</button>
|
||||
<button class="btn btn-danger btn-sm"
|
||||
(click)="onModalSubmit()"
|
||||
title="{{'item.version.delete.modal.button.confirm.tooltip' | translate}}">
|
||||
<i class="fas fa-check fa-fw"></i> {{'item.version.delete.modal.button.confirm' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ItemVersionsDeleteModalComponent } from './item-versions-delete-modal.component';
|
||||
|
||||
describe('ItemVersionsDeleteModalComponent', () => {
|
||||
let component: ItemVersionsDeleteModalComponent;
|
||||
let fixture: ComponentFixture<ItemVersionsDeleteModalComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ItemVersionsDeleteModalComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ItemVersionsDeleteModalComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,25 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-versions-delete-modal',
|
||||
templateUrl: './item-versions-delete-modal.component.html',
|
||||
styleUrls: ['./item-versions-delete-modal.component.scss']
|
||||
})
|
||||
export class ItemVersionsDeleteModalComponent {
|
||||
|
||||
versionNumber: number;
|
||||
|
||||
constructor(
|
||||
protected activeModal: NgbActiveModal,) {
|
||||
}
|
||||
|
||||
onModalClose() {
|
||||
this.activeModal.dismiss();
|
||||
}
|
||||
|
||||
onModalSubmit() {
|
||||
this.activeModal.close();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<div>
|
||||
<div class="modal-header">{{'item.version.create.modal.header' | translate}}
|
||||
<button type="button" class="close" (click)="onModalClose()" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="pb-2">
|
||||
{{ "item.version.create.modal.text" | translate }}
|
||||
<span *ngIf="!firstVersion">{{ "item.version.create.modal.text.startingFrom" | translate : {version: versionNumber} }}</span>
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="summary">{{'item.version.create.modal.form.summary.label' | translate }}:</label>
|
||||
<input type="text" id="summary" required class="form-control" [(ngModel)]="newVersionSummary"
|
||||
placeholder="{{'item.version.create.modal.form.summary.placeholder' | translate }}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-outline-secondary btn-sm"
|
||||
(click)="onModalClose()"
|
||||
title="{{'item.version.create.modal.button.cancel.tooltip' | translate}}">
|
||||
<i class="fas fa-times fa-fw"></i> {{'item.version.create.modal.button.cancel' | translate}}
|
||||
</button>
|
||||
<button class="btn btn-success btn-sm"
|
||||
(click)="onModalSubmit()"
|
||||
title="{{'item.version.create.modal.button.confirm.tooltip' | translate}}">
|
||||
<i class="fas fa-check fa-fw"></i> {{'item.version.create.modal.button.confirm' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ItemVersionsSummaryModalComponent } from './item-versions-summary-modal.component';
|
||||
|
||||
describe('ItemVersionsSummaryModalComponent', () => {
|
||||
let component: ItemVersionsSummaryModalComponent;
|
||||
let fixture: ComponentFixture<ItemVersionsSummaryModalComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ ItemVersionsSummaryModalComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ItemVersionsSummaryModalComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,28 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-versions-summary-modal',
|
||||
templateUrl: './item-versions-summary-modal.component.html',
|
||||
styleUrls: ['./item-versions-summary-modal.component.scss']
|
||||
})
|
||||
export class ItemVersionsSummaryModalComponent {
|
||||
|
||||
versionNumber: number;
|
||||
newVersionSummary: string;
|
||||
firstVersion: boolean;
|
||||
|
||||
constructor(
|
||||
protected activeModal: NgbActiveModal,
|
||||
) {
|
||||
}
|
||||
|
||||
onModalClose() {
|
||||
this.activeModal.dismiss();
|
||||
}
|
||||
|
||||
onModalSubmit() {
|
||||
this.activeModal.close(this.newVersionSummary);
|
||||
}
|
||||
|
||||
}
|
@@ -2,6 +2,9 @@
|
||||
<div *ngVar="(versionRD$ | async)?.payload as itemVersion">
|
||||
<div class="mb-2" *ngIf="versions?.page?.length > 0 || displayWhenEmpty">
|
||||
<h2 *ngIf="displayTitle">{{"item.version.history.head" | translate}}</h2>
|
||||
<ds-alert [type]="AlertTypeEnum.Info" *ngIf="itemVersion">
|
||||
{{ "item.version.history.selected.alert" | translate : {version: itemVersion.version} }}
|
||||
</ds-alert>
|
||||
<ds-pagination *ngIf="versions?.page?.length > 0"
|
||||
[hideGear]="true"
|
||||
[hidePagerWhenSinglePage]="true"
|
||||
@@ -9,38 +12,79 @@
|
||||
[pageInfoState]="versions"
|
||||
[collectionSize]="versions?.totalElements"
|
||||
[retainScrollPosition]="true">
|
||||
<table class="table table-striped my-2">
|
||||
<table class="table table-striped table-bordered align-middle my-2">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{"item.version.history.table.version" | translate}}</th>
|
||||
<th scope="col">{{"item.version.history.table.item" | translate}}</th>
|
||||
<th scope="col" *ngIf="(hasEpersons$ | async)">{{"item.version.history.table.editor" | translate}}</th>
|
||||
<th scope="col">{{"item.version.history.table.date" | translate}}</th>
|
||||
<th scope="col">{{"item.version.history.table.summary" | translate}}</th>
|
||||
<th scope="col" *ngIf="displayActions">{{"item.version.history.table.actions" | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let version of versions?.page" [id]="'version-row-' + version.id">
|
||||
<td class="version-row-element-version">{{version?.version}}</td>
|
||||
<td class="version-row-element-item">
|
||||
<span *ngVar="(version?.item | async)?.payload as item">
|
||||
<a *ngIf="item" [routerLink]="[(itemPageRoutes$ | async)[item?.id]]">{{item?.handle}}</a>
|
||||
<span *ngIf="version?.id === itemVersion?.id">*</span>
|
||||
</span>
|
||||
</td>
|
||||
<td *ngIf="(hasEpersons$ | async)" class="version-row-element-editor">
|
||||
<span *ngVar="(version?.eperson | async)?.payload as eperson">
|
||||
<a *ngIf="eperson" [href]="'mailto:' + eperson?.email">{{eperson?.name}}</a>
|
||||
</span>
|
||||
</td>
|
||||
<td class="version-row-element-date">{{version?.created}}</td>
|
||||
<td class="version-row-element-summary">{{version?.summary}}</td>
|
||||
<td class="version-row-element-date">{{version?.created | date : 'yyyy-MM-dd HH:mm:ss'}}</td>
|
||||
<td class="version-row-element-summary">
|
||||
<ng-container *ngIf="isThisBeingEdited(version); then editSummary else showSummary"></ng-container>
|
||||
<ng-template #showSummary>{{version?.summary}}</ng-template>
|
||||
<ng-template #editSummary>
|
||||
<input class="form-control" type="text" [(ngModel)]="versionBeingEditedSummary"/>
|
||||
</ng-template>
|
||||
</td>
|
||||
<td class="version-row-element-actions" *ngIf="displayActions">
|
||||
<div class="btn-group edit-field">
|
||||
<!--EDIT / SAVE-->
|
||||
<button class="btn btn-outline-primary btn-sm"
|
||||
*ngIf="!isThisBeingEdited(version)"
|
||||
[disabled]="isAnyBeingEdited()"
|
||||
(click)="enableVersionEditing(version)"
|
||||
title="{{'item.version.history.table.action.editSummary' | translate}}">
|
||||
<i class="fas fa-edit fa-fw"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-success btn-sm"
|
||||
*ngIf="isThisBeingEdited(version)"
|
||||
(click)="onSummarySubmit()"
|
||||
title="{{'item.version.history.table.action.saveSummary' | translate}}">
|
||||
<i class="fas fa-check fa-fw"></i>
|
||||
</button>
|
||||
<!--CREATE-->
|
||||
<button class="btn btn-outline-primary btn-sm"
|
||||
[disabled]="isAnyBeingEdited()"
|
||||
(click)="createNewVersion(version)"
|
||||
title="{{'item.version.history.table.action.newVersion' | translate}}">
|
||||
<i class="fas fa-code-branch fa-fw"></i>
|
||||
</button>
|
||||
<!--DELETE-->
|
||||
<button class="btn btn-sm"
|
||||
[ngClass]="isAnyBeingEdited() ? 'btn-outline-primary' : 'btn-outline-danger'"
|
||||
[disabled]="isAnyBeingEdited()"
|
||||
(click)="deleteVersion(version)"
|
||||
title="{{'item.version.history.table.action.deleteVersion' | translate}}">
|
||||
<i class="fas fa-trash fa-fw"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm"
|
||||
[ngClass]="isThisBeingEdited(version) ? 'btn-outline-warning' : 'btn-outline-primary'"
|
||||
[disabled]="!isAnyBeingEdited() || !isThisBeingEdited(version)"
|
||||
(click)="disableSummaryEditing()"
|
||||
title="{{'item.version.history.table.action.discardSummary' | translate}}">
|
||||
<i class="fas fa-undo-alt fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>* {{"item.version.history.selected" | translate}}</div>
|
||||
</ds-pagination>
|
||||
<ds-alert *ngIf="!itemVersion || versions?.page?.length === 0" [content]="'item.version.history.empty'" [type]="AlertTypeEnum.Info"></ds-alert>
|
||||
<ds-alert *ngIf="!itemVersion || versions?.page?.length === 0" [content]="'item.version.history.empty'"
|
||||
[type]="AlertTypeEnum.Info"></ds-alert>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -0,0 +1,9 @@
|
||||
.left-column {
|
||||
float: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.right-column {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
@@ -2,14 +2,17 @@ import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { Version } from '../../../core/shared/version.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable } from 'rxjs';
|
||||
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
|
||||
import { VersionHistory } from '../../../core/shared/version-history.model';
|
||||
import {
|
||||
getAllSucceededRemoteData,
|
||||
getAllSucceededRemoteDataPayload,
|
||||
getFirstCompletedRemoteData,
|
||||
getFirstSucceededRemoteData,
|
||||
getFirstSucceededRemoteDataPayload,
|
||||
getRemoteDataPayload
|
||||
} from '../../../core/shared/operators';
|
||||
import { map, startWith, switchMap } from 'rxjs/operators';
|
||||
import { map, startWith, switchMap, take } from 'rxjs/operators';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||
import { PaginationComponentOptions } from '../../pagination/pagination-component-options.model';
|
||||
import { VersionHistoryDataService } from '../../../core/data/version-history-data.service';
|
||||
@@ -19,15 +22,26 @@ import { followLink } from '../../utils/follow-link-config.model';
|
||||
import { hasValue, hasValueOperator } from '../../empty.util';
|
||||
import { PaginationService } from '../../../core/pagination/pagination.service';
|
||||
import { getItemPageRoute } from '../../../item-page/item-page-routing-paths';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ItemVersionsSummaryModalComponent } from './item-versions-summary-modal/item-versions-summary-modal.component';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ItemVersionsDeleteModalComponent } from './item-versions-delete-modal/item-versions-delete-modal.component';
|
||||
import { VersionDataService } from '../../../core/data/version-data.service';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-versions',
|
||||
templateUrl: './item-versions.component.html'
|
||||
templateUrl: './item-versions.component.html',
|
||||
styleUrls: ['./item-versions.component.scss']
|
||||
})
|
||||
|
||||
/**
|
||||
* Component listing all available versions of the history the provided item is a part of
|
||||
*/
|
||||
export class ItemVersionsComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* The item to display a version history for
|
||||
*/
|
||||
@@ -45,6 +59,16 @@ export class ItemVersionsComponent implements OnInit {
|
||||
*/
|
||||
@Input() displayTitle = true;
|
||||
|
||||
/**
|
||||
* Whether or not to display the action buttons (delete/create/edit version)
|
||||
*/
|
||||
@Input() displayActions: boolean;
|
||||
|
||||
/**
|
||||
* Array of active subscriptions
|
||||
*/
|
||||
subs: Subscription[] = [];
|
||||
|
||||
/**
|
||||
* The AlertType enumeration
|
||||
* @type {AlertType}
|
||||
@@ -101,11 +125,182 @@ export class ItemVersionsComponent implements OnInit {
|
||||
[itemId: string]: string
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Emits when the versionsRD$ must be refreshed
|
||||
* (should be used when a new version has been created)
|
||||
*/
|
||||
refreshSubject = new BehaviorSubject<any>(null);
|
||||
|
||||
/**
|
||||
* The number of the version whose summary is currently being edited
|
||||
*/
|
||||
versionBeingEditedNumber: string;
|
||||
|
||||
/**
|
||||
* The id of the version whose summary is currently being edited
|
||||
*/
|
||||
versionBeingEditedId: string;
|
||||
|
||||
/**
|
||||
* The summary currently being edited
|
||||
*/
|
||||
versionBeingEditedSummary: string;
|
||||
|
||||
constructor(private versionHistoryService: VersionHistoryDataService,
|
||||
private paginationService: PaginationService
|
||||
private versionService: VersionDataService,
|
||||
private itemService: ItemDataService,
|
||||
private paginationService: PaginationService,
|
||||
private formBuilder: FormBuilder,
|
||||
private modalService: NgbModal,
|
||||
private notificationsService: NotificationsService,
|
||||
private translateService: TranslateService,
|
||||
// private cacheService: ObjectCacheService,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* True when a version is being edited
|
||||
* (used to disable buttons for other versions)
|
||||
*/
|
||||
isAnyBeingEdited(): boolean {
|
||||
return this.versionBeingEditedNumber != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the specified version is being edited
|
||||
* (used to show input field and to change buttons for specified version)
|
||||
*/
|
||||
isThisBeingEdited(version): boolean {
|
||||
return version?.version === this.versionBeingEditedNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables editing for the specified version
|
||||
*/
|
||||
enableVersionEditing(version): void {
|
||||
this.versionBeingEditedSummary = version?.summary;
|
||||
this.versionBeingEditedNumber = version?.version;
|
||||
this.versionBeingEditedId = version?.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables editing for the specified version and discards all pending changes
|
||||
*/
|
||||
disableSummaryEditing(): void {
|
||||
this.versionBeingEditedSummary = undefined;
|
||||
this.versionBeingEditedNumber = undefined;
|
||||
this.versionBeingEditedId = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies changes to version currently being edited
|
||||
*/
|
||||
onSummarySubmit() {
|
||||
|
||||
const successMessageKey = 'item.version.edit.notification.success';
|
||||
const failureMessageKey = 'item.version.edit.notification.failure';
|
||||
|
||||
this.versionService.findById(this.versionBeingEditedId).pipe(getFirstSucceededRemoteData()).subscribe(
|
||||
(findRes) => {
|
||||
const updatedVersion =
|
||||
Object.assign({}, findRes.payload, {
|
||||
summary: this.versionBeingEditedSummary,
|
||||
});
|
||||
this.versionService.update(updatedVersion).pipe(take(1)).subscribe(
|
||||
(updateRes) => {
|
||||
// TODO check
|
||||
if (updateRes.hasSucceeded) {
|
||||
this.notificationsService.success(null, this.translateService.get(successMessageKey, {'version': this.versionBeingEditedNumber}));
|
||||
} else {
|
||||
this.notificationsService.warning(null, this.translateService.get(failureMessageKey, {'version': this.versionBeingEditedNumber}));
|
||||
}
|
||||
this.disableSummaryEditing();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the specified version
|
||||
* @param version the version to be deleted
|
||||
*/
|
||||
deleteVersion(version) {
|
||||
const successMessageKey = 'item.version.delete.notification.success';
|
||||
const failureMessageKey = 'item.version.delete.notification.failure';
|
||||
const versionNumber = version.version;
|
||||
|
||||
// Open modal
|
||||
const activeModal = this.modalService.open(ItemVersionsDeleteModalComponent);
|
||||
activeModal.componentInstance.versionNumber = version.version;
|
||||
activeModal.componentInstance.firstVersion = false;
|
||||
|
||||
// On modal submit/dismiss
|
||||
activeModal.result.then(() => {
|
||||
version.item.pipe(getFirstSucceededRemoteDataPayload()).subscribe((getItemRes) => {
|
||||
this.itemService.delete(getItemRes.id).pipe(getFirstCompletedRemoteData()).subscribe(
|
||||
(deleteItemRes) => {
|
||||
console.log(JSON.stringify(deleteItemRes));
|
||||
if (deleteItemRes.hasSucceeded) {
|
||||
this.notificationsService.success(null, this.translateService.get(successMessageKey, {'version': versionNumber}));
|
||||
this.refreshSubject.next(null);
|
||||
} else {
|
||||
this.notificationsService.error(null, this.translateService.get(failureMessageKey, {'version': versionNumber}));
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
/*version.item.pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
switchMap((getItemRes) => this.itemService.delete(getItemRes.id))
|
||||
).subscribe(
|
||||
getFirstCompletedRemoteData(),
|
||||
map((deleteItemRes) => {
|
||||
console.log(JSON.stringify(deleteItemRes));
|
||||
if (deleteItemRes.hasSucceeded) {
|
||||
this.notificationsService.success(null, this.translateService.get(successMessageKey, {'version': versionNumber}));
|
||||
} else {
|
||||
this.notificationsService.warning(null, this.translateService.get(failureMessageKey, {'version': versionNumber}));
|
||||
}
|
||||
}
|
||||
)
|
||||
);*/
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new version starting from the specified one
|
||||
* @param version the version from which a new one will be created
|
||||
*/
|
||||
createNewVersion(version) {
|
||||
const successMessageKey = 'item.version.create.notification.success';
|
||||
const failureMessageKey = 'item.version.create.notification.failure';
|
||||
const versionNumber = version.version;
|
||||
|
||||
// Open modal
|
||||
const activeModal = this.modalService.open(ItemVersionsSummaryModalComponent);
|
||||
activeModal.componentInstance.versionNumber = versionNumber;
|
||||
|
||||
// On modal submit/dismiss
|
||||
activeModal.result.then((modalResult) => {
|
||||
const summary = modalResult;
|
||||
version.item.pipe(getFirstSucceededRemoteDataPayload()).subscribe((item) => {
|
||||
|
||||
const itemHref = item._links.self.href;
|
||||
|
||||
this.versionHistoryService.createVersion(itemHref, summary).pipe(take(1)).subscribe((postResult) => {
|
||||
if (postResult.hasSucceeded) {
|
||||
const newVersionNumber = postResult.payload.version;
|
||||
this.notificationsService.success(null, this.translateService.get(successMessageKey, {version: newVersionNumber}));
|
||||
this.refreshSubject.next(null);
|
||||
} else {
|
||||
this.notificationsService.error(null, this.translateService.get(failureMessageKey));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all observables
|
||||
*/
|
||||
@@ -124,12 +319,24 @@ export class ItemVersionsComponent implements OnInit {
|
||||
hasValueOperator(),
|
||||
);
|
||||
const currentPagination = this.paginationService.getCurrentPagination(this.options.id, this.options);
|
||||
this.versionsRD$ = observableCombineLatest(versionHistory$, currentPagination).pipe(
|
||||
switchMap(([versionHistory, options]: [VersionHistory, PaginationComponentOptions]) =>
|
||||
this.versionHistoryService.getVersions(versionHistory.id,
|
||||
this.versionsRD$ = observableCombineLatest([versionHistory$, currentPagination]).pipe(
|
||||
switchMap(([versionHistory, options]: [VersionHistory, PaginationComponentOptions]) => {
|
||||
return this.versionHistoryService.getVersions(versionHistory.id,
|
||||
new PaginatedSearchOptions({pagination: Object.assign({}, options, {currentPage: options.currentPage})}),
|
||||
true, true, followLink('item'), followLink('eperson')))
|
||||
true, true, followLink('item'), followLink('eperson'));
|
||||
})
|
||||
);
|
||||
// Refresh the table when refreshSubject emits
|
||||
this.subs.push(this.refreshSubject.pipe(switchMap(() => {
|
||||
return observableCombineLatest([versionHistory$, currentPagination]).pipe(
|
||||
take(1),
|
||||
switchMap(([versionHistory, options]: [VersionHistory, PaginationComponentOptions]) => {
|
||||
return this.versionHistoryService.getVersions(versionHistory.id,
|
||||
new PaginatedSearchOptions({pagination: Object.assign({}, options, {currentPage: options.currentPage})}),
|
||||
false, true, followLink('item'), followLink('eperson'));
|
||||
})
|
||||
);
|
||||
})).subscribe());
|
||||
this.hasEpersons$ = this.versionsRD$.pipe(
|
||||
getAllSucceededRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
@@ -150,8 +357,15 @@ export class ItemVersionsComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.cleanupSubscribes();
|
||||
this.paginationService.clearPagination(this.options.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsub all subscriptions
|
||||
*/
|
||||
cleanupSubscribes() {
|
||||
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -211,6 +211,7 @@ import { CollectionSidebarSearchListElementComponent } from './object-list/sideb
|
||||
import { CommunitySidebarSearchListElementComponent } from './object-list/sidebar-search-list-element/community/community-sidebar-search-list-element.component';
|
||||
import { AuthorizedCollectionSelectorComponent } from './dso-selector/dso-selector/authorized-collection-selector/authorized-collection-selector.component';
|
||||
import { DsoPageEditButtonComponent } from './dso-page/dso-page-edit-button/dso-page-edit-button.component';
|
||||
import { DsoPageVersionButtonComponent } from './dso-page/dso-page-version-button/dso-page-version-button.component';
|
||||
import { HoverClassDirective } from './hover-class.directive';
|
||||
import { ValidationSuggestionsComponent } from './input-suggestions/validation-suggestions/validation-suggestions.component';
|
||||
import { ItemAlertsComponent } from './item/item-alerts/item-alerts.component';
|
||||
@@ -233,6 +234,8 @@ import { OnClickMenuItemComponent } from './menu/menu-item/onclick-menu-item.com
|
||||
import { TextMenuItemComponent } from './menu/menu-item/text-menu-item.component';
|
||||
import { ThemedConfigurationSearchPageComponent } from '../search-page/themed-configuration-search-page.component';
|
||||
import { SearchNavbarComponent } from '../search-navbar/search-navbar.component';
|
||||
import { ItemVersionsSummaryModalComponent } from './item/item-versions/item-versions-summary-modal/item-versions-summary-modal.component';
|
||||
import { ItemVersionsDeleteModalComponent } from './item/item-versions/item-versions-delete-modal/item-versions-delete-modal.component';
|
||||
|
||||
/**
|
||||
* Declaration needed to make sure all decorator functions are called in time
|
||||
@@ -534,6 +537,7 @@ const SHARED_ITEM_PAGE_COMPONENTS = [
|
||||
MetadataFieldWrapperComponent,
|
||||
MetadataValuesComponent,
|
||||
DsoPageEditButtonComponent,
|
||||
DsoPageVersionButtonComponent,
|
||||
ItemAlertsComponent,
|
||||
GenericItemPageFieldComponent,
|
||||
MetadataRepresentationListComponent,
|
||||
@@ -584,7 +588,9 @@ const DIRECTIVES = [
|
||||
...COMPONENTS,
|
||||
...DIRECTIVES,
|
||||
...SHARED_ITEM_PAGE_COMPONENTS,
|
||||
...SHARED_SEARCH_PAGE_COMPONENTS
|
||||
...SHARED_SEARCH_PAGE_COMPONENTS,
|
||||
ItemVersionsSummaryModalComponent,
|
||||
ItemVersionsDeleteModalComponent
|
||||
],
|
||||
providers: [
|
||||
...PROVIDERS
|
||||
|
@@ -1910,6 +1910,8 @@
|
||||
|
||||
"item.page.return": "Back",
|
||||
|
||||
"item.page.version": "Create new version",
|
||||
|
||||
"item.preview.dc.identifier.uri": "Identifier:",
|
||||
|
||||
"item.preview.dc.contributor.author": "Authors:",
|
||||
@@ -1965,6 +1967,8 @@
|
||||
|
||||
"item.version.history.selected": "Selected version",
|
||||
|
||||
"item.version.history.selected.alert": "You are currently viewing version {{version}} of the item.",
|
||||
|
||||
"item.version.history.table.version": "Version",
|
||||
|
||||
"item.version.history.table.item": "Item",
|
||||
@@ -1975,11 +1979,68 @@
|
||||
|
||||
"item.version.history.table.summary": "Summary",
|
||||
|
||||
"item.version.history.table.actions": "Action",
|
||||
|
||||
|
||||
"item.version.history.table.action.editSummary": "Edit summary",
|
||||
|
||||
"item.version.history.table.action.saveSummary": "Save summary edits",
|
||||
|
||||
"item.version.history.table.action.discardSummary": "Discard summary edits",
|
||||
|
||||
"item.version.history.table.action.newVersion": "Create new version from this one",
|
||||
|
||||
"item.version.history.table.action.deleteVersion": "Delete version",
|
||||
|
||||
|
||||
"item.version.notice": "This is not the latest version of this item. The latest version can be found <a href='{{destination}}'>here</a>.",
|
||||
|
||||
|
||||
"item.version.create.modal.header": "New version",
|
||||
|
||||
"item.version.create.modal.text": "Create a new version for this item",
|
||||
|
||||
"item.version.create.modal.text.startingFrom": "starting from version {{version}}",
|
||||
|
||||
"item.version.create.modal.button.confirm": "Create",
|
||||
|
||||
"item.version.create.modal.button.confirm.tooltip": "Create new version",
|
||||
|
||||
"item.version.create.modal.button.cancel": "Cancel",
|
||||
|
||||
"item.version.create.modal.button.cancel.tooltip": "Do not create new version",
|
||||
|
||||
"item.version.create.modal.form.summary.label": "Summary",
|
||||
|
||||
"item.version.create.modal.form.summary.placeholder": "Insert the summary for the new version",
|
||||
|
||||
"item.version.create.notification.success" : "New version has been created with version number {{version}}",
|
||||
|
||||
"item.version.create.notification.failure" : "New version has not been created",
|
||||
|
||||
|
||||
"item.version.delete.modal.header": "Delete version",
|
||||
|
||||
"item.version.delete.modal.text": "Do you want to delete version {{version}}?",
|
||||
|
||||
"item.version.delete.modal.button.confirm": "Delete",
|
||||
|
||||
"item.version.delete.modal.button.confirm.tooltip": "Delete this version",
|
||||
|
||||
"item.version.delete.modal.button.cancel": "Cancel",
|
||||
|
||||
"item.version.delete.modal.button.cancel.tooltip": "Do not delete this version",
|
||||
|
||||
"item.version.delete.notification.success" : "Version number {{version}} has been deleted",
|
||||
|
||||
"item.version.delete.notification.failure" : "Version number {{version}} has not been deleted",
|
||||
|
||||
|
||||
"item.version.edit.notification.success" : "The summary of version number {{version}} has been changed",
|
||||
|
||||
"item.version.edit.notification.failure" : "The summary of version number {{version}} has not been changed",
|
||||
|
||||
|
||||
|
||||
"journal.listelement.badge": "Journal",
|
||||
|
||||
|
Reference in New Issue
Block a user