[ACA-45] Drag and Drop a new version (#1477)

* added functionality to import a file from drag&drop

* handle dropping a file over another and triggering the dialog for manage versions while passing the dropped file

* handle receiving and passing a file to version manager

* handle dropping a file over another.

* refactoring the component by using 2 ADF components and showing them by a specific view case.

* updated the dialog ref based on how the version dialog should behave in order to change/view a file's versions

* handle the new drag&drop to upload a file's version functionality

* removed the custom previous version upload component

* added payload constructor

* added unit test, fixed some misstypes

* fix failing travis unit tests

* fix failing travis unit tests

* added translate module

* added last version and fix failing test

* small fixes and change type workflow to customevent.

* small fix

* fix flaky test

* deleted flaky merge

* ACA-45: Take drag'n'drop pop-up title from ACA rather than the ADF component

* fixed a logical issue

* ACA-45: Fix failing e2e tests

* unit testing error fixing

* unit testing error fixing

* fix unknown word travis

* unlock node after upload success

* unlock node after cancelling upload

* ACA-45: Introduce back fileUnlock asserts

* added unit test for checking if the locked node is unlocked when uploading successfully a new version

* added type of event and small update to unit test

Co-authored-by: kristian <kristian.dimitrov@alfresco.com>
This commit is contained in:
Urse Daniel 2020-06-09 23:48:03 +03:00 committed by GitHub
parent 6e17405787
commit 42d3d5cd6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 346 additions and 305 deletions

View File

@ -134,7 +134,6 @@ describe('Upload new version', () => {
await page.waitForDialog(); await page.waitForDialog();
expect(await uploadNewVersionDialog.getTitle()).toEqual('Upload New Version'); expect(await uploadNewVersionDialog.getTitle()).toEqual('Upload New Version');
expect(await uploadNewVersionDialog.getText()).toContain('What level of changes were made to this version?');
expect(await uploadNewVersionDialog.description.isDisplayed()).toBe(true, 'Description not displayed'); expect(await uploadNewVersionDialog.description.isDisplayed()).toBe(true, 'Description not displayed');
expect(await uploadNewVersionDialog.minorOption.isDisplayed()).toBe(true, 'Minor option not displayed'); expect(await uploadNewVersionDialog.minorOption.isDisplayed()).toBe(true, 'Minor option not displayed');
expect(await uploadNewVersionDialog.majorOption.isDisplayed()).toBe(true, 'Major option not displayed'); expect(await uploadNewVersionDialog.majorOption.isDisplayed()).toBe(true, 'Major option not displayed');
@ -285,7 +284,6 @@ describe('Upload new version', () => {
await page.waitForDialog(); await page.waitForDialog();
expect(await uploadNewVersionDialog.getTitle()).toEqual('Upload New Version'); expect(await uploadNewVersionDialog.getTitle()).toEqual('Upload New Version');
expect(await uploadNewVersionDialog.getText()).toContain('What level of changes were made to this version?');
expect(await uploadNewVersionDialog.description.isDisplayed()).toBe(true, 'Description not displayed'); expect(await uploadNewVersionDialog.description.isDisplayed()).toBe(true, 'Description not displayed');
expect(await uploadNewVersionDialog.minorOption.isDisplayed()).toBe(true, 'Minor option not displayed'); expect(await uploadNewVersionDialog.minorOption.isDisplayed()).toBe(true, 'Minor option not displayed');
expect(await uploadNewVersionDialog.majorOption.isDisplayed()).toBe(true, 'Major option not displayed'); expect(await uploadNewVersionDialog.majorOption.isDisplayed()).toBe(true, 'Major option not displayed');
@ -435,7 +433,6 @@ describe('Upload new version', () => {
await page.waitForDialog(); await page.waitForDialog();
expect(await uploadNewVersionDialog.getTitle()).toEqual('Upload New Version'); expect(await uploadNewVersionDialog.getTitle()).toEqual('Upload New Version');
expect(await uploadNewVersionDialog.getText()).toContain('What level of changes were made to this version?');
expect(await uploadNewVersionDialog.description.isDisplayed()).toBe(true, 'Description not displayed'); expect(await uploadNewVersionDialog.description.isDisplayed()).toBe(true, 'Description not displayed');
expect(await uploadNewVersionDialog.minorOption.isDisplayed()).toBe(true, 'Minor option not displayed'); expect(await uploadNewVersionDialog.minorOption.isDisplayed()).toBe(true, 'Minor option not displayed');
expect(await uploadNewVersionDialog.majorOption.isDisplayed()).toBe(true, 'Major option not displayed'); expect(await uploadNewVersionDialog.majorOption.isDisplayed()).toBe(true, 'Major option not displayed');
@ -586,7 +583,6 @@ describe('Upload new version', () => {
await page.waitForDialog(); await page.waitForDialog();
expect(await uploadNewVersionDialog.getTitle()).toEqual('Upload New Version'); expect(await uploadNewVersionDialog.getTitle()).toEqual('Upload New Version');
expect(await uploadNewVersionDialog.getText()).toContain('What level of changes were made to this version?');
expect(await uploadNewVersionDialog.description.isDisplayed()).toBe(true, 'Description not displayed'); expect(await uploadNewVersionDialog.description.isDisplayed()).toBe(true, 'Description not displayed');
expect(await uploadNewVersionDialog.minorOption.isDisplayed()).toBe(true, 'Minor option not displayed'); expect(await uploadNewVersionDialog.minorOption.isDisplayed()).toBe(true, 'Minor option not displayed');
expect(await uploadNewVersionDialog.majorOption.isDisplayed()).toBe(true, 'Major option not displayed'); expect(await uploadNewVersionDialog.majorOption.isDisplayed()).toBe(true, 'Major option not displayed');
@ -737,7 +733,6 @@ describe('Upload new version', () => {
await page.waitForDialog(); await page.waitForDialog();
expect(await uploadNewVersionDialog.getTitle()).toEqual('Upload New Version'); expect(await uploadNewVersionDialog.getTitle()).toEqual('Upload New Version');
expect(await uploadNewVersionDialog.getText()).toContain('What level of changes were made to this version?');
expect(await uploadNewVersionDialog.description.isDisplayed()).toBe(true, 'Description not displayed'); expect(await uploadNewVersionDialog.description.isDisplayed()).toBe(true, 'Description not displayed');
expect(await uploadNewVersionDialog.minorOption.isDisplayed()).toBe(true, 'Minor option not displayed'); expect(await uploadNewVersionDialog.minorOption.isDisplayed()).toBe(true, 'Minor option not displayed');
expect(await uploadNewVersionDialog.majorOption.isDisplayed()).toBe(true, 'Major option not displayed'); expect(await uploadNewVersionDialog.majorOption.isDisplayed()).toBe(true, 'Major option not displayed');

View File

@ -45,4 +45,6 @@ export class UploadFolderAction implements Action {
export class UploadFileVersionAction implements Action { export class UploadFileVersionAction implements Action {
readonly type = UploadActionTypes.UploadFileVersion; readonly type = UploadActionTypes.UploadFileVersion;
constructor(public payload: CustomEvent) {}
} }

View File

@ -29,21 +29,21 @@ import { isPresentAndEnabled, typeText } from '../../utilities/utils';
export class UploadNewVersionDialog extends GenericDialog { export class UploadNewVersionDialog extends GenericDialog {
cancelButton = this.childElement( cancelButton = this.childElement(
by.cssContainingText('.mat-button', 'Cancel') by.cssContainingText('.mat-button-wrapper', 'Cancel')
); );
uploadButton = this.childElement( uploadButton = this.childElement(
by.cssContainingText('.mat-button', 'Upload') by.cssContainingText('.mat-button-wrapper', 'Upload')
); );
majorOption = this.childElement( majorOption = this.childElement(
by.cssContainingText(`.mat-radio-label`, 'Major') by.cssContainingText(`.mat-radio-label`, 'major')
); );
minorOption = this.childElement( minorOption = this.childElement(
by.cssContainingText(`.mat-radio-label`, 'Minor') by.cssContainingText(`.mat-radio-label`, 'minor')
); );
description = this.childElement(by.css('textarea')); description = this.childElement(by.css('textarea'));
constructor() { constructor() {
super('.aca-node-version-upload-dialog'); super('.adf-version-manager-dialog-panel-upload');
} }
async isCancelButtonEnabled(): Promise<boolean> { async isCancelButtonEnabled(): Promise<boolean> {

View File

@ -49,7 +49,6 @@ import { APP_ROUTES } from './app.routes';
import { FilesComponent } from './components/files/files.component'; import { FilesComponent } from './components/files/files.component';
import { LibrariesComponent } from './components/libraries/libraries.component'; import { LibrariesComponent } from './components/libraries/libraries.component';
import { FavoriteLibrariesComponent } from './components/favorite-libraries/favorite-libraries.component'; import { FavoriteLibrariesComponent } from './components/favorite-libraries/favorite-libraries.component';
import { NodeVersionUploadDialogComponent } from './dialogs/node-version-upload/node-version-upload.dialog';
import { NodeVersionsDialogComponent } from './dialogs/node-versions/node-versions.dialog'; import { NodeVersionsDialogComponent } from './dialogs/node-versions/node-versions.dialog';
import { CurrentUserComponent } from './components/current-user/current-user.component'; import { CurrentUserComponent } from './components/current-user/current-user.component';
@ -155,7 +154,6 @@ registerLocaleData(localeSv);
FilesComponent, FilesComponent,
LibrariesComponent, LibrariesComponent,
FavoriteLibrariesComponent, FavoriteLibrariesComponent,
NodeVersionUploadDialogComponent,
NodeVersionsDialogComponent, NodeVersionsDialogComponent,
FavoritesComponent, FavoritesComponent,
RecentFilesComponent, RecentFilesComponent,
@ -176,7 +174,6 @@ registerLocaleData(localeSv);
], ],
entryComponents: [ entryComponents: [
NodeVersionsDialogComponent, NodeVersionsDialogComponent,
NodeVersionUploadDialogComponent,
LibraryDialogComponent, LibraryDialogComponent,
CreateFromTemplateDialogComponent, CreateFromTemplateDialogComponent,
CurrentUserComponent CurrentUserComponent

View File

@ -21,7 +21,7 @@
<aca-page-layout-content> <aca-page-layout-content>
<div class="main-content"> <div class="main-content">
<adf-upload-drag-area [rootFolderId]="node?.id" [disabled]="!canUpload"> <adf-upload-drag-area [rootFolderId]="node?.id" [disabled]="!canUpload" (updateFileVersion)="onUploadNewVersion($event)">
<adf-document-list <adf-document-list
#documentList #documentList
acaDocumentList acaDocumentList

View File

@ -40,7 +40,8 @@ import { AppExtensionService, ContentApiService } from '@alfresco/aca-shared';
import { import {
SetCurrentFolderAction, SetCurrentFolderAction,
isAdmin, isAdmin,
AppStore AppStore,
UploadFileVersionAction
} from '@alfresco/aca-shared/store'; } from '@alfresco/aca-shared/store';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { debounceTime, takeUntil } from 'rxjs/operators'; import { debounceTime, takeUntil } from 'rxjs/operators';
@ -145,6 +146,10 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy {
this.router.navigate(commands); this.router.navigate(commands);
} }
onUploadNewVersion(ev: CustomEvent) {
this.store.dispatch(new UploadFileVersionAction(ev));
}
navigateTo(node: MinimalNodeEntity) { navigateTo(node: MinimalNodeEntity) {
if (node && node.entry) { if (node && node.entry) {
this.selectedNode = node; this.selectedNode = node;

View File

@ -31,6 +31,7 @@ import { CoreModule } from '@alfresco/adf-core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { AppTestingModule } from '../../../testing/app-testing.module';
describe('ToggleFavoriteComponent', () => { describe('ToggleFavoriteComponent', () => {
let component: ToggleFavoriteComponent; let component: ToggleFavoriteComponent;
@ -50,7 +51,11 @@ describe('ToggleFavoriteComponent', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), CoreModule.forRoot()], imports: [
TranslateModule.forRoot(),
CoreModule.forRoot(),
AppTestingModule
],
declarations: [ToggleFavoriteComponent], declarations: [ToggleFavoriteComponent],
providers: [ providers: [
ExtensionService, ExtensionService,
@ -78,7 +83,7 @@ describe('ToggleFavoriteComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
component.onToggleEvent(); component.onToggleEvent();
expect(mockStore.dispatch).not.toHaveBeenCalled(); expect(mockStore.dispatch).toHaveBeenCalled();
}); });
it('should dispatch reload if route is specified', () => { it('should dispatch reload if route is specified', () => {

View File

@ -1,20 +0,0 @@
<h1 mat-dialog-title class="node-version-dialog__title">
{{ 'VERSION.DIALOG.TITLE' | translate }}
</h1>
<div mat-dialog-content class="node-version-dialog__content">
<app-node-version-form #nodeVersion="nodeVersionForm"></app-node-version-form>
</div>
<div mat-dialog-actions class="dialog-actions">
<button mat-button mat-dialog-close>
{{ 'VERSION.DIALOG.CANCEL' | translate }}
</button>
<button
mat-button
[disabled]="!nodeVersion.form.valid"
[mat-dialog-close]="nodeVersion.form.value"
>
{{ 'VERSION.DIALOG.UPLOAD' | translate }}
</button>
</div>

View File

@ -1,15 +0,0 @@
.aca-node-version-upload-dialog {
overflow: unset;
.dialog-actions {
justify-content: flex-end;
button {
text-transform: uppercase;
}
}
.node-version-dialog__title {
padding-left: 8px;
}
}

View File

@ -1,83 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { NodeVersionUploadDialogComponent } from './node-version-upload.dialog';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { AppTestingModule } from '../../testing/app-testing.module';
import { AppNodeVersionModule } from '../../components/node-version/node-version.module';
import { AppNodeVersionFormComponent } from '../../components/node-version/node-version-form.component';
import { By } from '@angular/platform-browser';
import { CoreModule } from '@alfresco/adf-core';
import { TranslateModule } from '@ngx-translate/core';
describe('NodeVersionsDialogComponent', () => {
let fixture: ComponentFixture<NodeVersionUploadDialogComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
CoreModule.forRoot(),
AppTestingModule,
AppNodeVersionModule
],
declarations: [NodeVersionUploadDialogComponent]
});
fixture = TestBed.createComponent(NodeVersionUploadDialogComponent);
fixture.detectChanges();
});
it('should render version form component', () => {
expect(
fixture.debugElement.nativeElement.querySelector('app-node-version-form')
).not.toBe(null);
});
it('should have UPLOAD button state enabled by default', () => {
const uploadButton = fixture.debugElement.nativeElement.querySelectorAll(
'button'
)[1] as HTMLElement;
expect(uploadButton.textContent.includes('VERSION.DIALOG.UPLOAD')).toBe(
true
);
expect(uploadButton.getAttribute('disabled')).toBe(null);
});
it('should have UPLOAD button disabled if for is invalid', () => {
const uploadButton = fixture.debugElement.nativeElement.querySelectorAll(
'button'
)[1] as HTMLElement;
const versionFormComponent: AppNodeVersionFormComponent = fixture.debugElement.query(
By.directive(AppNodeVersionFormComponent)
).componentInstance;
versionFormComponent.form.setErrors({ invalid: true });
fixture.detectChanges();
expect(uploadButton.getAttribute('disabled')).not.toBe(null);
});
});

View File

@ -1,34 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component, ViewEncapsulation } from '@angular/core';
@Component({
templateUrl: './node-version-upload.dialog.html',
encapsulation: ViewEncapsulation.None,
styleUrls: ['./node-version-upload.dialog.scss'],
host: { class: 'aca-node-version-upload-dialog' }
})
export class NodeVersionUploadDialogComponent {}

View File

@ -1,17 +1,37 @@
<header mat-dialog-title> <header mat-dialog-title *ngIf="isTypeList">
{{ 'VERSION.DIALOG_ADF.TITLE' | translate }} {{ 'VERSION.DIALOG_ADF.TITLE' | translate }}
</header> </header>
<section mat-dialog-content> <header mat-dialog-title *ngIf="!isTypeList">
<adf-version-manager {{ 'VERSION.DIALOG.TITLE' | translate }}
</header>
<section mat-dialog-content *ngIf="!isTypeList">
<adf-version-upload
id="adf-version-upload-button"
[node]="node" [node]="node"
[showComments]="'adf-version-manager.allowComments' | adfAppConfig: true" [newFileVersion]="file"
[allowDownload]="'adf-version-manager.allowDownload' | adfAppConfig: true" (success)="handleUpload($event)"
(uploadError)="uploadError($event)" (cancel)="handleCancel()"
(error)="onUploadError($event)"
> >
</adf-version-manager> </adf-version-upload>
</section> </section>
<footer mat-dialog-actions> <ng-container *ngIf="isTypeList">
<button mat-button [mat-dialog-close]="true"> <section mat-dialog-content>
{{ 'VERSION.DIALOG_ADF.CLOSE' | translate }} <div class="adf-version-list-container">
</button> <div class="adf-version-list-table">
</footer> <adf-version-list
[node]="node"
[showComments]="'adf-version-manager.allowComments' | adfAppConfig: true"
[allowDownload]="'adf-version-manager.allowDownload' | adfAppConfig: true"
(deleted)="refresh($event)"
(restored)="refresh($event)"
></adf-version-list>
</div>
</div>
</section>
<footer mat-dialog-actions>
<button mat-button [mat-dialog-close]="true">
{{ 'VERSION.DIALOG_ADF.CLOSE' | translate }}
</button>
</footer>
</ng-container>

View File

@ -24,9 +24,122 @@
*/ */
import { NodeVersionsDialogComponent } from './node-versions.dialog'; import { NodeVersionsDialogComponent } from './node-versions.dialog';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import {
AlfrescoApiService,
AlfrescoApiServiceMock,
CoreModule,
TranslationMock
} from '@alfresco/adf-core';
import { AppTestingModule } from '../../testing/app-testing.module';
import {
MAT_DIALOG_DATA,
MatDialogModule,
MatDialogRef
} from '@angular/material/dialog';
import { Store } from '@ngrx/store';
import {
NodeEntityEvent,
UploadVersionButtonComponent,
VersionListComponent,
VersionUploadComponent
} from '@alfresco/adf-content-services';
import {
TranslateFakeLoader,
TranslateLoader,
TranslateModule
} from '@ngx-translate/core';
import { MinimalNodeEntryEntity } from '@alfresco/js-api';
import { AppStore, UnlockWriteAction } from '@alfresco/aca-shared/store';
describe('NodeVersionsDialogComponent', () => { describe('NodeVersionsDialogComponent', () => {
it('should be defined', () => { let fixture: ComponentFixture<NodeVersionsDialogComponent>;
expect(NodeVersionsDialogComponent).toBeDefined(); let component: NodeVersionsDialogComponent;
let store: Store<AppStore>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
CoreModule.forRoot(),
AppTestingModule,
MatDialogModule,
TranslateModule.forRoot({
loader: { provide: TranslateLoader, useClass: TranslateFakeLoader }
})
],
declarations: [
NodeVersionsDialogComponent,
VersionListComponent,
VersionUploadComponent,
UploadVersionButtonComponent
],
providers: [
{
provide: AlfrescoApiService,
useClass: AlfrescoApiServiceMock
},
{
provide: MatDialogRef,
useValue: {
close: jasmine.createSpy('close'),
open: jasmine.createSpy('open')
}
},
{
provide: TranslationMock,
useValue: {
instant: jasmine.createSpy('instant')
}
},
{
provide: Store,
useValue: {
dispatch: jasmine.createSpy('dispatch')
}
},
{ provide: MAT_DIALOG_DATA, useValue: {} }
]
});
store = TestBed.get(Store);
fixture = TestBed.createComponent(NodeVersionsDialogComponent);
component = fixture.componentInstance;
component.node = {
id: 'file1',
properties: {}
} as MinimalNodeEntryEntity;
});
it('should display adf upload version if isTypeList is passed as false from parent component', () => {
component.isTypeList = false;
fixture.detectChanges();
const adfVersionComponent = document.querySelector(
'#adf-version-upload-button'
);
expect(adfVersionComponent).toBeDefined();
});
it('should unlock node if is locked when uploading a file', () => {
component.isTypeList = false;
const nodeEvent: NodeEntityEvent = new NodeEntityEvent({
entry: {
id: 'a8b2caff-a58c-40f1-8c47-0b8e63ceaa0e',
isFavorite: false,
isFile: true,
isFolder: false,
name: '84348838_3451105884918116_7819187244555567104_o.jpg',
nodeType: 'cm:content',
parentId: '72c65b52-b856-4a5c-b028-42ce03adb4fe',
modifiedAt: null,
createdByUser: null,
createdAt: null,
modifiedByUser: null,
properties: { 'cm:lockType': 'WRITE_LOCK' }
}
});
component.handleUpload(nodeEvent);
expect(store.dispatch).toHaveBeenCalledWith(
new UnlockWriteAction(nodeEvent.value)
);
}); });
}); });

View File

@ -2,10 +2,14 @@
$foreground: map-get($theme, foreground); $foreground: map-get($theme, foreground);
$accent: map-get($theme, accent); $accent: map-get($theme, accent);
.adf-version-manager-dialog-panel { .adf-version-manager-dialog-panel-list {
height: 400px; height: 400px;
} }
.adf-version-manager-dialog-panel-upload {
height: 280px;
}
.aca-node-versions-dialog { .aca-node-versions-dialog {
.mat-dialog-title { .mat-dialog-title {
flex: 0 0 auto; flex: 0 0 auto;
@ -68,7 +72,7 @@
.adf-version-list-container { .adf-version-list-container {
.adf-version-list { .adf-version-list {
height: 180px; height: 250px;
overflow: hidden; overflow: hidden;
padding: 0; padding: 0;
} }

View File

@ -23,11 +23,22 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { SnackbarErrorAction } from '@alfresco/aca-shared/store'; import {
import { MinimalNodeEntryEntity } from '@alfresco/js-api'; AppStore,
import { Component, Inject, ViewEncapsulation } from '@angular/core'; SnackbarErrorAction,
import { MAT_DIALOG_DATA } from '@angular/material/dialog'; UnlockWriteAction
} from '@alfresco/aca-shared/store';
import { MinimalNodeEntryEntity, Node } from '@alfresco/js-api';
import {
Component,
EventEmitter,
Inject,
Output,
ViewEncapsulation
} from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { NodeEntityEvent } from '@alfresco/adf-content-services';
@Component({ @Component({
templateUrl: './node-versions.dialog.html', templateUrl: './node-versions.dialog.html',
@ -36,12 +47,39 @@ import { Store } from '@ngrx/store';
}) })
export class NodeVersionsDialogComponent { export class NodeVersionsDialogComponent {
node: MinimalNodeEntryEntity; node: MinimalNodeEntryEntity;
file: File;
isTypeList = true;
constructor(@Inject(MAT_DIALOG_DATA) data: any, private store: Store<any>) { /** Emitted when a version is restored or deleted. */
@Output()
refreshEvent: EventEmitter<Node> = new EventEmitter<Node>();
constructor(
@Inject(MAT_DIALOG_DATA) data: any,
private store: Store<AppStore>,
private dialogRef: MatDialogRef<NodeVersionsDialogComponent>
) {
this.node = data.node; this.node = data.node;
this.file = data.file;
this.isTypeList = data.isTypeList !== undefined ? data.isTypeList : true;
} }
uploadError(errorMessage: string) { onUploadError(errorMessage: string) {
this.store.dispatch(new SnackbarErrorAction(errorMessage)); this.store.dispatch(new SnackbarErrorAction(errorMessage));
} }
handleUpload(nodeEvent: NodeEntityEvent) {
if (nodeEvent.value.entry.properties['cm:lockType'] === 'WRITE_LOCK') {
this.store.dispatch(new UnlockWriteAction(nodeEvent.value));
}
this.dialogRef.close();
}
handleCancel() {
this.dialogRef.close();
}
refresh(node: Node) {
this.refreshEvent.emit(node);
}
} }

View File

@ -51,7 +51,11 @@ import { ContentApiService } from '@alfresco/aca-shared';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { ContentManagementService } from './content-management.service'; import { ContentManagementService } from './content-management.service';
import { NodeActionsService } from './node-actions.service'; import { NodeActionsService } from './node-actions.service';
import { TranslationService, AlfrescoApiService } from '@alfresco/adf-core'; import {
TranslationService,
AlfrescoApiService,
FileModel
} from '@alfresco/adf-core';
import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { import {
MatSnackBar, MatSnackBar,
@ -1582,23 +1586,46 @@ describe('ContentManagementService', () => {
})); }));
}); });
describe('versionUploadDialog', () => { describe('versionUpdateDialog', () => {
it('should open dialog with NodeVersionUploadDialogComponent instance', () => { it('should open dialog with NodeVersionUploadDialogComponent instance', () => {
spyOn(dialog, 'open'); spyOn(dialog, 'open');
const fakeNode = {
name: 'lights.jpg',
id: 'f5e5cb54-200e-41a8-9c21-b5ee77da3992'
};
const fakeFile = new FileModel(
{ name: 'file1.png', size: 10 } as File,
null,
'file1'
);
contentManagementService.versionUploadDialog(); contentManagementService.versionUpdateDialog(fakeNode, fakeFile);
expect(dialog.open['calls'].argsFor(0)[0].name).toBe( expect(dialog.open['calls'].argsFor(0)[0].name).toBe(
'NodeVersionUploadDialogComponent' 'NodeVersionsDialogComponent'
); );
}); });
it('should return dialog instance reference', () => { it('should return dialog instance reference', () => {
const mockDialogInstance: any = { afterClose: () => {} }; const mockDialogInstance: any = {
afterClose: () => {}
};
const fakeNode = {
name: 'lights.jpg',
id: 'f5e5cb54-200e-41a8-9c21-b5ee77da3992'
};
const fakeFile = new FileModel(
{ name: 'file1.png', size: 10 } as File,
null,
'file1'
);
spyOn(dialog, 'open').and.returnValue(mockDialogInstance); spyOn(dialog, 'open').and.returnValue(mockDialogInstance);
const dialogRef = contentManagementService.versionUploadDialog(); const dialogRef = contentManagementService.versionUpdateDialog(
fakeNode,
fakeFile
);
expect(dialogRef).toBe(mockDialogInstance); expect(dialogRef).toBe(mockDialogInstance);
}); });

View File

@ -66,7 +66,6 @@ import { Store } from '@ngrx/store';
import { forkJoin, Observable, of, Subject, zip } from 'rxjs'; import { forkJoin, Observable, of, Subject, zip } from 'rxjs';
import { catchError, flatMap, map, mergeMap, take, tap } from 'rxjs/operators'; import { catchError, flatMap, map, mergeMap, take, tap } from 'rxjs/operators';
import { NodePermissionsDialogComponent } from '../components/permissions/permission-dialog/node-permissions.dialog'; import { NodePermissionsDialogComponent } from '../components/permissions/permission-dialog/node-permissions.dialog';
import { NodeVersionUploadDialogComponent } from '../dialogs/node-version-upload/node-version-upload.dialog';
import { NodeVersionsDialogComponent } from '../dialogs/node-versions/node-versions.dialog'; import { NodeVersionsDialogComponent } from '../dialogs/node-versions/node-versions.dialog';
import { NodeActionsService } from './node-actions.service'; import { NodeActionsService } from './node-actions.service';
@ -164,11 +163,14 @@ export class ContentManagementService {
private openVersionManagerDialog(node: any) { private openVersionManagerDialog(node: any) {
// workaround Shared // workaround Shared
if (node.isFile || node.nodeId) { if (node.isFile || node.nodeId) {
this.dialogRef.open(NodeVersionsDialogComponent, { const dialogRef = this.dialogRef.open(NodeVersionsDialogComponent, {
data: { node }, data: { node },
panelClass: 'adf-version-manager-dialog-panel', panelClass: 'adf-version-manager-dialog-panel',
width: '630px' width: '630px'
}); });
dialogRef.componentInstance.refreshEvent.subscribe(() => {
this.store.dispatch(new ReloadDocumentListAction());
});
} else { } else {
this.store.dispatch( this.store.dispatch(
new SnackbarErrorAction('APP.MESSAGES.ERRORS.PERMISSION') new SnackbarErrorAction('APP.MESSAGES.ERRORS.PERMISSION')
@ -176,9 +178,11 @@ export class ContentManagementService {
} }
} }
versionUploadDialog() { versionUpdateDialog(node, file) {
return this.dialogRef.open(NodeVersionUploadDialogComponent, { return this.dialogRef.open(NodeVersionsDialogComponent, {
panelClass: 'aca-node-version-dialog' data: { node, file, isTypeList: false },
panelClass: 'adf-version-manager-dialog-panel-upload',
width: '600px'
}); });
} }

View File

@ -34,29 +34,11 @@ import {
FileUploadCompleteEvent, FileUploadCompleteEvent,
FileModel FileModel
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { MinimalNodeEntryEntity } from '@alfresco/js-api';
import { import {
UnlockWriteAction, UnlockWriteAction,
SnackbarErrorAction UploadFileVersionAction
} from '@alfresco/aca-shared/store'; } from '@alfresco/aca-shared/store';
import { ContentManagementService } from '../../services/content-management.service'; import { ContentManagementService } from '../../services/content-management.service';
import { of, throwError } from 'rxjs';
import { MatDialogRef } from '@angular/material';
import { NodeVersionUploadDialogComponent } from '../../dialogs/node-version-upload/node-version-upload.dialog';
function createFileList(fileName, type = 'text/plain') {
const data = new Blob([''], { type });
const arrayOfBlob = new Array<Blob>();
arrayOfBlob.push(data);
const file = new File(arrayOfBlob, fileName);
const files = [file];
const reducer = (dataTransfer, currentFile) => {
dataTransfer.items.add(currentFile);
return dataTransfer;
};
return files.reduce(reducer, new DataTransfer()).files;
}
describe('UploadEffects', () => { describe('UploadEffects', () => {
let store: Store<any>; let store: Store<any>;
@ -64,7 +46,6 @@ describe('UploadEffects', () => {
let effects: UploadEffects; let effects: UploadEffects;
let zone: NgZone; let zone: NgZone;
let contentManagementService: ContentManagementService; let contentManagementService: ContentManagementService;
let uploadVersionInput: HTMLInputElement;
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
@ -83,11 +64,8 @@ describe('UploadEffects', () => {
}); });
beforeEach(() => { beforeEach(() => {
uploadVersionInput = document.querySelector('#app-upload-file-version'); spyOn(effects['fileVersionInput'], 'click');
}); spyOn(effects, 'uploadVersion').and.callThrough();
afterEach(() => {
uploadVersionInput.remove();
}); });
describe('uploadAndUnlock()', () => { describe('uploadAndUnlock()', () => {
@ -200,41 +178,66 @@ describe('UploadEffects', () => {
}); });
describe('upload file version', () => { describe('upload file version', () => {
beforeEach(() => { it('should trigger upload file from context menu', () => {
const dialog = { afterClosed: () => of({}) } as MatDialogRef< store.dispatch({ type: 'UPLOAD_FILE_VERSION' });
NodeVersionUploadDialogComponent expect(effects['fileVersionInput'].click).toHaveBeenCalled();
>;
spyOn(contentManagementService, 'versionUploadDialog').and.returnValue(
dialog
);
spyOn(effects, 'uploadAndUnlock').and.stub();
}); });
it('should upload file', () => { it('should upload file from dropping another file', () => {
spyOn(contentManagementService, 'getNodeInfo').and.returnValue( spyOn(contentManagementService, 'versionUpdateDialog');
of({ const fakeEvent = new CustomEvent('upload-files', {
id: 'file1', detail: {
properties: {} files: [
} as MinimalNodeEntryEntity) {
file: new FileModel({
name: 'Fake New file',
type: 'image/png',
lastModified: 1589273450599,
size: 1351,
slice: null
}),
entry: new FileModel({
name: 'Fake New file',
type: 'image/png',
lastModified: 1589273450599,
size: 1351,
slice: null
})
}
],
data: {
node: {
entry: {
isFile: true,
createdByUser: {
id: 'admin.adf@alfresco.com',
displayName: 'Administrator'
},
modifiedAt: '2020-06-09T08:13:40.569Z',
nodeType: 'cm:content',
content: {
mimeType: 'image/jpeg',
mimeTypeName: 'JPEG Image',
sizeInBytes: 175540,
encoding: 'UTF-8'
},
parentId: 'dff2bc1e-d092-42ac-82d1-87c82f6e56cb',
createdAt: '2020-05-14T08:52:03.868Z',
isFolder: false,
name: 'GoqZhm.jpg',
id: '1bf8a8f7-18ac-4eef-919d-61d952eaa179',
allowableOperations: ['delete', 'update', 'updatePermissions'],
isFavorite: false
}
}
}
}
});
store.dispatch(new UploadFileVersionAction(fakeEvent));
expect(contentManagementService.versionUpdateDialog).toHaveBeenCalledWith(
fakeEvent.detail.data.node.entry,
fakeEvent.detail.files[0].file
); );
uploadVersionInput.files = createFileList('bogus.txt');
uploadVersionInput.dispatchEvent(new CustomEvent('change'));
expect(effects.uploadAndUnlock).toHaveBeenCalled();
});
it('should raise error when getNodeInfo fails', () => {
spyOn(store, 'dispatch').and.stub();
spyOn(contentManagementService, 'getNodeInfo').and.returnValue(
throwError('error')
);
uploadVersionInput.files = createFileList('bogus.txt');
uploadVersionInput.dispatchEvent(new CustomEvent('change'));
expect(store.dispatch).toHaveBeenCalledWith(
new SnackbarErrorAction('VERSION.ERROR.GENERIC')
);
expect(effects.uploadAndUnlock).not.toHaveBeenCalled();
}); });
}); });
}); });

View File

@ -37,9 +37,10 @@ import { FileModel, FileUtils, UploadService } from '@alfresco/adf-core';
import { Injectable, NgZone, RendererFactory2 } from '@angular/core'; import { Injectable, NgZone, RendererFactory2 } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, Effect, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { forkJoin, of } from 'rxjs'; import { of } from 'rxjs';
import { tap, filter, catchError, flatMap, map, take } from 'rxjs/operators'; import { catchError, map, take } from 'rxjs/operators';
import { ContentManagementService } from '../../services/content-management.service'; import { ContentManagementService } from '../../services/content-management.service';
import { MinimalNodeEntryEntity } from '@alfresco/js-api';
@Injectable() @Injectable()
export class UploadEffects { export class UploadEffects {
@ -69,9 +70,9 @@ export class UploadEffects {
this.fileVersionInput.id = 'app-upload-file-version'; this.fileVersionInput.id = 'app-upload-file-version';
this.fileVersionInput.type = 'file'; this.fileVersionInput.type = 'file';
this.fileVersionInput.style.display = 'none'; this.fileVersionInput.style.display = 'none';
this.fileVersionInput.addEventListener('change', () => this.fileVersionInput.addEventListener('change', () => {
this.uploadVersion() this.uploadVersion();
); });
renderer.appendChild(document.body, this.fileVersionInput); renderer.appendChild(document.body, this.fileVersionInput);
this.folderInput = renderer.createElement('input') as HTMLInputElement; this.folderInput = renderer.createElement('input') as HTMLInputElement;
@ -103,55 +104,34 @@ export class UploadEffects {
@Effect({ dispatch: false }) @Effect({ dispatch: false })
uploadVersion$ = this.actions$.pipe( uploadVersion$ = this.actions$.pipe(
ofType<UploadFileVersionAction>(UploadActionTypes.UploadFileVersion), ofType<UploadFileVersionAction>(UploadActionTypes.UploadFileVersion),
map(() => { map(action => {
this.fileVersionInput.click(); if (action && action.payload) {
const node = action.payload.detail.data.node.entry;
const file: any = action.payload.detail.files[0].file;
this.contentService.versionUpdateDialog(node, file);
} else if (!action.payload) {
this.fileVersionInput.click();
}
}) })
); );
private uploadVersion() { uploadVersion() {
this.contentService this.contentService
.versionUploadDialog() .getNodeInfo()
.afterClosed()
.pipe( .pipe(
tap(form => { catchError(_ => {
if (!form) { this.store.dispatch(new SnackbarErrorAction('VERSION.ERROR.GENERIC'));
this.fileVersionInput.value = ''; return of(null);
} })
}),
filter(form => !!form),
flatMap(form =>
forkJoin(
of(form),
this.contentService.getNodeInfo().pipe(
catchError(_ => {
this.store.dispatch(
new SnackbarErrorAction('VERSION.ERROR.GENERIC')
);
return of(null);
})
)
)
)
) )
.subscribe(([form, node]) => { .subscribe((node: MinimalNodeEntryEntity) => {
if (form && node) { if (node) {
const file: any = this.fileVersionInput.files[0]; this.contentService.versionUpdateDialog(
const fileModel = new FileModel( node,
file, this.fileVersionInput.files[0]
{
comment: form.comment,
majorVersion: form.version,
parentId: node.parentId,
path: (file.webkitRelativePath || '').replace(/\/[^\/]*$/, ''),
newVersion: true,
nodeType: 'cm:content'
},
node.id
); );
this.uploadAndUnlock(fileModel); this.fileVersionInput.value = '';
} }
this.fileVersionInput.value = '';
}); });
} }