diff --git a/src/app/+item-page/full/full-item-page.component.html b/src/app/+item-page/full/full-item-page.component.html index 349dd1c928..0eeb12467a 100644 --- a/src/app/+item-page/full/full-item-page.component.html +++ b/src/app/+item-page/full/full-item-page.component.html @@ -10,7 +10,7 @@ - diff --git a/src/app/+item-page/full/full-item-page.component.spec.ts b/src/app/+item-page/full/full-item-page.component.spec.ts index b4b9e77ed5..b4ab926667 100644 --- a/src/app/+item-page/full/full-item-page.component.spec.ts +++ b/src/app/+item-page/full/full-item-page.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed, waitForAsync } from '@angular/core/testing'; import { ItemDataService } from '../../core/data/item-data.service'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; @@ -29,9 +29,7 @@ const mockItem: Item = Object.assign(new Item(), { ] } }); -const routeStub = Object.assign(new ActivatedRouteStub(), { - data: observableOf({ dso: createSuccessfulRemoteDataObject(mockItem) }) -}); + const metadataServiceStub = { /* tslint:disable:no-empty */ processRemoteData: () => { @@ -44,6 +42,10 @@ describe('FullItemPageComponent', () => { let fixture: ComponentFixture; let authService: AuthService; + let routeStub: ActivatedRouteStub; + let routeData; + + beforeEach(waitForAsync(() => { authService = jasmine.createSpyObj('authService', { @@ -51,6 +53,14 @@ describe('FullItemPageComponent', () => { setRedirectUrl: {} }); + routeData = { + dso: createSuccessfulRemoteDataObject(mockItem), + }; + + routeStub = Object.assign(new ActivatedRouteStub(), { + data: observableOf(routeData) + }); + TestBed.configureTestingModule({ imports: [TranslateModule.forRoot({ loader: { @@ -84,4 +94,21 @@ describe('FullItemPageComponent', () => { expect(table.nativeElement.innerHTML).toContain(metadatum.value); } }); + + it('should show simple view button when not originated from workflow item', () => { + expect(comp.fromWfi).toBe(false); + const simpleViewBtn = fixture.debugElement.query(By.css('.simple-view-link')); + expect(simpleViewBtn).toBeTruthy(); + }); + + it('should not show simple view button when originated from workflow', fakeAsync(() => { + routeData.wfi = createSuccessfulRemoteDataObject$({ id: 'wfiId'}); + comp.ngOnInit(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(comp.fromWfi).toBe(true); + const simpleViewBtn = fixture.debugElement.query(By.css('.simple-view-link')); + expect(simpleViewBtn).toBeFalsy(); + }); + })); }); diff --git a/src/app/+item-page/full/full-item-page.component.ts b/src/app/+item-page/full/full-item-page.component.ts index aea350e58e..cb6a1ccf60 100644 --- a/src/app/+item-page/full/full-item-page.component.ts +++ b/src/app/+item-page/full/full-item-page.component.ts @@ -1,6 +1,6 @@ import {filter, map} from 'rxjs/operators'; -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; +import { ActivatedRoute, Data, Router } from '@angular/router'; import { Observable , BehaviorSubject } from 'rxjs'; @@ -16,6 +16,8 @@ import { MetadataService } from '../../core/metadata/metadata.service'; import { fadeInOut } from '../../shared/animations/fade'; import { hasValue } from '../../shared/empty.util'; import { AuthService } from '../../core/auth/auth.service'; +import { Location } from '@angular/common'; + /** * This component renders a full item page. @@ -29,13 +31,21 @@ import { AuthService } from '../../core/auth/auth.service'; changeDetection: ChangeDetectionStrategy.OnPush, animations: [fadeInOut] }) -export class FullItemPageComponent extends ItemPageComponent implements OnInit { +export class FullItemPageComponent extends ItemPageComponent implements OnInit, OnDestroy { itemRD$: BehaviorSubject>; metadata$: Observable; - constructor(route: ActivatedRoute, router: Router, items: ItemDataService, metadataService: MetadataService, authService: AuthService) { + /** + * True when the itemRD has been originated from its workflowitem, false otherwise. + */ + fromWfi = false; + + subs = []; + + constructor(protected route: ActivatedRoute, router: Router, items: ItemDataService, metadataService: MetadataService, authService: AuthService, + private _location: Location) { super(route, router, items, metadataService, authService); } @@ -46,5 +56,21 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit { map((rd: RemoteData) => rd.payload), filter((item: Item) => hasValue(item)), map((item: Item) => item.metadata),); + + this.subs.push(this.route.data.subscribe((data: Data) => { + this.fromWfi = hasValue(data.wfi); + }) + ); + } + + /** + * Navigate back in browser history. + */ + back() { + this._location.back(); + } + + ngOnDestroy() { + this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()); } } diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts index 67e278c2fb..c647647339 100644 --- a/src/app/+item-page/simple/item-page.component.ts +++ b/src/app/+item-page/simple/item-page.component.ts @@ -51,7 +51,7 @@ export class ItemPageComponent implements OnInit { itemPageRoute$: Observable; constructor( - private route: ActivatedRoute, + protected route: ActivatedRoute, private router: Router, private items: ItemDataService, private metadataService: MetadataService, diff --git a/src/app/+workflowitems-edit-page/item-from-workflow.resolver.spec.ts b/src/app/+workflowitems-edit-page/item-from-workflow.resolver.spec.ts new file mode 100644 index 0000000000..1ef87ad10f --- /dev/null +++ b/src/app/+workflowitems-edit-page/item-from-workflow.resolver.spec.ts @@ -0,0 +1,36 @@ +import { first } from 'rxjs/operators'; +import { WorkflowItemDataService } from '../core/submission/workflowitem-data.service'; +import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils'; +import { ItemFromWorkflowResolver } from './item-from-workflow.resolver'; + +describe('ItemFromWorkflowResolver', () => { + describe('resolve', () => { + let resolver: ItemFromWorkflowResolver; + let wfiService: WorkflowItemDataService; + const uuid = '1234-65487-12354-1235'; + const itemUuid = '8888-8888-8888-8888'; + const wfi = { + id: uuid, + item: createSuccessfulRemoteDataObject$({ id: itemUuid }) + }; + + + beforeEach(() => { + wfiService = { + findById: (id: string) => createSuccessfulRemoteDataObject$(wfi) + } as any; + resolver = new ItemFromWorkflowResolver(wfiService, null); + }); + + it('should resolve a an item from from the workflow item with the correct id', (done) => { + resolver.resolve({ params: { id: uuid } } as any, undefined) + .pipe(first()) + .subscribe( + (resolved) => { + expect(resolved.payload.id).toEqual(itemUuid); + done(); + } + ); + }); + }); +}); diff --git a/src/app/+workflowitems-edit-page/item-from-workflow.resolver.ts b/src/app/+workflowitems-edit-page/item-from-workflow.resolver.ts new file mode 100644 index 0000000000..2aaa762b2a --- /dev/null +++ b/src/app/+workflowitems-edit-page/item-from-workflow.resolver.ts @@ -0,0 +1,43 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs'; +import { RemoteData } from '../core/data/remote-data'; +import { Item } from '../core/shared/item.model'; +import { followLink } from '../shared/utils/follow-link-config.model'; +import { getFirstCompletedRemoteData } from '../core/shared/operators'; +import { Store } from '@ngrx/store'; +import { WorkflowItemDataService } from '../core/submission/workflowitem-data.service'; +import { WorkflowItem } from '../core/submission/models/workflowitem.model'; +import { switchMap } from 'rxjs/operators'; + +/** + * This class represents a resolver that requests a specific item before the route is activated + */ +@Injectable() +export class ItemFromWorkflowResolver implements Resolve> { + constructor( + private workflowItemService: WorkflowItemDataService, + protected store: Store + ) { + } + + /** + * Method for resolving an item based on the parameters in the current route + * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot + * @param {RouterStateSnapshot} state The current RouterStateSnapshot + * @returns Observable<> Emits the found item based on the parameters in the current route, + * or an error if something went wrong + */ + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable> { + const itemRD$ = this.workflowItemService.findById(route.params.id, + true, + false, + followLink('item'), + ).pipe( + getFirstCompletedRemoteData(), + switchMap((wfiRD: RemoteData) => wfiRD.payload.item as Observable>), + getFirstCompletedRemoteData() + ); + return itemRD$; + } +} diff --git a/src/app/+workflowitems-edit-page/workflow-item-view/themed-workflow-item-view.component.ts b/src/app/+workflowitems-edit-page/workflow-item-view/themed-workflow-item-view.component.ts deleted file mode 100644 index f0630fa561..0000000000 --- a/src/app/+workflowitems-edit-page/workflow-item-view/themed-workflow-item-view.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ThemedComponent } from '../../shared/theme-support/themed.component'; -import { Component } from '@angular/core'; -import { WorkflowItemViewComponent } from './workflow-item-view.component'; - -/** - * Themed wrapper for WorkflowItemViewComponent - */ - -@Component({ - selector: 'ds-themed-workflow-item-view', - styleUrls: [], - templateUrl: './../../shared/theme-support/themed.component.html' -}) -export class ThemedWorkflowItemViewComponent extends ThemedComponent { - protected getComponentName(): string { - return 'WorkflowItemViewComponent'; - } - - protected importThemedComponent(themeName: string): Promise { - return import(`../../../themes/${themeName}/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component`); - } - - protected importUnthemedComponent(): Promise { - return import(`./workflow-item-view.component`); - } -} diff --git a/src/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.html b/src/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.html deleted file mode 100644 index 00401f34ce..0000000000 --- a/src/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.html +++ /dev/null @@ -1,9 +0,0 @@ -
-
-
- -
-
- - -
diff --git a/src/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.spec.ts b/src/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.spec.ts deleted file mode 100644 index 6804c7a816..0000000000 --- a/src/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.spec.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { RouteService } from '../../core/services/route.service'; -import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { WorkflowItem } from '../../core/submission/models/workflowitem.model'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { VarDirective } from '../../shared/utils/var.directive'; -import { of as observableOf } from 'rxjs'; -import { RequestService } from '../../core/data/request.service'; -import { - createFailedRemoteDataObject$, - createPendingRemoteDataObject$, - createSuccessfulRemoteDataObject, - createSuccessfulRemoteDataObject$ -} from '../../shared/remote-data.utils'; -import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock'; -import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; -import { RouterStub } from '../../shared/testing/router.stub'; -import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; -import { getMockRequestService } from '../../shared/mocks/request.service.mock'; -import { Item } from '../../core/shared/item.model'; -import { createPaginatedList } from '../../shared/testing/utils.test'; -import { createRelationshipsObservable } from '../../+item-page/simple/item-types/shared/item.component.spec'; -import { WorkflowItemViewComponent } from './workflow-item-view.component'; -import { By } from '@angular/platform-browser'; - -describe('WorkflowItemViewComponent', () => { - - const mockItem: Item = Object.assign(new Item(), { - bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])), - metadata: [], - relationships: createRelationshipsObservable() - }); - - const mockWfi = new WorkflowItem(); - mockWfi.item = createSuccessfulRemoteDataObject$(mockItem); - - const mockRoute = Object.assign(new ActivatedRouteStub(), { - data: observableOf({ wfi: createSuccessfulRemoteDataObject(mockWfi) }) - }); - - let comp: WorkflowItemViewComponent; - let fixture: ComponentFixture; - let wfi; - let itemRD$; - let id; - - function init() { - itemRD$ = createSuccessfulRemoteDataObject$(itemRD$); - wfi = new WorkflowItem(); - wfi.item = itemRD$; - id = 'de11b5e5-064a-4e98-a7ac-a1a6a65ddf80'; - } - - beforeEach(waitForAsync(() => { - init(); - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock - } - })], - declarations: [WorkflowItemViewComponent, VarDirective], - providers: [ - { provide: ActivatedRoute, useValue: mockRoute }, - { provide: Router, useClass: RouterStub }, - { provide: RouteService, useValue: {} }, - { provide: NotificationsService, useClass: NotificationsServiceStub }, - { provide: RequestService, useValue: getMockRequestService() }, - ], - schemas: [NO_ERRORS_SCHEMA] - }) - .compileComponents(); - })); - - beforeEach(waitForAsync(() => { - fixture = TestBed.createComponent(WorkflowItemViewComponent); - comp = fixture.componentInstance; - fixture.detectChanges(); - })); - - it('should create', () => { - expect(comp).toBeTruthy(); - }); - - describe('when the item is loading', () => { - beforeEach(() => { - comp.itemRD$ = createPendingRemoteDataObject$(); - // comp.itemRD$ = observableOf(new RemoteData(true, true, true, null, undefined)); - fixture.detectChanges(); - }); - - it('should display a loading component', () => { - const loading = fixture.debugElement.query(By.css('ds-loading')); - expect(loading.nativeElement).toBeDefined(); - }); - }); - - describe('when the item failed loading', () => { - beforeEach(() => { - comp.itemRD$ = createFailedRemoteDataObject$('server error', 500); - fixture.detectChanges(); - }); - - it('should display an error component', () => { - const error = fixture.debugElement.query(By.css('ds-error')); - expect(error.nativeElement).toBeDefined(); - }); - }); - -}); diff --git a/src/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.ts b/src/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.ts deleted file mode 100644 index e9a51074fc..0000000000 --- a/src/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; -import { ActivatedRoute, Data } from '@angular/router'; -import { map, switchMap } from 'rxjs/operators';import { RemoteData } from '../../core/data/remote-data'; - -import { - getAllSucceededRemoteData, - getRemoteDataPayload -} from '../../core/shared/operators'; -import { WorkflowItem } from '../../core/submission/models/workflowitem.model'; -import { Item } from '../../core/shared/item.model'; -import { ViewMode } from '../../core/shared/view-mode.model'; - -@Component({ - selector: 'ds-workflow-item-view', - templateUrl: './workflow-item-view.component.html' -}) -/** - * Component representing a page to delete a workflow item - */ -export class WorkflowItemViewComponent implements OnInit { - - public wfi$: Observable; - public itemRD$: Observable>; - - /** - * The view-mode we're currently on - */ - viewMode = ViewMode.StandalonePage; - - - constructor(protected route: ActivatedRoute) { - } - - ngOnInit() { - this.wfi$ = this.route.data.pipe(map((data: Data) => data.wfi as RemoteData), getRemoteDataPayload()); - this.itemRD$ = this.wfi$.pipe(switchMap((wfi: WorkflowItem) => (wfi.item as Observable>).pipe(getAllSucceededRemoteData()))); - } - -} diff --git a/src/app/+workflowitems-edit-page/workflowitems-edit-page-routing.module.ts b/src/app/+workflowitems-edit-page/workflowitems-edit-page-routing.module.ts index eee235e12f..7daee0a472 100644 --- a/src/app/+workflowitems-edit-page/workflowitems-edit-page-routing.module.ts +++ b/src/app/+workflowitems-edit-page/workflowitems-edit-page-routing.module.ts @@ -13,7 +13,8 @@ import { ThemedSubmissionEditComponent } from '../submission/edit/themed-submiss import { ThemedWorkflowItemDeleteComponent } from './workflow-item-delete/themed-workflow-item-delete.component'; import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/themed-workflow-item-send-back.component'; import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver'; -import { ThemedWorkflowItemViewComponent } from './workflow-item-view/themed-workflow-item-view.component'; +import { ItemFromWorkflowResolver } from './item-from-workflow.resolver'; +import { ThemedFullItemPageComponent } from '../+item-page/full/themed-full-item-page.component'; @NgModule({ imports: [ @@ -34,8 +35,9 @@ import { ThemedWorkflowItemViewComponent } from './workflow-item-view/themed-wor { canActivate: [AuthenticatedGuard], path: WORKFLOW_ITEM_VIEW_PATH, - component: ThemedWorkflowItemViewComponent, + component: ThemedFullItemPageComponent, resolve: { + dso: ItemFromWorkflowResolver, breadcrumb: I18nBreadcrumbResolver }, data: { title: 'workflow-item.view.title', breadcrumbKey: 'workflow-item.view' } @@ -62,7 +64,7 @@ import { ThemedWorkflowItemViewComponent } from './workflow-item-view/themed-wor }] ) ], - providers: [WorkflowItemPageResolver] + providers: [WorkflowItemPageResolver, ItemFromWorkflowResolver] }) /** * This module defines the default component to load when navigating to the workflowitems edit page path. diff --git a/src/app/+workflowitems-edit-page/workflowitems-edit-page.module.ts b/src/app/+workflowitems-edit-page/workflowitems-edit-page.module.ts index 221c0328a0..463e76e54d 100644 --- a/src/app/+workflowitems-edit-page/workflowitems-edit-page.module.ts +++ b/src/app/+workflowitems-edit-page/workflowitems-edit-page.module.ts @@ -7,8 +7,6 @@ import { WorkflowItemDeleteComponent } from './workflow-item-delete/workflow-ite import { WorkflowItemSendBackComponent } from './workflow-item-send-back/workflow-item-send-back.component'; import { ThemedWorkflowItemDeleteComponent } from './workflow-item-delete/themed-workflow-item-delete.component'; import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/themed-workflow-item-send-back.component'; -import { WorkflowItemViewComponent } from './workflow-item-view/workflow-item-view.component'; -import { ThemedWorkflowItemViewComponent } from './workflow-item-view/themed-workflow-item-view.component'; import { StatisticsModule } from '../statistics/statistics.module'; import { ItemPageModule } from '../+item-page/item-page.module'; @@ -25,9 +23,7 @@ import { ItemPageModule } from '../+item-page/item-page.module'; WorkflowItemDeleteComponent, ThemedWorkflowItemDeleteComponent, WorkflowItemSendBackComponent, - ThemedWorkflowItemSendBackComponent, - WorkflowItemViewComponent, - ThemedWorkflowItemViewComponent + ThemedWorkflowItemSendBackComponent ] }) /** diff --git a/src/themes/custom/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.html b/src/themes/custom/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.html deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/themes/custom/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.scss b/src/themes/custom/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/themes/custom/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.ts b/src/themes/custom/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.ts deleted file mode 100644 index 07305e81e3..0000000000 --- a/src/themes/custom/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Component } from '@angular/core'; -import { WorkflowItemViewComponent as BaseComponent } from '../../../../../app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component'; - -@Component({ - selector: 'ds-workflow-item-view', - // styleUrls: ['workflow-item-view.component.scss'], - // templateUrl: './workflow-item-view.component.html' - templateUrl: '../../../../../app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.html' -}) -/** - * Component representing a page to view a workflow item - */ -export class WorkflowItemViewComponent extends BaseComponent { -} diff --git a/src/themes/custom/theme.module.ts b/src/themes/custom/theme.module.ts index 0e8e3a437d..49b54cd850 100644 --- a/src/themes/custom/theme.module.ts +++ b/src/themes/custom/theme.module.ts @@ -79,7 +79,6 @@ import { HeaderComponent } from './app/header/header.component'; import { FooterComponent } from './app/footer/footer.component'; import { BreadcrumbsComponent } from './app/breadcrumbs/breadcrumbs.component'; import { HeaderNavbarWrapperComponent } from './app/header-nav-wrapper/header-navbar-wrapper.component'; -import { WorkflowItemViewComponent } from './app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component'; const DECLARATIONS = [ HomePageComponent, @@ -116,7 +115,6 @@ const DECLARATIONS = [ SubmissionSubmitComponent, WorkflowItemDeleteComponent, WorkflowItemSendBackComponent, - WorkflowItemViewComponent, FooterComponent, HeaderComponent, NavbarComponent,