mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
71712: confirmation modal for export + tests &
- request causing error because of issue #756, commented out for now & - drop event prevention in a HostListener like dragover event
This commit is contained in:
@@ -357,7 +357,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
|
|
||||||
observableCombineLatest(
|
observableCombineLatest(
|
||||||
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
||||||
this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME)
|
// this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_EXPORT_SCRIPT_NAME)
|
||||||
).pipe(
|
).pipe(
|
||||||
// TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed; otherwise even in production mode, the metadata export button is only available after a refresh (and not in dev mode)
|
// TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed; otherwise even in production mode, the metadata export button is only available after a refresh (and not in dev mode)
|
||||||
// filter(([authorized, metadataExportScriptExists]: boolean[]) => authorized && metadataExportScriptExists),
|
// filter(([authorized, metadataExportScriptExists]: boolean[]) => authorized && metadataExportScriptExists),
|
||||||
@@ -416,7 +416,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit {
|
|||||||
|
|
||||||
observableCombineLatest(
|
observableCombineLatest(
|
||||||
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
this.authorizationService.isAuthorized(FeatureID.AdministratorOf),
|
||||||
this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME)
|
// this.scriptDataService.scriptWithNameExistsAndCanExecute(METADATA_IMPORT_SCRIPT_NAME)
|
||||||
).pipe(
|
).pipe(
|
||||||
// TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed
|
// TODO uncomment when #635 (https://github.com/DSpace/dspace-angular/issues/635) is fixed
|
||||||
// filter(([authorized, metadataImportScriptExists]: boolean[]) => authorized && metadataImportScriptExists),
|
// filter(([authorized, metadataImportScriptExists]: boolean[]) => authorized && metadataImportScriptExists),
|
||||||
|
@@ -0,0 +1,16 @@
|
|||||||
|
<div>
|
||||||
|
<div class="modal-header">{{ headerLabel | translate:{dsoName: dso?.name} }}
|
||||||
|
<button type="button" class="close" (click)="close()" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>{{ infoLabel | translate:{dsoName: dso?.name} }}</p>
|
||||||
|
<button type="button" class="cancel btn btn-secondary" (click)="cancelPressed()" aria-label="Cancel">
|
||||||
|
{{ cancelLabel | translate:{dsoName: dso?.name} }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="confirm btn btn-primary" (click)="confirmPressed()" aria-label="Confirm" ngbAutofocus>
|
||||||
|
{{ confirmLabel | translate:{dsoName: dso?.name} }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
@@ -0,0 +1,117 @@
|
|||||||
|
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ConfirmationModalComponent } from './confirmation-modal.component';
|
||||||
|
|
||||||
|
describe('ConfirmationModalComponent', () => {
|
||||||
|
let component: ConfirmationModalComponent;
|
||||||
|
let fixture: ComponentFixture<ConfirmationModalComponent>;
|
||||||
|
let debugElement: DebugElement;
|
||||||
|
|
||||||
|
const modalStub = jasmine.createSpyObj('modalStub', ['close']);
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot()],
|
||||||
|
declarations: [ConfirmationModalComponent],
|
||||||
|
providers: [
|
||||||
|
{ provide: NgbActiveModal, useValue: modalStub }
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ConfirmationModalComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
debugElement = fixture.debugElement;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('close', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
component.close();
|
||||||
|
});
|
||||||
|
it('should call the close method on the active modal', () => {
|
||||||
|
expect(modalStub.close).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('confirmPressed', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(component.response, 'emit');
|
||||||
|
component.confirmPressed();
|
||||||
|
});
|
||||||
|
it('should call the close method on the active modal', () => {
|
||||||
|
expect(modalStub.close).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it('event emitter should emit true', () => {
|
||||||
|
expect(component.response.emit).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cancelPressed', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(component.response, 'emit');
|
||||||
|
component.cancelPressed();
|
||||||
|
});
|
||||||
|
it('should call the close method on the active modal', () => {
|
||||||
|
expect(modalStub.close).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it('event emitter should emit false', () => {
|
||||||
|
expect(component.response.emit).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the click method emits on close button', () => {
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
spyOn(component, 'close');
|
||||||
|
debugElement.query(By.css('button.close')).triggerEventHandler('click', {});
|
||||||
|
tick();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
it('should call the close method on the component', () => {
|
||||||
|
expect(component.close).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the click method emits on cancel button', () => {
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
spyOn(component, 'close');
|
||||||
|
spyOn(component.response, 'emit');
|
||||||
|
debugElement.query(By.css('button.cancel')).triggerEventHandler('click', {});
|
||||||
|
tick();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
it('should call the close method on the component', () => {
|
||||||
|
expect(component.close).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it('event emitter should emit false', () => {
|
||||||
|
expect(component.response.emit).toHaveBeenCalledWith(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when the click method emits on confirm button', () => {
|
||||||
|
beforeEach(fakeAsync(() => {
|
||||||
|
spyOn(component, 'close');
|
||||||
|
spyOn(component.response, 'emit');
|
||||||
|
debugElement.query(By.css('button.confirm')).triggerEventHandler('click', {});
|
||||||
|
tick();
|
||||||
|
fixture.detectChanges();
|
||||||
|
}));
|
||||||
|
it('should call the close method on the component', () => {
|
||||||
|
expect(component.close).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
it('event emitter should emit true', () => {
|
||||||
|
expect(component.response.emit).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@@ -0,0 +1,48 @@
|
|||||||
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-confirmation-modal',
|
||||||
|
templateUrl: 'confirmation-modal.component.html',
|
||||||
|
})
|
||||||
|
export class ConfirmationModalComponent {
|
||||||
|
@Input() headerLabel: string;
|
||||||
|
@Input() infoLabel: string;
|
||||||
|
@Input() cancelLabel: string;
|
||||||
|
@Input() confirmLabel: string;
|
||||||
|
@Input() dso: DSpaceObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event fired when the cancel or confirm button is clicked, with respectively false or true
|
||||||
|
*/
|
||||||
|
@Output()
|
||||||
|
response = new EventEmitter<boolean>();
|
||||||
|
|
||||||
|
constructor(protected activeModal: NgbActiveModal) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Confirm the action that led to the modal
|
||||||
|
*/
|
||||||
|
confirmPressed() {
|
||||||
|
this.response.emit(true);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the action that led to the modal and close modal
|
||||||
|
*/
|
||||||
|
cancelPressed() {
|
||||||
|
this.response.emit(false);
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the modal
|
||||||
|
*/
|
||||||
|
close() {
|
||||||
|
this.activeModal.close();
|
||||||
|
}
|
||||||
|
}
|
@@ -4,16 +4,19 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { Observable } from 'rxjs/internal/Observable';
|
import { Observable } from 'rxjs/internal/Observable';
|
||||||
import { take, map } from 'rxjs/operators';
|
import { take, map } from 'rxjs/operators';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { AuthService } from '../../../../core/auth/auth.service';
|
||||||
import { METADATA_EXPORT_SCRIPT_NAME, ScriptDataService } from '../../../../core/data/processes/script-data.service';
|
import { METADATA_EXPORT_SCRIPT_NAME, ScriptDataService } from '../../../../core/data/processes/script-data.service';
|
||||||
import { RequestEntry } from '../../../../core/data/request.reducer';
|
import { RequestEntry } from '../../../../core/data/request.reducer';
|
||||||
import { Collection } from '../../../../core/shared/collection.model';
|
import { Collection } from '../../../../core/shared/collection.model';
|
||||||
import { Community } from '../../../../core/shared/community.model';
|
import { Community } from '../../../../core/shared/community.model';
|
||||||
import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model';
|
import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model';
|
||||||
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { ProcessParameter } from '../../../../process-page/processes/process-parameter.model';
|
import { ProcessParameter } from '../../../../process-page/processes/process-parameter.model';
|
||||||
|
import { ConfirmationModalComponent } from '../../../confirmation-modal/confirmation-modal.component';
|
||||||
import { isNotEmpty } from '../../../empty.util';
|
import { isNotEmpty } from '../../../empty.util';
|
||||||
import { NotificationsService } from '../../../notifications/notifications.service';
|
import { NotificationsService } from '../../../notifications/notifications.service';
|
||||||
|
import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils';
|
||||||
import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component';
|
import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,7 +24,7 @@ import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-sel
|
|||||||
* Used to choose a dso from to export metadata of
|
* Used to choose a dso from to export metadata of
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-edit-item-selector',
|
selector: 'ds-export-metadata-selector',
|
||||||
templateUrl: '../dso-selector-modal-wrapper.component.html',
|
templateUrl: '../dso-selector-modal-wrapper.component.html',
|
||||||
})
|
})
|
||||||
export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit {
|
export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit {
|
||||||
@@ -31,7 +34,8 @@ export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComp
|
|||||||
|
|
||||||
constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute, private router: Router,
|
constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute, private router: Router,
|
||||||
protected notificationsService: NotificationsService, protected translationService: TranslateService,
|
protected notificationsService: NotificationsService, protected translationService: TranslateService,
|
||||||
protected scriptDataService: ScriptDataService) {
|
protected scriptDataService: ScriptDataService,
|
||||||
|
private modalService: NgbModal) {
|
||||||
super(activeModal, route);
|
super(activeModal, route);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,9 +45,23 @@ export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComp
|
|||||||
*/
|
*/
|
||||||
navigate(dso: DSpaceObject): Observable<boolean> {
|
navigate(dso: DSpaceObject): Observable<boolean> {
|
||||||
if (dso instanceof Collection || dso instanceof Community) {
|
if (dso instanceof Collection || dso instanceof Community) {
|
||||||
const startScriptSucceeded = this.startScriptNotifyAndRedirect(dso, dso.handle);
|
const modalRef = this.modalService.open(ConfirmationModalComponent);
|
||||||
startScriptSucceeded.pipe(take(1)).subscribe();
|
modalRef.componentInstance.dso = dso;
|
||||||
return startScriptSucceeded;
|
modalRef.componentInstance.headerLabel = "confirmation-modal.export-metadata.header";
|
||||||
|
modalRef.componentInstance.infoLabel = "confirmation-modal.export-metadata.info";
|
||||||
|
modalRef.componentInstance.cancelLabel = "confirmation-modal.export-metadata.cancel";
|
||||||
|
modalRef.componentInstance.confirmLabel = "confirmation-modal.export-metadata.confirm";
|
||||||
|
|
||||||
|
modalRef.componentInstance.response.subscribe((confirm: boolean) => {
|
||||||
|
if (confirm) {
|
||||||
|
const startScriptSucceeded = this.startScriptNotifyAndRedirect(dso, dso.handle);
|
||||||
|
startScriptSucceeded.pipe(take(1)).subscribe();
|
||||||
|
return startScriptSucceeded;
|
||||||
|
} else {
|
||||||
|
const modalRef = this.modalService.open(ExportMetadataSelectorComponent);
|
||||||
|
modalRef.componentInstance.dsoRD = createSuccessfulRemoteDataObject(dso);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.notificationsService.error(this.translationService.get('dso-selector.export-metadata.notValidDSO'));
|
this.notificationsService.error(this.translationService.get('dso-selector.export-metadata.notValidDSO'));
|
||||||
return observableOf(false);
|
return observableOf(false);
|
||||||
|
@@ -51,15 +51,17 @@ export class FileDropzoneNoUploaderComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.uploaderId = 'ds-drag-and-drop-uploader' + uniqueId();
|
this.uploaderId = 'ds-drag-and-drop-uploader' + uniqueId();
|
||||||
this.isOverDocumentDropZone = observableOf(false);
|
this.isOverDocumentDropZone = observableOf(false);
|
||||||
window.addEventListener('drop', (e: DragEvent) => {
|
|
||||||
return e && e.preventDefault();
|
|
||||||
}, false);
|
|
||||||
this.uploader = new FileUploader({
|
this.uploader = new FileUploader({
|
||||||
// required, but using onFileDrop, not uploader
|
// required, but using onFileDrop, not uploader
|
||||||
url: 'placeholder',
|
url: 'placeholder',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@HostListener('window:drop', ['$event'])
|
||||||
|
onDrop(event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
@HostListener('window:dragover', ['$event'])
|
@HostListener('window:dragover', ['$event'])
|
||||||
onDragOver(event: any) {
|
onDragOver(event: any) {
|
||||||
// Show drop area on the page
|
// Show drop area on the page
|
||||||
|
@@ -10,6 +10,7 @@ import { MissingTranslationHandler, TranslateModule } from '@ngx-translate/core'
|
|||||||
|
|
||||||
import { NgxPaginationModule } from 'ngx-pagination';
|
import { NgxPaginationModule } from 'ngx-pagination';
|
||||||
import { ComcolRoleComponent } from './comcol-forms/edit-comcol-page/comcol-role/comcol-role.component';
|
import { ComcolRoleComponent } from './comcol-forms/edit-comcol-page/comcol-role/comcol-role.component';
|
||||||
|
import { ConfirmationModalComponent } from './confirmation-modal/confirmation-modal.component';
|
||||||
import { ExportMetadataSelectorComponent } from './dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component';
|
import { ExportMetadataSelectorComponent } from './dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component';
|
||||||
import { FileDropzoneNoUploaderComponent } from './file-dropzone-no-uploader/file-dropzone-no-uploader.component';
|
import { FileDropzoneNoUploaderComponent } from './file-dropzone-no-uploader/file-dropzone-no-uploader.component';
|
||||||
import { PublicationListElementComponent } from './object-list/item-list-element/item-types/publication/publication-list-element.component';
|
import { PublicationListElementComponent } from './object-list/item-list-element/item-types/publication/publication-list-element.component';
|
||||||
@@ -401,7 +402,8 @@ const COMPONENTS = [
|
|||||||
GroupSearchBoxComponent,
|
GroupSearchBoxComponent,
|
||||||
FileDownloadLinkComponent,
|
FileDownloadLinkComponent,
|
||||||
CollectionDropdownComponent,
|
CollectionDropdownComponent,
|
||||||
ExportMetadataSelectorComponent
|
ExportMetadataSelectorComponent,
|
||||||
|
ConfirmationModalComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
const ENTRY_COMPONENTS = [
|
const ENTRY_COMPONENTS = [
|
||||||
@@ -478,7 +480,8 @@ const ENTRY_COMPONENTS = [
|
|||||||
ClaimedTaskActionsEditMetadataComponent,
|
ClaimedTaskActionsEditMetadataComponent,
|
||||||
FileDownloadLinkComponent,
|
FileDownloadLinkComponent,
|
||||||
CurationFormComponent,
|
CurationFormComponent,
|
||||||
ExportMetadataSelectorComponent
|
ExportMetadataSelectorComponent,
|
||||||
|
ConfirmationModalComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
const SHARED_ITEM_PAGE_COMPONENTS = [
|
const SHARED_ITEM_PAGE_COMPONENTS = [
|
||||||
|
@@ -995,6 +995,16 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"confirmation-modal.export-metadata.header": "Export metadata for {{ dsoName }}",
|
||||||
|
|
||||||
|
"confirmation-modal.export-metadata.info": "Are you sure you want to export metadata for {{ dsoName }}",
|
||||||
|
|
||||||
|
"confirmation-modal.export-metadata.cancel": "Cancel",
|
||||||
|
|
||||||
|
"confirmation-modal.export-metadata.confirm": "Export",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"error.bitstream": "Error fetching bitstream",
|
"error.bitstream": "Error fetching bitstream",
|
||||||
|
|
||||||
"error.browse-by": "Error fetching items",
|
"error.browse-by": "Error fetching items",
|
||||||
|
@@ -105,7 +105,7 @@ export const environment: Partial<GlobalConfig> = {
|
|||||||
},
|
},
|
||||||
// Angular Universal settings
|
// Angular Universal settings
|
||||||
universal: {
|
universal: {
|
||||||
preboot: true,
|
preboot: false,
|
||||||
async: true,
|
async: true,
|
||||||
time: false
|
time: false
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user