Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UFAL/share submission by email #720

Open
wants to merge 8 commits into
base: dtq-dev
Choose a base branch
from
5 changes: 5 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ import { STATIC_PAGE_PATH } from './static-page/static-page-routing-paths';
path: STATIC_PAGE_PATH,
loadChildren: () => import('./static-page/static-page.module').then((m) => m.StaticPageModule),
},
{
path: 'share-submission',
loadChildren: () => import('./share-submission/share-submission.module').then((m) => m.ShareSubmissionModule),
canActivate: [AuthenticatedGuard, EndUserAgreementCurrentUserGuard]
},
{ path: '**', pathMatch: 'full', component: ThemedPageNotFoundComponent }
]
}
Expand Down
16 changes: 16 additions & 0 deletions src/app/change-submitter-page/change-submitter-page.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div class="container pt-2 pb-4" *ngVar="submitter | async as sub">
<h3>{{'share.submission.page.title' | translate}}</h3>
<span>{{'change.submitter.page.message' | translate}}</span>
<span>{{getSubmitterName(sub)}}</span>
<span> ({{sub?.email}})</span>
<div class="pt-4">
<button class="btn btn-info" (click)="changeSubmitter()">
<span *ngIf="changeSubmitterSpinner"
class="spinner-border spinner-border-sm spinner-button mr-1" role="status" aria-hidden="true">
</span>
<span>{{'change.submitter.page.button.take-it' | translate}}</span>
</button>
</div>
</div>


Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/**
The file for styling the ChangeSubmitterPageComponent.
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChangeSubmitterPageComponent } from './change-submitter-page.component';
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
import { ActivatedRoute, Router } from '@angular/router';
import { HALEndpointService } from '../core/shared/hal-endpoint.service';
import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service';
import { RequestService } from '../core/data/request.service';
import { NotificationsService } from '../shared/notifications/notifications.service';
import { TranslateModule } from '@ngx-translate/core';
import { NotificationsServiceStub } from '../shared/testing/notifications-service.stub';
import { RouterStub } from '../shared/testing/router.stub';
import { of as observableOf } from 'rxjs';
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
import { getMockRequestService } from '../shared/mocks/request.service.mock';
import { createPaginatedList } from '../shared/testing/utils.test';
import { HALEndpointServiceStub } from '../shared/testing/hal-endpoint-service.stub';
import { getMockRemoteDataBuildService } from '../shared/mocks/remote-data-build.service.mock';
import { DSONameService } from '../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../shared/mocks/dso-name.service.mock';

describe('ChangeSubmitterPageComponent', () => {
let component: ChangeSubmitterPageComponent;
let fixture: ComponentFixture<ChangeSubmitterPageComponent>;

let activatedRoute;
let requestService: RequestService;
let mockDataService: WorkspaceitemDataService;
let halService: HALEndpointService;
let rdbService: RemoteDataBuildService;

beforeEach(async () => {
activatedRoute = {
snapshot: {
queryParams: new Map([
['shareToken', 'fake-share-token'],
])
}
};
requestService = getMockRequestService();
mockDataService = jasmine.createSpyObj('WorkspaceitemDataService', {
searchBy: observableOf(createSuccessfulRemoteDataObject$(createPaginatedList([]))),
});
halService = Object.assign(new HALEndpointServiceStub('some-url'));
rdbService = getMockRemoteDataBuildService();

await TestBed.configureTestingModule({
declarations: [ ChangeSubmitterPageComponent ],
imports: [
TranslateModule.forRoot()
],
providers: [
{ provide: ActivatedRoute, useValue: activatedRoute },
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
{ provide: Router, useValue: new RouterStub() },
{ provide: RequestService, useValue: requestService },
{ provide: WorkspaceitemDataService, useValue: mockDataService },
{ provide: HALEndpointService, useValue: halService },
{ provide: RemoteDataBuildService, useValue: rdbService },
{ provide: DSONameService, useValue: DSONameServiceMock },
]
})
.compileComponents();

fixture = TestBed.createComponent(ChangeSubmitterPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
153 changes: 153 additions & 0 deletions src/app/change-submitter-page/change-submitter-page.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { Component, OnInit } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
import { RequestParam } from '../core/cache/models/request-param.model';
import {
getFirstCompletedRemoteData,
getFirstSucceededRemoteDataPayload,
getFirstSucceededRemoteListPayload
} from '../core/shared/operators';
import { map } from 'rxjs/operators';
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
import { ActivatedRoute } from '@angular/router';
import { followLink } from '../shared/utils/follow-link-config.model';
import { EPerson } from '../core/eperson/models/eperson.model';
import { DSONameService } from '../core/breadcrumbs/dso-name.service';
import { isNullOrUndef } from 'chart.js/helpers';
import { HALEndpointService } from '../core/shared/hal-endpoint.service';
import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service';
import { RequestService } from '../core/data/request.service';
import { PostRequest } from '../core/data/request.models';
import { RemoteData } from '../core/data/remote-data';
import { NotificationsService } from '../shared/notifications/notifications.service';
import { TranslateService } from '@ngx-translate/core';

@Component({
selector: 'ds-change-submitter-page',
templateUrl: './change-submitter-page.component.html',
styleUrls: ['./change-submitter-page.component.scss']
})
export class ChangeSubmitterPageComponent implements OnInit {

/**
* Share token from the url. This token is used to retrieve the WorkspaceItem.
*/
private shareToken = '';

/**
* BehaviorSubject that contains the submitter of the WorkspaceItem.
*/
submitter: BehaviorSubject<EPerson> = new BehaviorSubject(null);

/**
* BehaviorSubject that contains the WorkspaceItem.
*/
workspaceItem: BehaviorSubject<WorkspaceItem> = new BehaviorSubject(null);

/**
* Boolean that indicates if the spinner should be shown when the submitter is being changed.
*/
changeSubmitterSpinner = false;

constructor(private workspaceItemService: WorkspaceitemDataService,
private route: ActivatedRoute,
public dsoNameService: DSONameService,
protected halService: HALEndpointService,
protected rdbService: RemoteDataBuildService,
protected requestService: RequestService,
protected notificationsService: NotificationsService,
protected translate: TranslateService) {}

ngOnInit(): void {
// Load `share_token` param value from the url
this.shareToken = this.route.snapshot.queryParams.share_token;
this.loadWorkspaceItemAndAssignSubmitter(this.shareToken);
}

/**
* Load the WorkspaceItem using the shareToken and assign the submitter from the retrieved WorkspaceItem.
*/
loadWorkspaceItemAndAssignSubmitter(shareToken: string) {
this.findWorkspaceItemByShareToken(shareToken)?.subscribe((workspaceItem: WorkspaceItem) => {
this.workspaceItem.next(workspaceItem);
this.loadAndAssignSubmitter(workspaceItem);
});
}

/**
* Find a WorkspaceItem by its shareToken.
*/
findWorkspaceItemByShareToken(shareToken: string): Observable<WorkspaceItem> {
return this.workspaceItemService.searchBy('shareToken', {
searchParams: [Object.assign(new RequestParam('shareToken', shareToken))]
}, false, false, followLink('submitter')).pipe(getFirstSucceededRemoteListPayload(),
map((workspaceItems: WorkspaceItem[]) => workspaceItems?.[0]));
}

/**
* Load the submitter from the WorkspaceItem and assign it to the submitter BehaviorSubject.
*/
loadAndAssignSubmitter(workspaceItem: WorkspaceItem) {
if (isNullOrUndef(workspaceItem)) {
console.error('Cannot load submitter because WorkspaceItem is null or undefined');
return;
}

if (workspaceItem.submitter instanceof Observable<EPerson>) {
console.log('Loading submitter from observable', workspaceItem);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

workspaceItem.submitter
.pipe(getFirstSucceededRemoteDataPayload())
.subscribe((submitter: any) => {
this.assignSubmitter(submitter);
});
} else {
this.assignSubmitter(workspaceItem.submitter);
}
}

/**
* Assign a new submitter to the submitter BehaviorSubject.
*/
assignSubmitter(eperson: EPerson) {
this.submitter.next(eperson);
}

/**
* Get the name of the submitter using the DSONameService.
* @param submitter
*/
getSubmitterName(submitter: EPerson): string {
if (isNullOrUndef(submitter)) {
return '';
}
return this.dsoNameService.getName(submitter);
}

/**
* Change the submitter of the WorkspaceItem using the shareToken. This will send a POST request to the backend when
* the submitter of the Item is changed.
*/
changeSubmitter() {
const requestId = this.requestService.generateRequestId();

const url = this.halService.getRootHref() + '/submission/setOwner?shareToken=' + this.shareToken;
const postRequest = new PostRequest(requestId, url);
// Send GET request
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

this.requestService.send(postRequest);
this.changeSubmitterSpinner = true;
// Get response
const response = this.rdbService.buildFromRequestUUID(requestId);
response.pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData<WorkspaceItem>) => {
if (rd.hasSucceeded) {
this.notificationsService.success(
this.translate.instant('change.submitter.page.changed-successfully'));
// Update the submitter
this.loadWorkspaceItemAndAssignSubmitter(this.shareToken);
} else {
this.notificationsService.error(
this.translate.instant('change.submitter.page.changed-error'));
}
this.changeSubmitterSpinner = false;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div class="container pt-2 pb-4">
<h3>{{'share.submission.page.title' | translate}}</h3>
<span>{{'share.submission.page.share-link.message.start' | translate}}</span>
<a [href]="changeSubmitterLink">{{changeSubmitterLink}}</a>
<span>{{'share.submission.page.share-link.message.end' | translate}}</span>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/**
The file for styling the ShareSubmissionPageComponent.
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { ShareSubmissionPageComponent } from './share-submission-page.component';
import { ActivatedRoute } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';

describe('ShareSubmissionPageComponent', () => {
let component: ShareSubmissionPageComponent;
let fixture: ComponentFixture<ShareSubmissionPageComponent>;
let activatedRoute;

beforeEach(async () => {
activatedRoute = {
snapshot: {
queryParams: new Map([
['shareToken', 'fake-share-token'],
])
}
};

await TestBed.configureTestingModule({
declarations: [ ShareSubmissionPageComponent ],
imports: [
TranslateModule.forRoot()
],
providers: [
{ provide: ActivatedRoute, useValue: activatedRoute }
]
})
.compileComponents();

fixture = TestBed.createComponent(ShareSubmissionPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
selector: 'ds-share-submission-page',
templateUrl: './share-submission-page.component.html',
styleUrls: ['./share-submission-page.component.scss']
})
export class ShareSubmissionPageComponent {

/**
* Share token from the url. This token is used to retrieve the WorkspaceItem.
* With this link, the submitter can be changed.
*/
changeSubmitterLink: string;

constructor(private route: ActivatedRoute) {}

ngOnInit(): void {
// Load `share-token` param value from the url
this.changeSubmitterLink = this.route.snapshot.queryParams.changeSubmitterLink;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
import { ShareSubmissionPageComponent } from './share-submission-page.component';
import { I18nBreadcrumbResolver } from '../../core/breadcrumbs/i18n-breadcrumb.resolver';
import { ChangeSubmitterPageComponent } from '../../change-submitter-page/change-submitter-page.component';

const routes: Routes = [
{ path: '',
component: ShareSubmissionPageComponent,
resolve: { breadcrumb: I18nBreadcrumbResolver },
data: {
breadcrumbKey: 'share.submission',
},
},
{ path: 'change-submitter',
component: ChangeSubmitterPageComponent,
resolve: { breadcrumb: I18nBreadcrumbResolver },
data: {
breadcrumbKey: 'change.submitter',
},
},
];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ShareSubmissionPageModule { }
19 changes: 19 additions & 0 deletions src/app/share-submission/share-submission.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ShareSubmissionPageComponent } from './share-submission-page/share-submission-page.component';
import { SharedModule } from '../shared/shared.module';
import { ShareSubmissionPageModule } from './share-submission-page/share-submission-routing.module';
import { ChangeSubmitterPageComponent } from '../change-submitter-page/change-submitter-page.component';

@NgModule({
declarations: [
ShareSubmissionPageComponent,
ChangeSubmitterPageComponent
],
imports: [
CommonModule,
ShareSubmissionPageModule,
SharedModule,
]
})
export class ShareSubmissionModule { }
Loading
Loading