diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts index 628fc3f89c..a8f0581ec0 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.spec.ts @@ -11,7 +11,7 @@ import { URLCombiner } from '../../../../../core/url-combiner/url-combiner'; import { WorkspaceItemAdminWorkflowActionsComponent } from './workspace-item-admin-workflow-actions.component'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; import { - getWorkflowItemDeleteRoute, + getWorkspaceItemDeleteRoute, } from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { Item } from '../../../../../core/shared/item.model'; import { RemoteData } from '../../../../../core/data/remote-data'; @@ -83,7 +83,7 @@ describe('WorkspaceItemAdminWorkflowActionsComponent', () => { it('should render a delete button with the correct link', () => { const button = fixture.debugElement.query(By.css('a.delete-link')); const link = button.nativeElement.href; - expect(link).toContain(new URLCombiner(getWorkflowItemDeleteRoute(wsi.id)).toString()); + expect(link).toContain(new URLCombiner(getWorkspaceItemDeleteRoute(wsi.id)).toString()); }); it('should render a policies button with the correct link', () => { diff --git a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts index adbd421628..36678460da 100644 --- a/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts +++ b/src/app/admin/admin-workflow-page/admin-workflow-search-results/actions/workspace-item/workspace-item-admin-workflow-actions.component.ts @@ -11,7 +11,7 @@ import { SupervisionOrderGroupSelectorComponent } from './supervision-order-group-selector/supervision-order-group-selector.component'; import { - getWorkflowItemDeleteRoute + getWorkspaceItemDeleteRoute } from '../../../../../workflowitems-edit-page/workflowitems-edit-page-routing-paths'; import { ITEM_EDIT_AUTHORIZATIONS_PATH } from '../../../../../item-page/edit-item-page/edit-item-page.routing-paths'; import { WorkspaceItem } from '../../../../../core/submission/models/workspaceitem.model'; @@ -105,10 +105,10 @@ export class WorkspaceItemAdminWorkflowActionsComponent implements OnInit { } /** - * Returns the path to the delete page of this workflow item + * Returns the path to the delete page of this workspace item */ getDeleteRoute(): string { - return getWorkflowItemDeleteRoute(this.wsi.id); + return getWorkspaceItemDeleteRoute(this.wsi.id); } /** diff --git a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts index ece61f0321..326eebe4a7 100644 --- a/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts +++ b/src/app/workflowitems-edit-page/workflowitems-edit-page-routing-paths.ts @@ -1,5 +1,5 @@ import { URLCombiner } from '../core/url-combiner/url-combiner'; -import { getWorkflowItemModuleRoute } from '../app-routing-paths'; +import { getWorkflowItemModuleRoute, getWorkspaceItemModuleRoute } from '../app-routing-paths'; export function getWorkflowItemPageRoute(wfiId: string) { return new URLCombiner(getWorkflowItemModuleRoute(), wfiId).toString(); @@ -24,8 +24,13 @@ export function getAdvancedWorkflowRoute(wfiId: string) { return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, ADVANCED_WORKFLOW_PATH).toString(); } +export function getWorkspaceItemDeleteRoute(wsiId: string) { + return new URLCombiner(getWorkspaceItemModuleRoute(), wsiId, WORKSPACE_ITEM_DELETE_PATH).toString(); +} + export const WORKFLOW_ITEM_EDIT_PATH = 'edit'; export const WORKFLOW_ITEM_DELETE_PATH = 'delete'; export const WORKFLOW_ITEM_VIEW_PATH = 'view'; export const WORKFLOW_ITEM_SEND_BACK_PATH = 'sendback'; export const ADVANCED_WORKFLOW_PATH = 'advanced'; +export const WORKSPACE_ITEM_DELETE_PATH = 'delete'; diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/themed-workspaceitems-delete-page.component.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/themed-workspaceitems-delete-page.component.ts new file mode 100644 index 0000000000..681cba21c8 --- /dev/null +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/themed-workspaceitems-delete-page.component.ts @@ -0,0 +1,26 @@ +import { ThemedComponent } from '../../shared/theme-support/themed.component'; +import { Component } from '@angular/core'; +import { WorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page.component'; + +/** + * Themed wrapper for WorkspaceItemsDeletePageComponent + */ + +@Component({ + selector: 'ds-themed-workspace-items-delete', + styleUrls: [], + templateUrl: './../../shared/theme-support/themed.component.html' +}) +export class ThemedWorkspaceItemsDeletePageComponent extends ThemedComponent { + protected getComponentName(): string { + return 'WorkspaceItemsDeletePageComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../themes/${themeName}/app/workflowitems-edit-page/workflow-item-delete/workflow-item-delete.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./workspaceitems-delete-page.component`); + } +} diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.html b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.html new file mode 100644 index 0000000000..a0f0a1711e --- /dev/null +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.html @@ -0,0 +1,24 @@ +
+

{{ 'workspace-item.delete.header' | translate }}

+ + + +
+ + + + + + diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.scss b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.scss new file mode 100644 index 0000000000..e52175abea --- /dev/null +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.scss @@ -0,0 +1,4 @@ + +:host ::ng-deep ds-modify-item-overview table { + display: inline-table !important; +} diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts new file mode 100644 index 0000000000..f52dd497d8 --- /dev/null +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.spec.ts @@ -0,0 +1,110 @@ +import { RouteService } from '../..//core/services/route.service'; +import { NotificationsService } from '../..//shared/notifications/notifications.service'; +import { WorkspaceitemDataService } from '../..//core/submission/workspaceitem-data.service'; +import { RouterMock } from './../../shared/mocks/router.mock'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page.component'; +import { ActivatedRoute, Router } from '@angular/router'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; +import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; +import { Location } from '@angular/common'; +import { of as observableOf } from 'rxjs'; +import { routeServiceStub } from '../../shared/testing/route-service.stub'; +import { LocationStub } from '../../shared/testing/location.stub'; +import { By } from '@angular/platform-browser'; +import { ActivatedRouteStub } from 'src/app/shared/testing/active-router.stub'; +import { createSuccessfulRemoteDataObject } from 'src/app/shared/remote-data.utils'; +import { WorkspaceItem } from 'src/app/core/submission/models/workspaceitem.model'; +import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; + +describe('WorkspaceitemsDeletePageComponent', () => { + let component: WorkspaceItemsDeletePageComponent; + let fixture: ComponentFixture; + + const workspaceitemDataServiceSpy = jasmine.createSpyObj('WorkspaceitemDataService', { + delete: jasmine.createSpy('delete') + }); + + const wsi = new WorkspaceItem(); + wsi.id = '1234'; + const dso = new DSpaceObject(); + dso.uuid = '1234'; + + const translateServiceStub = { + get: () => observableOf('test-message'), + onLangChange: new EventEmitter(), + onTranslationChange: new EventEmitter(), + onDefaultLangChange: new EventEmitter() + }; + + const modalService = { + open: () => {/** empty */}, + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [WorkspaceItemsDeletePageComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: new ActivatedRouteStub( + {}, + { + wsi: createSuccessfulRemoteDataObject(wsi), + dso: createSuccessfulRemoteDataObject(dso), + } + ), + }, + { provide: Router, useValue: new RouterMock() }, + { + provide: WorkspaceitemDataService, + useValue: workspaceitemDataServiceSpy, + }, + { provide: Location, useValue: new LocationStub() }, + { provide: NgbModal, useValue: modalService }, + { + provide: NotificationsService, + useValue: new NotificationsServiceStub(), + }, + { provide: TranslateService, useValue: translateServiceStub }, + { provide: RouteService, useValue: routeServiceStub }, + ], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(WorkspaceItemsDeletePageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should have the current WorkspaceItem', () => { + console.log( (component as any).activatedRoute, 'data wsi'); + (component as any).activatedRoute.data.subscribe((data) => { + console.log(data, 'dataaa'); + expect(data.wsi.payload.id).toEqual('1234'); + }); + }); + + it('should delete the target workspace item', () => { + spyOn((component as any).modalService, 'open').and.returnValue({}); + component.confirmDelete(By.css('#delete-modal')); + fixture.detectChanges(); + expect((component as any).modalService.open).toHaveBeenCalled(); + }); + + it('should call workspaceItemService.delete', () => { + spyOn(workspaceitemDataServiceSpy, 'delete').and.returnValue(observableOf(createSuccessfulRemoteDataObject({}))); + component.sendDeleteRequest(); + expect((component as any).workspaceItemService.delete).toHaveBeenCalledWith('1234'); + }); +}); diff --git a/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts new file mode 100644 index 0000000000..cff5fd6994 --- /dev/null +++ b/src/app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.ts @@ -0,0 +1,111 @@ +import { NotificationsService } from 'src/app/shared/notifications/notifications.service'; +import { NoContent } from './../../core/shared/NoContent.model'; +import { RouteService } from 'src/app/core/services/route.service'; +import { getFirstCompletedRemoteData, getRemoteDataPayload } from './../../core/shared/operators'; +import { RemoteData } from 'src/app/core/data/remote-data'; +import { Component, OnInit } from '@angular/core'; +import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model'; +import { Observable, map, switchMap, take } from 'rxjs'; +import { ActivatedRoute, Data, Params, Router } from '@angular/router'; +import { Location } from '@angular/common'; +import { WorkspaceitemDataService } from 'src/app/core/submission/workspaceitem-data.service'; +import { TranslateService } from '@ngx-translate/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; + +@Component({ + selector: 'ds-workspaceitems-delete-page', + templateUrl: './workspaceitems-delete-page.component.html', + styleUrls: ['./workspaceitems-delete-page.component.scss'] +}) +export class WorkspaceItemsDeletePageComponent implements OnInit { + + /** + * The workspaceitem to delete + */ + public wsi$: Observable; + + /** + * The dspace object + */ + public dso$: Observable; + + /** + * The previous query parameters + */ + private previousQueryParameters?: Params; + + constructor( + private activatedRoute: ActivatedRoute, + private router: Router, + private routeService: RouteService, + private workspaceItemService: WorkspaceitemDataService, + private notificationsService: NotificationsService, + private translationService: TranslateService, + private location: Location, + private modalService: NgbModal, + ) { } + + ngOnInit(): void { + this.wsi$ = this.activatedRoute.data.pipe(map((data: Data) => data.wsi as RemoteData), getRemoteDataPayload()); + this.dso$ = this.activatedRoute.data.pipe(map((data: Data) => data.dso as RemoteData), getRemoteDataPayload()); + this.previousQueryParameters = (this.location.getState() as { [key: string]: any }).previousQueryParams; + } + + /** + * Navigates to the previous url + * If there's not previous url, it continues to the mydspace page instead + */ + previousPage() { + this.routeService.getPreviousUrl().pipe(take(1)) + .subscribe((url: string) => { + let params: Params = {}; + if (!url) { + url = '/mydspace'; + params = this.previousQueryParameters; + } + if (url.split('?').length > 1) { + for (const param of url.split('?')[1].split('&')) { + params[param.split('=')[0]] = decodeURIComponent(param.split('=')[1]); + } + } + void this.router.navigate([url.split('?')[0]], { queryParams: params }); + } + ); + } + + /** + * Open the modal to confirm the deletion of the workspaceitem + */ + public async confirmDelete(content) { + await this.modalService.open(content).result.then( + (result) => { + if (result === 'ok') { + this.sendDeleteRequest(); + } + } + ); + } + + /** + * Delete the target workspaceitem object + */ + sendDeleteRequest() { + this.wsi$.pipe( + switchMap((wsi: WorkspaceItem) => this.workspaceItemService.delete(wsi.id).pipe( + getFirstCompletedRemoteData(), + )) + ).subscribe((response: RemoteData) => { + if (response.hasSucceeded) { + const title = this.translationService.get('workspace-item.delete.notification.success.title'); + const content = this.translationService.get('workspace-item.delete.title'); + this.notificationsService.success(title, content); + this.previousPage(); + } else { + const title = this.translationService.get('workspace-item.delete.notification.error.title'); + const content = this.translationService.get('workspace-item.delete.notification.error.content'); + this.notificationsService.error(title, content); + } + }); + } +} diff --git a/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routing.module.ts b/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routing.module.ts index cc76634c03..61393d9f64 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routing.module.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-edit-page-routing.module.ts @@ -7,6 +7,8 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso import { ThemedFullItemPageComponent } from '../item-page/full/themed-full-item-page.component'; import { ItemFromWorkspaceResolver } from './item-from-workspace.resolver'; import { WorkspaceItemPageResolver } from './workspace-item-page.resolver'; +import { WorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/workspaceitems-delete-page.component'; +import { ThemedWorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/themed-workspaceitems-delete-page.component'; @NgModule({ imports: [ @@ -34,7 +36,27 @@ import { WorkspaceItemPageResolver } from './workspace-item-page.resolver'; breadcrumb: I18nBreadcrumbResolver }, data: { title: 'workspace-item.view.title', breadcrumbKey: 'workspace-item.view' } - } + }, + { + canActivate: [AuthenticatedGuard], + path: 'delete', + component: WorkspaceItemsDeletePageComponent, + resolve: { + dso: ItemFromWorkspaceResolver, + breadcrumb: I18nBreadcrumbResolver + }, + data: { title: 'workspace-item.delete', breadcrumbKey: 'workspace-item.delete' } + }, + { + canActivate: [AuthenticatedGuard], + path: 'delete', + component: ThemedWorkspaceItemsDeletePageComponent, + resolve: { + dso: ItemFromWorkspaceResolver, + breadcrumb: I18nBreadcrumbResolver + }, + data: { title: 'workspace-item.delete', breadcrumbKey: 'workspace-item.delete' } + }, ] } ]) diff --git a/src/app/workspaceitems-edit-page/workspaceitems-edit-page.module.ts b/src/app/workspaceitems-edit-page/workspaceitems-edit-page.module.ts index 65a40f3f7c..77cb7a6e66 100644 --- a/src/app/workspaceitems-edit-page/workspaceitems-edit-page.module.ts +++ b/src/app/workspaceitems-edit-page/workspaceitems-edit-page.module.ts @@ -3,6 +3,8 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { WorkspaceitemsEditPageRoutingModule } from './workspaceitems-edit-page-routing.module'; import { SubmissionModule } from '../submission/submission.module'; +import { WorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/workspaceitems-delete-page.component'; +import { ThemedWorkspaceItemsDeletePageComponent } from './workspaceitems-delete-page/themed-workspaceitems-delete-page.component'; @NgModule({ imports: [ @@ -11,7 +13,10 @@ import { SubmissionModule } from '../submission/submission.module'; SharedModule, SubmissionModule, ], - declarations: [] + declarations: [ + WorkspaceItemsDeletePageComponent, + ThemedWorkspaceItemsDeletePageComponent, + ] }) /** * This module handles all modules that need to access the workspaceitems edit page. diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 66824e56b3..f493abbe87 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4959,10 +4959,24 @@ "workspace-item.view.title": "Workspace View", + "workspace-item.delete.breadcrumbs": "Workspace Delete", + + "workspace-item.delete.header": "Delete workspace item", + + "workspace-item.delete.button.confirm": "Delete", + + "workspace-item.delete.button.cancel": "Cancel", + + "workspace-item.delete.notification.success.title": "Deleted", + + "workspace-item.delete.title": "This workspace item was successfully deleted", + + "workspace-item.delete.notification.error.title": "Something went wrong", + + "workspace-item.delete.notification.error.content": "The workspace item could not be deleted", "workflow-item.advanced.title": "Advanced workflow", - "workflow-item.selectrevieweraction.notification.success.title": "Selected reviewer", "workflow-item.selectrevieweraction.notification.success.content": "The reviewer for this workflow item has been successfully selected", diff --git a/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.html b/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.scss b/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.ts b/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.ts new file mode 100644 index 0000000000..95494d76dd --- /dev/null +++ b/src/themes/custom/app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; +import { WorkspaceItemsDeletePageComponent as BaseComponent } from '../../../../../app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component'; + + +@Component({ + selector: 'ds-workspaceitems-delete-page', + templateUrl: '../../../../../app/workspaceitems-edit-page/workspaceitems-delete-page/workspaceitems-delete-page.component.html', +}) +export class WorkspaceItemsDeletePageComponent extends BaseComponent { +} diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts index b1290cc634..4297d8a024 100644 --- a/src/themes/custom/lazy-theme.module.ts +++ b/src/themes/custom/lazy-theme.module.ts @@ -145,6 +145,7 @@ import { MediaViewerVideoComponent } from './app/item-page/media-viewer/media-viewer-video/media-viewer-video.component'; import { NgxGalleryModule } from '@kolkov/ngx-gallery'; +import { WorkspaceItemsDeletePageComponent } from './app/workspace-items-delete-page/workspace-items-delete/workspace-items-delete.component'; const DECLARATIONS = [ FileSectionComponent, @@ -212,6 +213,7 @@ const DECLARATIONS = [ MediaViewerComponent, MediaViewerImageComponent, MediaViewerVideoComponent, + WorkspaceItemsDeletePageComponent, ]; @NgModule({