mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
[DURACOM-248] move element version table row logic to a component
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
<div class="left-column">
|
||||||
|
<span *ngIf="(workspaceId$ | async) || (workflowId$ | async); then versionNumberWithoutLink else versionNumberWithLink"></span>
|
||||||
|
<ng-template #versionNumberWithLink>
|
||||||
|
<a [routerLink]="getVersionRoute(version.id)">{{version.version}}</a>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #versionNumberWithoutLink>
|
||||||
|
{{version.version}}
|
||||||
|
</ng-template>
|
||||||
|
<span *ngIf="version?.id === itemVersion?.id">*</span>
|
||||||
|
|
||||||
|
<span *ngIf="workspaceId$ | async" class="text-light badge badge-primary ml-3">
|
||||||
|
{{ "item.version.history.table.workspaceItem" | translate }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span *ngIf="workflowId$ | async" class="text-light badge badge-info ml-3">
|
||||||
|
{{ "item.version.history.table.workflowItem" | translate }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="right-column">
|
||||||
|
|
||||||
|
<div class="btn-group edit-field space-children-mr" *ngIf="displayActions">
|
||||||
|
<!--EDIT WORKSPACE ITEM-->
|
||||||
|
<button class="btn btn-outline-primary btn-sm version-row-element-edit"
|
||||||
|
*ngIf="workspaceId$ | async"
|
||||||
|
(click)="editWorkspaceItem(workspaceId$)"
|
||||||
|
title="{{'item.version.history.table.action.editWorkspaceItem' | translate }}">
|
||||||
|
<i class="fas fa-pencil-alt fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
<!--CREATE-->
|
||||||
|
<ng-container *ngIf="canCreateVersion$ | async">
|
||||||
|
<button class="btn btn-outline-primary btn-sm version-row-element-create"
|
||||||
|
[disabled]="isAnyBeingEdited() || hasDraftVersion"
|
||||||
|
(click)="createNewVersion(version)"
|
||||||
|
title="{{createVersionTitle | translate }}">
|
||||||
|
<i class="fas fa-code-branch fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
<!--DELETE-->
|
||||||
|
<ng-container *ngIf="canDeleteVersion$ | async">
|
||||||
|
<button class="btn btn-sm version-row-element-delete"
|
||||||
|
[ngClass]="isAnyBeingEdited() ? 'btn-outline-primary' : 'btn-outline-danger'"
|
||||||
|
[disabled]="isAnyBeingEdited()"
|
||||||
|
(click)="deleteVersion(version, version.id === itemVersion.id)"
|
||||||
|
title="{{'item.version.history.table.action.deleteVersion' | translate}}">
|
||||||
|
<i class="fas fa-trash fa-fw"></i>
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
@@ -0,0 +1,9 @@
|
|||||||
|
.left-column {
|
||||||
|
float: left;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-column {
|
||||||
|
float: right;
|
||||||
|
text-align: right;
|
||||||
|
}
|
@@ -0,0 +1,26 @@
|
|||||||
|
import {
|
||||||
|
ComponentFixture,
|
||||||
|
TestBed,
|
||||||
|
} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ItemVersionsRowElementVersionComponent } from './item-versions-row-element-version.component';
|
||||||
|
|
||||||
|
describe('ItemVersionsRowElementVersionComponent', () => {
|
||||||
|
let component: ItemVersionsRowElementVersionComponent;
|
||||||
|
let fixture: ComponentFixture<ItemVersionsRowElementVersionComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ItemVersionsRowElementVersionComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ItemVersionsRowElementVersionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,301 @@
|
|||||||
|
import {
|
||||||
|
AsyncPipe,
|
||||||
|
NgClass,
|
||||||
|
NgIf,
|
||||||
|
} from '@angular/common';
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
Router,
|
||||||
|
RouterLink,
|
||||||
|
} from '@angular/router';
|
||||||
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import {
|
||||||
|
TranslateModule,
|
||||||
|
TranslateService,
|
||||||
|
} from '@ngx-translate/core';
|
||||||
|
import {
|
||||||
|
combineLatest, concatMap,
|
||||||
|
Observable,
|
||||||
|
of,
|
||||||
|
} from 'rxjs';
|
||||||
|
import {
|
||||||
|
map,
|
||||||
|
mergeMap,
|
||||||
|
switchMap,
|
||||||
|
take,
|
||||||
|
tap,
|
||||||
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
|
||||||
|
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
|
||||||
|
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||||
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
|
import { VersionDataService } from '../../../core/data/version-data.service';
|
||||||
|
import { VersionHistoryDataService } from '../../../core/data/version-history-data.service';
|
||||||
|
import { Item } from '../../../core/shared/item.model';
|
||||||
|
import {
|
||||||
|
getFirstCompletedRemoteData,
|
||||||
|
getFirstSucceededRemoteDataPayload,
|
||||||
|
} from '../../../core/shared/operators';
|
||||||
|
import { Version } from '../../../core/shared/version.model';
|
||||||
|
import { VersionHistory } from '../../../core/shared/version-history.model';
|
||||||
|
import { WorkspaceItem } from '../../../core/submission/models/workspaceitem.model';
|
||||||
|
import { WorkflowItemDataService } from '../../../core/submission/workflowitem-data.service';
|
||||||
|
import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service';
|
||||||
|
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||||
|
import {
|
||||||
|
getItemEditVersionhistoryRoute,
|
||||||
|
getItemVersionRoute,
|
||||||
|
} from '../../item-page-routing-paths';
|
||||||
|
import { ItemVersionsDeleteModalComponent } from '../item-versions-delete-modal/item-versions-delete-modal.component';
|
||||||
|
import { ItemVersionsSharedService } from '../item-versions-shared.service';
|
||||||
|
import { ItemVersionsSummaryModalComponent } from '../item-versions-summary-modal/item-versions-summary-modal.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-item-versions-row-element-version',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
AsyncPipe,
|
||||||
|
RouterLink,
|
||||||
|
TranslateModule,
|
||||||
|
NgClass,
|
||||||
|
NgIf,
|
||||||
|
],
|
||||||
|
templateUrl: './item-versions-row-element-version.component.html',
|
||||||
|
styleUrl: './item-versions-row-element-version.component.scss',
|
||||||
|
})
|
||||||
|
export class ItemVersionsRowElementVersionComponent implements OnInit {
|
||||||
|
@Input() hasDraftVersion: boolean | null;
|
||||||
|
@Input() version: Version;
|
||||||
|
@Input() itemVersion: Version;
|
||||||
|
@Input() item: Item;
|
||||||
|
@Input() displayActions: boolean;
|
||||||
|
@Input() versionBeingEditedNumber: number;
|
||||||
|
|
||||||
|
@Output() versionsHistoryChange = new EventEmitter<Observable<VersionHistory>>();
|
||||||
|
|
||||||
|
workspaceId$: Observable<string>;
|
||||||
|
workflowId$: Observable<string>;
|
||||||
|
canDeleteVersion$: Observable<boolean>;
|
||||||
|
canCreateVersion$: Observable<boolean>;
|
||||||
|
|
||||||
|
createVersionTitle: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private workspaceItemDataService: WorkspaceitemDataService,
|
||||||
|
private workflowItemDataService: WorkflowItemDataService,
|
||||||
|
private router: Router,
|
||||||
|
private itemService: ItemDataService,
|
||||||
|
private authorizationService: AuthorizationDataService,
|
||||||
|
private itemVersionShared: ItemVersionsSharedService,
|
||||||
|
private versionHistoryService: VersionHistoryDataService,
|
||||||
|
private versionService: VersionDataService,
|
||||||
|
private notificationsService: NotificationsService,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.workspaceId$ = this.getWorkspaceId(this.version.item);
|
||||||
|
this.workflowId$ = this.getWorkflowId(this.version.item);
|
||||||
|
this.canDeleteVersion$ = this.canDeleteVersion(this.version);
|
||||||
|
this.canCreateVersion$ = this.authorizationService.isAuthorized(FeatureID.CanCreateVersion, this.item.self);
|
||||||
|
|
||||||
|
this.createVersionTitle = this.hasDraftVersion ? 'item.version.history.table.action.hasDraft' : 'item.version.history.table.action.newVersion';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ID of the workspace item, if present, otherwise return undefined
|
||||||
|
* @param versionItem the item for which retrieve the workspace item id
|
||||||
|
*/
|
||||||
|
getWorkspaceId(versionItem: Observable<RemoteData<Item>>): Observable<string> {
|
||||||
|
if (!this.hasDraftVersion) {
|
||||||
|
return of(undefined);
|
||||||
|
}
|
||||||
|
return versionItem.pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
map((item: Item) => item.uuid),
|
||||||
|
switchMap((itemUuid: string) => this.workspaceItemDataService.findByItem(itemUuid, true)),
|
||||||
|
getFirstCompletedRemoteData<WorkspaceItem>(),
|
||||||
|
map((res: RemoteData<WorkspaceItem>) => res?.payload?.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ID of the workflow item, if present, otherwise return undefined
|
||||||
|
* @param versionItem the item for which retrieve the workspace item id
|
||||||
|
*/
|
||||||
|
getWorkflowId(versionItem: Observable<RemoteData<Item>>): Observable<string> {
|
||||||
|
return this.getWorkspaceId(versionItem).pipe(
|
||||||
|
concatMap((workspaceId: string) => {
|
||||||
|
if (workspaceId) {
|
||||||
|
return of(undefined);
|
||||||
|
}
|
||||||
|
return versionItem.pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
map((item: Item) => item.uuid),
|
||||||
|
switchMap((itemUuid: string) => this.workflowItemDataService.findByItem(itemUuid, true)),
|
||||||
|
getFirstCompletedRemoteData<WorkspaceItem>(),
|
||||||
|
map((res: RemoteData<WorkspaceItem>) => res?.payload?.id),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* redirect to the edit page of the workspace item
|
||||||
|
* @param id$ the id of the workspace item
|
||||||
|
*/
|
||||||
|
editWorkspaceItem(id$: Observable<string>) {
|
||||||
|
id$.subscribe((id) => {
|
||||||
|
void this.router.navigateByUrl('workspaceitems/' + id + '/edit');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the current user can delete the version
|
||||||
|
* @param version
|
||||||
|
*/
|
||||||
|
canDeleteVersion(version: Version): Observable<boolean> {
|
||||||
|
return this.authorizationService.isAuthorized(FeatureID.CanDeleteVersion, version.self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the route to the specified version
|
||||||
|
* @param versionId the ID of the version for which the route will be retrieved
|
||||||
|
*/
|
||||||
|
getVersionRoute(versionId: string) {
|
||||||
|
return getItemVersionRoute(versionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new version starting from the specified one
|
||||||
|
* @param version the version from which a new one will be created
|
||||||
|
*/
|
||||||
|
createNewVersion(version: Version) {
|
||||||
|
const versionNumber = version.version;
|
||||||
|
|
||||||
|
// Open modal and set current version number
|
||||||
|
const activeModal = this.modalService.open(ItemVersionsSummaryModalComponent);
|
||||||
|
activeModal.componentInstance.versionNumber = versionNumber;
|
||||||
|
|
||||||
|
// On createVersionEvent emitted create new version and notify
|
||||||
|
activeModal.componentInstance.createVersionEvent.pipe(
|
||||||
|
mergeMap((summary: string) => combineLatest([
|
||||||
|
of(summary),
|
||||||
|
version.item.pipe(getFirstSucceededRemoteDataPayload()),
|
||||||
|
])),
|
||||||
|
mergeMap(([summary, item]: [string, Item]) => this.versionHistoryService.createVersion(item._links.self.href, summary)),
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
// close model (should be displaying loading/waiting indicator) when version creation failed/succeeded
|
||||||
|
tap(() => activeModal.close()),
|
||||||
|
// show success/failure notification
|
||||||
|
tap((newVersionRD: RemoteData<Version>) => {
|
||||||
|
this.itemVersionShared.notifyCreateNewVersion(newVersionRD);
|
||||||
|
if (newVersionRD.hasSucceeded) {
|
||||||
|
const versionHistory$ = this.versionService.getHistoryFromVersion(version).pipe(
|
||||||
|
tap((versionHistory: VersionHistory) => {
|
||||||
|
this.itemService.invalidateItemCache(this.item.uuid);
|
||||||
|
this.versionHistoryService.invalidateVersionHistoryCache(versionHistory.id);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
this.versionsHistoryChange.emit(versionHistory$);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
// get workspace item
|
||||||
|
getFirstSucceededRemoteDataPayload<Version>(),
|
||||||
|
switchMap((newVersion: Version) => this.itemService.findByHref(newVersion._links.item.href)),
|
||||||
|
getFirstSucceededRemoteDataPayload<Item>(),
|
||||||
|
switchMap((newVersionItem: Item) => this.workspaceItemDataService.findByItem(newVersionItem.uuid, true, false)),
|
||||||
|
getFirstSucceededRemoteDataPayload<WorkspaceItem>(),
|
||||||
|
).subscribe((wsItem) => {
|
||||||
|
const wsiId = wsItem.id;
|
||||||
|
const route = 'workspaceitems/' + wsiId + '/edit';
|
||||||
|
this.router.navigateByUrl(route);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the specified version, notify the success/failure and redirect to latest version
|
||||||
|
* @param version the version to be deleted
|
||||||
|
* @param redirectToLatest force the redirect to the latest version in the history
|
||||||
|
*/
|
||||||
|
deleteVersion(version: Version, redirectToLatest: boolean): void {
|
||||||
|
const successMessageKey = 'item.version.delete.notification.success';
|
||||||
|
const failureMessageKey = 'item.version.delete.notification.failure';
|
||||||
|
const versionNumber = version.version;
|
||||||
|
const versionItem$ = version.item;
|
||||||
|
|
||||||
|
// Open modal
|
||||||
|
const activeModal = this.modalService.open(ItemVersionsDeleteModalComponent);
|
||||||
|
activeModal.componentInstance.versionNumber = version.version;
|
||||||
|
activeModal.componentInstance.firstVersion = false;
|
||||||
|
|
||||||
|
// On modal submit/dismiss
|
||||||
|
activeModal.componentInstance.response.pipe(take(1)).subscribe((ok) => {
|
||||||
|
if (ok) {
|
||||||
|
versionItem$.pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload<Item>(),
|
||||||
|
// Retrieve version history
|
||||||
|
mergeMap((item: Item) => combineLatest([
|
||||||
|
of(item),
|
||||||
|
this.versionHistoryService.getVersionHistoryFromVersion$(version),
|
||||||
|
])),
|
||||||
|
// Delete item
|
||||||
|
mergeMap(([item, versionHistory]: [Item, VersionHistory]) => combineLatest([
|
||||||
|
this.deleteItemAndGetResult$(item),
|
||||||
|
of(versionHistory),
|
||||||
|
])),
|
||||||
|
// Retrieve new latest version
|
||||||
|
mergeMap(([deleteItemResult, versionHistory]: [boolean, VersionHistory]) => combineLatest([
|
||||||
|
of(deleteItemResult),
|
||||||
|
this.versionHistoryService.getLatestVersionItemFromHistory$(versionHistory).pipe(
|
||||||
|
tap(() => {
|
||||||
|
this.versionsHistoryChange.emit(of(versionHistory));
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
).subscribe(([deleteHasSucceeded, newLatestVersionItem]: [boolean, Item]) => {
|
||||||
|
// Notify operation result and redirect to latest item
|
||||||
|
if (deleteHasSucceeded) {
|
||||||
|
this.notificationsService.success(null, this.translateService.get(successMessageKey, { 'version': versionNumber }));
|
||||||
|
} else {
|
||||||
|
this.notificationsService.error(null, this.translateService.get(failureMessageKey, { 'version': versionNumber }));
|
||||||
|
}
|
||||||
|
if (redirectToLatest) {
|
||||||
|
const path = getItemEditVersionhistoryRoute(newLatestVersionItem);
|
||||||
|
this.router.navigateByUrl(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the item and get the result of the operation
|
||||||
|
* @param item
|
||||||
|
*/
|
||||||
|
deleteItemAndGetResult$(item: Item): Observable<boolean> {
|
||||||
|
return this.itemService.delete(item.id).pipe(
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
map((deleteItemRes) => deleteItemRes.hasSucceeded),
|
||||||
|
take(1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True when a version is being edited
|
||||||
|
* (used to disable buttons for other versions)
|
||||||
|
*/
|
||||||
|
isAnyBeingEdited(): boolean {
|
||||||
|
return this.versionBeingEditedNumber != null;
|
||||||
|
}
|
||||||
|
}
|
@@ -25,67 +25,13 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let version of versions?.page" [id]="'version-row-' + version.id">
|
<tr *ngFor="let version of versions?.page" [id]="'version-row-' + version.id">
|
||||||
<td class="version-row-element-version">
|
<td class="version-row-element-version">
|
||||||
<!-- Get the ID of the workspace/workflow item (`undefined` if they don't exist).
|
<ds-item-versions-row-element-version [hasDraftVersion]="hasDraftVersion$ | async"
|
||||||
Conditionals inside *ngVar are needed in order to avoid useless calls. -->
|
[version]="version"
|
||||||
<ng-container *ngVar="((hasDraftVersion$ | async) ? getWorkspaceId(version?.item) : undefined) as workspaceId$">
|
[item]="item" [displayActions]="displayActions"
|
||||||
<ng-container *ngVar=" ((workspaceId$ | async) ? undefined : getWorkflowId(version?.item)) as workflowId$">
|
[itemVersion]="itemVersion"
|
||||||
|
[versionBeingEditedNumber]="versionBeingEditedNumber"
|
||||||
<div class="left-column">
|
(versionsHistoryChange)="getAllVersions($event)"
|
||||||
|
></ds-item-versions-row-element-version>
|
||||||
<span *ngIf="(workspaceId$ | async) || (workflowId$ | async); then versionNumberWithoutLink else versionNumberWithLink"></span>
|
|
||||||
<ng-template #versionNumberWithLink>
|
|
||||||
<a [routerLink]="getVersionRoute(version.id)">{{version.version}}</a>
|
|
||||||
</ng-template>
|
|
||||||
<ng-template #versionNumberWithoutLink>
|
|
||||||
{{version.version}}
|
|
||||||
</ng-template>
|
|
||||||
<span *ngIf="version?.id === itemVersion?.id">*</span>
|
|
||||||
|
|
||||||
<span *ngIf="workspaceId$ | async" class="text-light badge badge-primary ml-3">
|
|
||||||
{{ "item.version.history.table.workspaceItem" | translate }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span *ngIf="workflowId$ | async" class="text-light badge badge-info ml-3">
|
|
||||||
{{ "item.version.history.table.workflowItem" | translate }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="right-column">
|
|
||||||
|
|
||||||
<div class="btn-group edit-field space-children-mr" *ngIf="displayActions">
|
|
||||||
<!--EDIT WORKSPACE ITEM-->
|
|
||||||
<button class="btn btn-outline-primary btn-sm version-row-element-edit"
|
|
||||||
*ngIf="workspaceId$ | async"
|
|
||||||
(click)="editWorkspaceItem(workspaceId$)"
|
|
||||||
title="{{'item.version.history.table.action.editWorkspaceItem' | translate }}">
|
|
||||||
<i class="fas fa-pencil-alt fa-fw"></i>
|
|
||||||
</button>
|
|
||||||
<!--CREATE-->
|
|
||||||
<ng-container *ngIf="canCreateVersion$ | async">
|
|
||||||
<button class="btn btn-outline-primary btn-sm version-row-element-create"
|
|
||||||
[disabled]="isAnyBeingEdited() || (hasDraftVersion$ | async)"
|
|
||||||
(click)="createNewVersion(version)"
|
|
||||||
title="{{createVersionTitle$ | async | translate }}">
|
|
||||||
<i class="fas fa-code-branch fa-fw"></i>
|
|
||||||
</button>
|
|
||||||
</ng-container>
|
|
||||||
<!--DELETE-->
|
|
||||||
<ng-container *ngIf="canDeleteVersion$(version) | async">
|
|
||||||
<button class="btn btn-sm version-row-element-delete"
|
|
||||||
[ngClass]="isAnyBeingEdited() ? 'btn-outline-primary' : 'btn-outline-danger'"
|
|
||||||
[disabled]="isAnyBeingEdited()"
|
|
||||||
(click)="deleteVersion(version, version.id===itemVersion.id)"
|
|
||||||
title="{{'item.version.history.table.action.deleteVersion' | translate}}">
|
|
||||||
<i class="fas fa-trash fa-fw"></i>
|
|
||||||
</button>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</ng-container>
|
|
||||||
</ng-container>
|
|
||||||
</td>
|
</td>
|
||||||
<td class="version-row-element-editor" *ngIf="(showSubmitter() | async)">
|
<td class="version-row-element-editor" *ngIf="(showSubmitter() | async)">
|
||||||
{{version?.submitterName}}
|
{{version?.submitterName}}
|
||||||
|
@@ -1,9 +0,0 @@
|
|||||||
.left-column {
|
|
||||||
float: left;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-column {
|
|
||||||
float: right;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
@@ -11,15 +11,8 @@ import {
|
|||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {
|
import { FormsModule } from '@angular/forms';
|
||||||
FormsModule,
|
import { RouterLink } from '@angular/router';
|
||||||
UntypedFormBuilder,
|
|
||||||
} from '@angular/forms';
|
|
||||||
import {
|
|
||||||
Router,
|
|
||||||
RouterLink,
|
|
||||||
} from '@angular/router';
|
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
|
||||||
import {
|
import {
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
TranslateService,
|
TranslateService,
|
||||||
@@ -28,22 +21,18 @@ import {
|
|||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
combineLatest,
|
combineLatest,
|
||||||
Observable,
|
Observable,
|
||||||
of,
|
|
||||||
Subscription,
|
Subscription,
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import {
|
import {
|
||||||
map,
|
map,
|
||||||
mergeMap,
|
|
||||||
startWith,
|
startWith,
|
||||||
switchMap,
|
switchMap,
|
||||||
take,
|
take,
|
||||||
tap,
|
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
|
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
|
||||||
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
|
||||||
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
|
||||||
import { ItemDataService } from '../../core/data/item-data.service';
|
|
||||||
import { PaginatedList } from '../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||||
import { RemoteData } from '../../core/data/remote-data';
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
import { VersionDataService } from '../../core/data/version-data.service';
|
import { VersionDataService } from '../../core/data/version-data.service';
|
||||||
@@ -60,9 +49,6 @@ import {
|
|||||||
} from '../../core/shared/operators';
|
} from '../../core/shared/operators';
|
||||||
import { Version } from '../../core/shared/version.model';
|
import { Version } from '../../core/shared/version.model';
|
||||||
import { VersionHistory } from '../../core/shared/version-history.model';
|
import { VersionHistory } from '../../core/shared/version-history.model';
|
||||||
import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model';
|
|
||||||
import { WorkflowItemDataService } from '../../core/submission/workflowitem-data.service';
|
|
||||||
import { WorkspaceitemDataService } from '../../core/submission/workspaceitem-data.service';
|
|
||||||
import { AlertComponent } from '../../shared/alert/alert.component';
|
import { AlertComponent } from '../../shared/alert/alert.component';
|
||||||
import { AlertType } from '../../shared/alert/alert-type';
|
import { AlertType } from '../../shared/alert/alert-type';
|
||||||
import {
|
import {
|
||||||
@@ -75,21 +61,15 @@ import { PaginationComponentOptions } from '../../shared/pagination/pagination-c
|
|||||||
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
|
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
|
||||||
import { followLink } from '../../shared/utils/follow-link-config.model';
|
import { followLink } from '../../shared/utils/follow-link-config.model';
|
||||||
import { VarDirective } from '../../shared/utils/var.directive';
|
import { VarDirective } from '../../shared/utils/var.directive';
|
||||||
import {
|
import { getItemPageRoute } from '../item-page-routing-paths';
|
||||||
getItemEditVersionhistoryRoute,
|
import { ItemVersionsRowElementVersionComponent } from './item-versions-row-element-version/item-versions-row-element-version.component';
|
||||||
getItemPageRoute,
|
|
||||||
getItemVersionRoute,
|
|
||||||
} from '../item-page-routing-paths';
|
|
||||||
import { ItemVersionsDeleteModalComponent } from './item-versions-delete-modal/item-versions-delete-modal.component';
|
|
||||||
import { ItemVersionsSharedService } from './item-versions-shared.service';
|
|
||||||
import { ItemVersionsSummaryModalComponent } from './item-versions-summary-modal/item-versions-summary-modal.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-item-versions',
|
selector: 'ds-item-versions',
|
||||||
templateUrl: './item-versions.component.html',
|
templateUrl: './item-versions.component.html',
|
||||||
styleUrls: ['./item-versions.component.scss'],
|
styleUrls: ['./item-versions.component.scss'],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [VarDirective, NgIf, AlertComponent, PaginationComponent, NgFor, RouterLink, NgClass, FormsModule, AsyncPipe, DatePipe, TranslateModule],
|
imports: [VarDirective, NgIf, AlertComponent, PaginationComponent, NgFor, RouterLink, NgClass, FormsModule, AsyncPipe, DatePipe, TranslateModule, ItemVersionsRowElementVersionComponent],
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -206,17 +186,10 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
constructor(private versionHistoryService: VersionHistoryDataService,
|
constructor(private versionHistoryService: VersionHistoryDataService,
|
||||||
private versionService: VersionDataService,
|
private versionService: VersionDataService,
|
||||||
private itemService: ItemDataService,
|
|
||||||
private paginationService: PaginationService,
|
private paginationService: PaginationService,
|
||||||
private formBuilder: UntypedFormBuilder,
|
|
||||||
private modalService: NgbModal,
|
|
||||||
private notificationsService: NotificationsService,
|
private notificationsService: NotificationsService,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private router: Router,
|
|
||||||
private itemVersionShared: ItemVersionsSharedService,
|
|
||||||
private authorizationService: AuthorizationDataService,
|
private authorizationService: AuthorizationDataService,
|
||||||
private workspaceItemDataService: WorkspaceitemDataService,
|
|
||||||
private workflowItemDataService: WorkflowItemDataService,
|
|
||||||
private configurationService: ConfigurationDataService,
|
private configurationService: ConfigurationDataService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@@ -255,14 +228,6 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
|
|||||||
this.versionBeingEditedId = undefined;
|
this.versionBeingEditedId = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the route to the specified version
|
|
||||||
* @param versionId the ID of the version for which the route will be retrieved
|
|
||||||
*/
|
|
||||||
getVersionRoute(versionId: string) {
|
|
||||||
return getItemVersionRoute(versionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies changes to version currently being edited
|
* Applies changes to version currently being edited
|
||||||
*/
|
*/
|
||||||
@@ -291,121 +256,6 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete the item and get the result of the operation
|
|
||||||
* @param item
|
|
||||||
*/
|
|
||||||
deleteItemAndGetResult$(item: Item): Observable<boolean> {
|
|
||||||
return this.itemService.delete(item.id).pipe(
|
|
||||||
getFirstCompletedRemoteData(),
|
|
||||||
map((deleteItemRes) => deleteItemRes.hasSucceeded),
|
|
||||||
take(1),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the specified version, notify the success/failure and redirect to latest version
|
|
||||||
* @param version the version to be deleted
|
|
||||||
* @param redirectToLatest force the redirect to the latest version in the history
|
|
||||||
*/
|
|
||||||
deleteVersion(version: Version, redirectToLatest: boolean): void {
|
|
||||||
const successMessageKey = 'item.version.delete.notification.success';
|
|
||||||
const failureMessageKey = 'item.version.delete.notification.failure';
|
|
||||||
const versionNumber = version.version;
|
|
||||||
const versionItem$ = version.item;
|
|
||||||
|
|
||||||
// Open modal
|
|
||||||
const activeModal = this.modalService.open(ItemVersionsDeleteModalComponent);
|
|
||||||
activeModal.componentInstance.versionNumber = version.version;
|
|
||||||
activeModal.componentInstance.firstVersion = false;
|
|
||||||
|
|
||||||
// On modal submit/dismiss
|
|
||||||
activeModal.componentInstance.response.pipe(take(1)).subscribe((ok) => {
|
|
||||||
if (ok) {
|
|
||||||
versionItem$.pipe(
|
|
||||||
getFirstSucceededRemoteDataPayload<Item>(),
|
|
||||||
// Retrieve version history
|
|
||||||
mergeMap((item: Item) => combineLatest([
|
|
||||||
of(item),
|
|
||||||
this.versionHistoryService.getVersionHistoryFromVersion$(version),
|
|
||||||
])),
|
|
||||||
// Delete item
|
|
||||||
mergeMap(([item, versionHistory]: [Item, VersionHistory]) => combineLatest([
|
|
||||||
this.deleteItemAndGetResult$(item),
|
|
||||||
of(versionHistory),
|
|
||||||
])),
|
|
||||||
// Retrieve new latest version
|
|
||||||
mergeMap(([deleteItemResult, versionHistory]: [boolean, VersionHistory]) => combineLatest([
|
|
||||||
of(deleteItemResult),
|
|
||||||
this.versionHistoryService.getLatestVersionItemFromHistory$(versionHistory).pipe(
|
|
||||||
tap(() => {
|
|
||||||
this.getAllVersions(of(versionHistory));
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
])),
|
|
||||||
).subscribe(([deleteHasSucceeded, newLatestVersionItem]: [boolean, Item]) => {
|
|
||||||
// Notify operation result and redirect to latest item
|
|
||||||
if (deleteHasSucceeded) {
|
|
||||||
this.notificationsService.success(null, this.translateService.get(successMessageKey, { 'version': versionNumber }));
|
|
||||||
} else {
|
|
||||||
this.notificationsService.error(null, this.translateService.get(failureMessageKey, { 'version': versionNumber }));
|
|
||||||
}
|
|
||||||
if (redirectToLatest) {
|
|
||||||
const path = getItemEditVersionhistoryRoute(newLatestVersionItem);
|
|
||||||
this.router.navigateByUrl(path);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new version starting from the specified one
|
|
||||||
* @param version the version from which a new one will be created
|
|
||||||
*/
|
|
||||||
createNewVersion(version: Version) {
|
|
||||||
const versionNumber = version.version;
|
|
||||||
|
|
||||||
// Open modal and set current version number
|
|
||||||
const activeModal = this.modalService.open(ItemVersionsSummaryModalComponent);
|
|
||||||
activeModal.componentInstance.versionNumber = versionNumber;
|
|
||||||
|
|
||||||
// On createVersionEvent emitted create new version and notify
|
|
||||||
activeModal.componentInstance.createVersionEvent.pipe(
|
|
||||||
mergeMap((summary: string) => combineLatest([
|
|
||||||
of(summary),
|
|
||||||
version.item.pipe(getFirstSucceededRemoteDataPayload()),
|
|
||||||
])),
|
|
||||||
mergeMap(([summary, item]: [string, Item]) => this.versionHistoryService.createVersion(item._links.self.href, summary)),
|
|
||||||
getFirstCompletedRemoteData(),
|
|
||||||
// close model (should be displaying loading/waiting indicator) when version creation failed/succeeded
|
|
||||||
tap(() => activeModal.close()),
|
|
||||||
// show success/failure notification
|
|
||||||
tap((newVersionRD: RemoteData<Version>) => {
|
|
||||||
this.itemVersionShared.notifyCreateNewVersion(newVersionRD);
|
|
||||||
if (newVersionRD.hasSucceeded) {
|
|
||||||
const versionHistory$ = this.versionService.getHistoryFromVersion(version).pipe(
|
|
||||||
tap((versionHistory: VersionHistory) => {
|
|
||||||
this.itemService.invalidateItemCache(this.item.uuid);
|
|
||||||
this.versionHistoryService.invalidateVersionHistoryCache(versionHistory.id);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
this.getAllVersions(versionHistory$);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
// get workspace item
|
|
||||||
getFirstSucceededRemoteDataPayload<Version>(),
|
|
||||||
switchMap((newVersion: Version) => this.itemService.findByHref(newVersion._links.item.href)),
|
|
||||||
getFirstSucceededRemoteDataPayload<Item>(),
|
|
||||||
switchMap((newVersionItem: Item) => this.workspaceItemDataService.findByItem(newVersionItem.uuid, true, false)),
|
|
||||||
getFirstSucceededRemoteDataPayload<WorkspaceItem>(),
|
|
||||||
).subscribe((wsItem) => {
|
|
||||||
const wsiId = wsItem.id;
|
|
||||||
const route = 'workspaceitems/' + wsiId + '/edit';
|
|
||||||
this.router.navigateByUrl(route);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check is the current user can edit the version summary
|
* Check is the current user can edit the version summary
|
||||||
* @param version
|
* @param version
|
||||||
@@ -444,14 +294,6 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the current user can delete the version
|
|
||||||
* @param version
|
|
||||||
*/
|
|
||||||
canDeleteVersion$(version: Version): Observable<boolean> {
|
|
||||||
return this.authorizationService.isAuthorized(FeatureID.CanDeleteVersion, version.self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all versions for the given version history and store them in versionRD$
|
* Get all versions for the given version history and store them in versionRD$
|
||||||
* @param versionHistory$
|
* @param versionHistory$
|
||||||
@@ -477,44 +319,6 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
|
|||||||
this.getAllVersions(this.versionHistory$);
|
this.getAllVersions(this.versionHistory$);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the ID of the workspace item, if present, otherwise return undefined
|
|
||||||
* @param versionItem the item for which retrieve the workspace item id
|
|
||||||
*/
|
|
||||||
getWorkspaceId(versionItem): Observable<string> {
|
|
||||||
return versionItem.pipe(
|
|
||||||
getFirstSucceededRemoteDataPayload(),
|
|
||||||
map((item: Item) => item.uuid),
|
|
||||||
switchMap((itemUuid: string) => this.workspaceItemDataService.findByItem(itemUuid, true)),
|
|
||||||
getFirstCompletedRemoteData<WorkspaceItem>(),
|
|
||||||
map((res: RemoteData<WorkspaceItem>) => res?.payload?.id ),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the ID of the workflow item, if present, otherwise return undefined
|
|
||||||
* @param versionItem the item for which retrieve the workspace item id
|
|
||||||
*/
|
|
||||||
getWorkflowId(versionItem): Observable<string> {
|
|
||||||
return versionItem.pipe(
|
|
||||||
getFirstSucceededRemoteDataPayload(),
|
|
||||||
map((item: Item) => item.uuid),
|
|
||||||
switchMap((itemUuid: string) => this.workflowItemDataService.findByItem(itemUuid, true)),
|
|
||||||
getFirstCompletedRemoteData<WorkspaceItem>(),
|
|
||||||
map((res: RemoteData<WorkspaceItem>) => res?.payload?.id ),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* redirect to the edit page of the workspace item
|
|
||||||
* @param id$ the id of the workspace item
|
|
||||||
*/
|
|
||||||
editWorkspaceItem(id$: Observable<string>) {
|
|
||||||
id$.subscribe((id) => {
|
|
||||||
this.router.navigateByUrl('workspaceitems/' + id + '/edit');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize all observables
|
* Initialize all observables
|
||||||
*/
|
*/
|
||||||
@@ -532,19 +336,12 @@ export class ItemVersionsComponent implements OnDestroy, OnInit {
|
|||||||
hasValueOperator(),
|
hasValueOperator(),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.canCreateVersion$ = this.authorizationService.isAuthorized(FeatureID.CanCreateVersion, this.item.self);
|
|
||||||
|
|
||||||
// If there is a draft item in the version history the 'Create version' button is disabled and a different tooltip message is shown
|
// If there is a draft item in the version history the 'Create version' button is disabled and a different tooltip message is shown
|
||||||
this.hasDraftVersion$ = this.versionHistoryRD$.pipe(
|
this.hasDraftVersion$ = this.versionHistoryRD$.pipe(
|
||||||
getFirstSucceededRemoteDataPayload(),
|
getFirstSucceededRemoteDataPayload(),
|
||||||
map((res) => Boolean(res?.draftVersion)),
|
map((res) => Boolean(res?.draftVersion)),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.createVersionTitle$ = this.hasDraftVersion$.pipe(
|
|
||||||
take(1),
|
|
||||||
switchMap((res) => of(res ? 'item.version.history.table.action.hasDraft' : 'item.version.history.table.action.newVersion')),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.getAllVersions(this.versionHistory$);
|
this.getAllVersions(this.versionHistory$);
|
||||||
this.hasEpersons$ = this.versionsRD$.pipe(
|
this.hasEpersons$ = this.versionsRD$.pipe(
|
||||||
getAllSucceededRemoteData(),
|
getAllSucceededRemoteData(),
|
||||||
|
Reference in New Issue
Block a user