diff --git a/e2e/content-services/upload/remove-upload.e2e.ts b/e2e/content-services/upload/remove-upload.e2e.ts
new file mode 100644
index 0000000000..60100b4fb4
--- /dev/null
+++ b/e2e/content-services/upload/remove-upload.e2e.ts
@@ -0,0 +1,101 @@
+/*!
+ * @license
+ * Copyright 2019 Alfresco Software, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { LoginPage } from '@alfresco/adf-testing';
+import { ContentServicesPage } from '../../pages/adf/contentServicesPage';
+import { UploadDialog } from '../../pages/adf/dialog/uploadDialog';
+import { VersionManagePage } from '../../pages/adf/versionManagerPage';
+
+import { AcsUserModel } from '../../models/ACS/acsUserModel';
+import { FileModel } from '../../models/ACS/fileModel';
+
+import TestConfig = require('../../test.config');
+import resources = require('../../util/resources');
+
+import { AlfrescoApiCompatibility as AlfrescoApi } from '@alfresco/js-api';
+import { browser } from 'protractor';
+
+describe('Upload component', () => {
+ const contentServicesPage = new ContentServicesPage();
+ const uploadDialog = new UploadDialog();
+ const versionManagePage = new VersionManagePage();
+ const loginPage = new LoginPage();
+ const acsUser = new AcsUserModel();
+
+ const docxFileModel = new FileModel({
+ name: resources.Files.ADF_DOCUMENTS.DOCX_SUPPORTED.file_name,
+ location: resources.Files.ADF_DOCUMENTS.DOCX_SUPPORTED.file_location
+ });
+
+ const fileModelVersion = new FileModel({
+ 'name': resources.Files.ADF_DOCUMENTS.PNG.file_name,
+ 'location': resources.Files.ADF_DOCUMENTS.PNG.file_location
+ });
+
+ beforeAll(async (done) => {
+ this.alfrescoJsApi = new AlfrescoApi({
+ provider: 'ECM',
+ hostEcm: TestConfig.adf.url
+ });
+
+ await this.alfrescoJsApi.login(
+ TestConfig.adf.adminEmail,
+ TestConfig.adf.adminPassword
+ );
+
+ await this.alfrescoJsApi.core.peopleApi.addPerson(acsUser);
+
+ await this.alfrescoJsApi.login(acsUser.id, acsUser.password);
+
+ loginPage.loginToContentServicesUsingUserModel(acsUser);
+
+ contentServicesPage.goToDocumentList();
+
+ done();
+ });
+
+ beforeEach(() => {
+ contentServicesPage.goToDocumentList();
+ });
+
+ it('should remove uploaded file', () => {
+ contentServicesPage.uploadFile(docxFileModel.location);
+ uploadDialog.fileIsUploaded(docxFileModel.name);
+ uploadDialog
+ .removeUploadedFile(docxFileModel.name)
+ .fileIsCancelled(docxFileModel.name)
+ .clickOnCloseButton();
+ });
+
+ it('should revert to last version when remove uploaded version file', () => {
+ contentServicesPage.uploadFile(docxFileModel.location);
+ uploadDialog.fileIsUploaded(docxFileModel.name);
+ contentServicesPage.checkContentIsDisplayed(docxFileModel.name);
+
+ contentServicesPage.versionManagerContent(docxFileModel.name);
+ versionManagePage.showNewVersionButton.click();
+ versionManagePage.uploadNewVersionFile(
+ fileModelVersion.location
+ );
+ versionManagePage.closeVersionDialog();
+ uploadDialog
+ .removeUploadedFile(fileModelVersion.name)
+ .fileIsCancelled(fileModelVersion.name);
+ browser.refresh();
+ contentServicesPage.checkContentIsDisplayed(docxFileModel.name);
+ });
+});
diff --git a/lib/content-services/i18n/en.json b/lib/content-services/i18n/en.json
index 17022cb032..bfbfd692ae 100644
--- a/lib/content-services/i18n/en.json
+++ b/lib/content-services/i18n/en.json
@@ -161,7 +161,8 @@
"500": "Internal server error, try again or contact IT support [500]",
"504": "The server timed out, try again or contact IT support [504]",
"403": "Insufficient permissions to upload in this location [403]",
- "404": "Upload location no longer exists [404]"
+ "404": "Upload location no longer exists [404]",
+ "409": "A file with the same name already exists [409]"
},
"ARIA-LABEL": {
"ERROR": "Upload error"
diff --git a/lib/content-services/upload/components/file-uploading-list-row.component.html b/lib/content-services/upload/components/file-uploading-list-row.component.html
index 192491d5d3..1a50d171d2 100644
--- a/lib/content-services/upload/components/file-uploading-list-row.component.html
+++ b/lib/content-services/upload/components/file-uploading-list-row.component.html
@@ -1,16 +1,22 @@
-
+
insert_drive_file
+
+
{{ file.name }}
+
+ {{
+ versionNumber
+ }}
+
+
{
beforeEach(() => {
fixture = TestBed.createComponent(FileUploadingListRowComponent);
component = fixture.componentInstance;
- component.file = file;
});
- it('emits cancel event', () => {
- spyOn(component.cancel, 'emit');
- component.onCancel(component.file);
+ describe('events', () => {
+ beforeEach(() => {
+ component.file = file;
+ });
- expect(component.cancel.emit).toHaveBeenCalledWith(file);
+ it('should emit cancel event', () => {
+ spyOn(component.cancel, 'emit');
+ component.onCancel(component.file);
+
+ expect(component.cancel.emit).toHaveBeenCalledWith(file);
+ });
+
+ it('should emit remove event', () => {
+ spyOn(component.remove, 'emit');
+ component.onRemove(component.file);
+
+ expect(component.remove.emit).toHaveBeenCalledWith(file);
+ });
});
- it('emits remove event', () => {
- spyOn(component.remove, 'emit');
- component.onRemove(component.file);
+ it('should render node version when upload a version file', () => {
+ component.file = new FileModel(
{ name: 'fake-name' });
+ component.file.options = { newVersion: true };
+ component.file.data = { entry: { properties: { 'cm:versionLabel': '1' } } };
- expect(component.remove.emit).toHaveBeenCalledWith(file);
+ fixture.detectChanges();
+
+ expect(fixture.nativeElement.querySelector(
+ '.adf-file-uploading-row__version'
+ ).textContent).toContain('1');
});
});
diff --git a/lib/content-services/upload/components/file-uploading-list-row.component.ts b/lib/content-services/upload/components/file-uploading-list-row.component.ts
index 04ce69ad89..4ab520c834 100644
--- a/lib/content-services/upload/components/file-uploading-list-row.component.ts
+++ b/lib/content-services/upload/components/file-uploading-list-row.component.ts
@@ -48,4 +48,26 @@ export class FileUploadingListRowComponent {
this.file.status === FileUploadStatus.Aborted ||
this.file.status === FileUploadStatus.Deleted;
}
+
+ get versionNumber(): string {
+ return this.file.data.entry.properties['cm:versionLabel'];
+ }
+
+ get mimeType(): string {
+ if (this.file && this.file.file && this.file.file.type) {
+ return this.file.file.type;
+ }
+
+ return 'default';
+ }
+
+ isUploadVersion(): boolean {
+ return (
+ !!this.file.data &&
+ this.file.options &&
+ this.file.options.newVersion &&
+ this.file.data.entry.properties &&
+ this.file.data.entry.properties['cm:versionLabel']
+ );
+ }
}
diff --git a/lib/content-services/upload/components/file-uploading-list.component.spec.ts b/lib/content-services/upload/components/file-uploading-list.component.spec.ts
index 4afa46c18e..c2e5789511 100644
--- a/lib/content-services/upload/components/file-uploading-list.component.spec.ts
+++ b/lib/content-services/upload/components/file-uploading-list.component.spec.ts
@@ -17,7 +17,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslationService, FileUploadStatus, NodesApiService, UploadService,
- setupTestBed, CoreModule, AlfrescoApiService, AlfrescoApiServiceMock
+ setupTestBed, CoreModule, AlfrescoApiService, AlfrescoApiServiceMock, FileModel, FileUploadOptions
} from '@alfresco/adf-core';
import { of, throwError } from 'rxjs';
import { UploadModule } from '../upload.module';
@@ -30,6 +30,7 @@ describe('FileUploadingListComponent', () => {
let uploadService: UploadService;
let nodesApiService: NodesApiService;
let translateService: TranslationService;
+ let alfrescoApiService: AlfrescoApiService;
let file: any;
beforeEach(() => {
@@ -55,6 +56,7 @@ describe('FileUploadingListComponent', () => {
translateService = TestBed.get(TranslationService);
fixture = TestBed.createComponent(FileUploadingListComponent);
+ alfrescoApiService = TestBed.get(AlfrescoApiService);
component = fixture.componentInstance;
spyOn(translateService, 'get').and.returnValue(of('some error message'));
@@ -106,6 +108,30 @@ describe('FileUploadingListComponent', () => {
expect(uploadService.cancelUpload).toHaveBeenCalled();
});
+ it('should delete node version', () => {
+ spyOn(alfrescoApiService.versionsApi, 'deleteVersion').and.returnValue(of(file));
+ file = new FileModel( { name: 'fake-name' });
+ file.options = { newVersion: true };
+ file.data = { entry: { id: 'nodeId', properties: { 'cm:versionLabel': '1' } } };
+
+ component.removeFile(file);
+
+ expect(alfrescoApiService.versionsApi.deleteVersion).toHaveBeenCalled();
+ });
+
+ it('should throw error when delete node version fails', (done) => {
+ spyOn(alfrescoApiService.versionsApi, 'deleteVersion').and.returnValue(throwError(file));
+ file = new FileModel( { name: 'fake-name' });
+ file.options = { newVersion: true };
+ file.data = { entry: { id: 'nodeId', properties: { 'cm:versionLabel': '1' } } };
+
+ component.error.subscribe(() => {
+ done();
+ });
+
+ component.removeFile(file);
+ });
+
describe('Events', () => {
it('should throw an error event if delete file goes wrong', (done) => {
diff --git a/lib/content-services/upload/components/file-uploading-list.component.ts b/lib/content-services/upload/components/file-uploading-list.component.ts
index 054aa03a3c..c1954a11c8 100644
--- a/lib/content-services/upload/components/file-uploading-list.component.ts
+++ b/lib/content-services/upload/components/file-uploading-list.component.ts
@@ -15,9 +15,23 @@
* limitations under the License.
*/
-import { FileModel, FileUploadStatus, NodesApiService, TranslationService, UploadService } from '@alfresco/adf-core';
-import { Component, ContentChild, Input, Output, TemplateRef, EventEmitter } from '@angular/core';
-import { Observable, forkJoin, of } from 'rxjs';
+import {
+ FileModel,
+ FileUploadStatus,
+ NodesApiService,
+ AlfrescoApiService,
+ TranslationService,
+ UploadService
+} from '@alfresco/adf-core';
+import {
+ Component,
+ ContentChild,
+ Input,
+ Output,
+ TemplateRef,
+ EventEmitter
+} from '@angular/core';
+import { Observable, forkJoin, of, from } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
@Component({
@@ -26,7 +40,6 @@ import { map, catchError } from 'rxjs/operators';
styleUrls: ['./file-uploading-list.component.scss']
})
export class FileUploadingListComponent {
-
FileUploadStatus = FileUploadStatus;
@ContentChild(TemplateRef)
@@ -40,10 +53,11 @@ export class FileUploadingListComponent {
error: EventEmitter = new EventEmitter();
constructor(
+ private alfrescoApiService: AlfrescoApiService,
private uploadService: UploadService,
private nodesApi: NodesApiService,
- private translateService: TranslationService) {
- }
+ private translateService: TranslationService
+ ) {}
/**
* Cancel file upload
@@ -56,100 +70,147 @@ export class FileUploadingListComponent {
this.uploadService.cancelUpload(file);
}
+ /**
+ * Remove uploaded file
+ *
+ * @param file File model to remove upload for.
+ *
+ * @memberOf FileUploadingListComponent
+ */
removeFile(file: FileModel): void {
- this.deleteNode(file)
- .subscribe(() => {
- if ( file.status === FileUploadStatus.Error) {
+ if (file.options && file.options.newVersion) {
+ this.deleteNodeVersion(file).subscribe(() => {
+ if (file.status === FileUploadStatus.Error) {
+ this.notifyError(file);
+ }
+ this.uploadService.cancelUpload(file);
+ });
+ } else {
+ this.deleteNode(file).subscribe(() => {
+ if (file.status === FileUploadStatus.Error) {
this.notifyError(file);
}
+ this.cancelNodeVersionInstances(file);
this.uploadService.cancelUpload(file);
});
+ }
}
/**
* Call the appropriate method for each file, depending on state
*/
cancelAllFiles(): void {
- this.getUploadingFiles()
- .forEach((file) => this.uploadService.cancelUpload(file));
+ this.getUploadingFiles().forEach((file) =>
+ this.uploadService.cancelUpload(file)
+ );
const deletedFiles = this.files
.filter((file) => file.status === FileUploadStatus.Complete)
.map((file) => this.deleteNode(file));
- forkJoin(...deletedFiles)
- .subscribe((files: FileModel[]) => {
- const errors = files
- .filter((file) => file.status === FileUploadStatus.Error);
+ forkJoin(...deletedFiles).subscribe((files: FileModel[]) => {
+ const errors = files.filter(
+ (file) => file.status === FileUploadStatus.Error
+ );
- if (errors.length) {
- this.notifyError(...errors);
- }
+ if (errors.length) {
+ this.notifyError(...errors);
+ }
- this.uploadService.cancelUpload(...files);
- });
+ this.uploadService.cancelUpload(...files);
+ });
}
/**
* Checks if all the files are uploaded false if there is at least one file in Progress | Starting | Pending
*/
isUploadCompleted(): boolean {
- return !this.isUploadCancelled() &&
+ return (
+ !this.isUploadCancelled() &&
Boolean(this.files.length) &&
- !this.files
- .some(({status}) =>
+ !this.files.some(
+ ({ status }) =>
status === FileUploadStatus.Starting ||
status === FileUploadStatus.Progress ||
status === FileUploadStatus.Pending
- );
+ )
+ );
}
/**
* Check if all the files are Cancelled | Aborted | Error. false if there is at least one file in uploading states
*/
isUploadCancelled(): boolean {
- return !!this.files.length &&
- this.files
- .every(({status}) =>
+ return (
+ !!this.files.length &&
+ this.files.every(
+ ({ status }) =>
status === FileUploadStatus.Aborted ||
status === FileUploadStatus.Cancelled ||
status === FileUploadStatus.Deleted
- );
+ )
+ );
}
private deleteNode(file: FileModel): Observable {
const { id } = file.data.entry;
- return this.nodesApi
- .deleteNode(id, { permanent: true })
- .pipe(
- map(() => {
- file.status = FileUploadStatus.Deleted;
- return file;
- }),
- catchError(() => {
- file.status = FileUploadStatus.Error;
- return of(file);
- })
- );
+ return this.nodesApi.deleteNode(id, { permanent: true }).pipe(
+ map(() => {
+ file.status = FileUploadStatus.Deleted;
+ return file;
+ }),
+ catchError(() => {
+ file.status = FileUploadStatus.Error;
+ return of(file);
+ })
+ );
+ }
+
+ private deleteNodeVersion(file: FileModel): Observable {
+ return from(
+ this.alfrescoApiService.versionsApi.deleteVersion(
+ file.data.entry.id,
+ file.data.entry.properties['cm:versionLabel']
+ )
+ ).pipe(
+ map(() => {
+ file.status = FileUploadStatus.Deleted;
+ return file;
+ }),
+ catchError(() => {
+ file.status = FileUploadStatus.Error;
+ return of(file);
+ })
+ );
+ }
+
+ private cancelNodeVersionInstances(file) {
+ this.files
+ .filter(
+ (item) =>
+ item.data.entry.id === file.data.entry.id &&
+ item.options.newVersion
+ )
+ .map((item) => {
+ item.status = FileUploadStatus.Deleted;
+ });
}
private notifyError(...files: FileModel[]) {
let messageError: string = null;
if (files.length === 1) {
- messageError = this.translateService
- .instant(
- 'FILE_UPLOAD.MESSAGES.REMOVE_FILE_ERROR',
- { fileName: files[0].name}
- );
+ messageError = this.translateService.instant(
+ 'FILE_UPLOAD.MESSAGES.REMOVE_FILE_ERROR',
+ { fileName: files[0].name }
+ );
} else {
- messageError = this.translateService
- .instant(
- 'FILE_UPLOAD.MESSAGES.REMOVE_FILES_ERROR',
- { total: files.length }
- );
+ messageError = this.translateService.instant(
+ 'FILE_UPLOAD.MESSAGES.REMOVE_FILES_ERROR',
+ { total: files.length }
+ );
}
this.error.emit(messageError);
diff --git a/lib/core/services/thumbnail.service.ts b/lib/core/services/thumbnail.service.ts
index e211e1e163..eb7e181a33 100644
--- a/lib/core/services/thumbnail.service.ts
+++ b/lib/core/services/thumbnail.service.ts
@@ -84,6 +84,7 @@ export class ThumbnailService {
'application/vnd.sun.xml.writer': './assets/images/ft_ic_ms_word.svg',
'application/vnd.sun.xml.writer.template': './assets/images/ft_ic_ms_word.svg',
'application/rtf': './assets/images/ft_ic_ms_word.svg',
+ 'text/rtf': './assets/images/ft_ic_ms_word.svg',
'application/vnd.ms-powerpoint': './assets/images/ft_ic_ms_powerpoint.svg',
'application/vnd.openxmlformats-officedocument.presentationml.presentation': './assets/images/ft_ic_ms_powerpoint.svg',
'application/vnd.openxmlformats-officedocument.presentationml.template': './assets/images/ft_ic_ms_powerpoint.svg',