diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 529759f53..00797dff3 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -50,6 +50,7 @@ import { APP_ROUTES } from './app.routes'; import { FilesComponent } from './components/files/files.component'; import { LibrariesComponent } from './components/libraries/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 { AppStoreModule } from './store/app-store.module'; @@ -74,6 +75,7 @@ import { DocumentListCustomComponentsModule } from './components/dl-custom-compo import { AppSearchResultsModule } from './components/search/search-results.module'; import { AppLoginModule } from './components/login/login.module'; import { AppHeaderModule } from './components/header/header.module'; +import { AppNodeVersionModule } from './components/node-version/node-version.module'; import { environment } from '../environments/environment'; import { AppDataService } from './services/data.service'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; @@ -111,6 +113,7 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; AppSearchInputModule, AppSearchResultsModule, AppHeaderModule, + AppNodeVersionModule, TranslateModule.forRoot({ loader: { provide: TranslateLoader, useClass: TranslateLoaderService } }) @@ -120,6 +123,7 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; FilesComponent, LibrariesComponent, FavoriteLibrariesComponent, + NodeVersionUploadDialogComponent, NodeVersionsDialogComponent ], providers: [ @@ -135,7 +139,11 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; } } ], - entryComponents: [LibraryDialogComponent, NodeVersionsDialogComponent], + entryComponents: [ + NodeVersionsDialogComponent, + NodeVersionUploadDialogComponent, + LibraryDialogComponent + ], bootstrap: [AppComponent] }) export class AppModule {} diff --git a/src/app/components/favorites/favorites.component.ts b/src/app/components/favorites/favorites.component.ts index 99c860225..fea7d4c51 100644 --- a/src/app/components/favorites/favorites.component.ts +++ b/src/app/components/favorites/favorites.component.ts @@ -38,7 +38,8 @@ import { AppStore } from '../../store/states/app.state'; import { PageComponent } from '../page.component'; import { ContentApiService } from '../../services/content-api.service'; import { AppExtensionService } from '../../extensions/extension.service'; -import { map } from 'rxjs/operators'; +import { map, debounceTime } from 'rxjs/operators'; +import { FileUploadEvent, UploadService } from '@alfresco/adf-core'; @Component({ templateUrl: './favorites.component.html' @@ -54,6 +55,7 @@ export class FavoritesComponent extends PageComponent implements OnInit { extensions: AppExtensionService, private contentApi: ContentApiService, content: ContentManagementService, + private uploadService: UploadService, private breakpointObserver: BreakpointObserver ) { super(store, extensions, content); @@ -69,6 +71,13 @@ export class FavoritesComponent extends PageComponent implements OnInit { this.content.nodesMoved.subscribe(() => this.reload()), this.content.favoriteRemoved.subscribe(() => this.reload()), this.content.favoriteToggle.subscribe(() => this.reload()), + this.content.favoriteToggle.subscribe(() => this.reload()), + this.uploadService.fileUploadComplete + .pipe(debounceTime(300)) + .subscribe(file => this.onFileUploadedEvent(file)), + this.uploadService.fileUploadDeleted + .pipe(debounceTime(300)) + .subscribe(file => this.onFileUploadedEvent(file)), this.breakpointObserver .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) @@ -114,4 +123,8 @@ export class FavoritesComponent extends PageComponent implements OnInit { } } } + + private onFileUploadedEvent(event: FileUploadEvent) { + this.documentList.reload(); + } } diff --git a/src/app/components/node-version/node-version-form.component.html b/src/app/components/node-version/node-version-form.component.html new file mode 100644 index 000000000..b7d265c96 --- /dev/null +++ b/src/app/components/node-version/node-version-form.component.html @@ -0,0 +1,27 @@ +
+

+ {{ 'VERSION.FORM.SUBTITLE' | translate }} +

+ + + + {{ option.label | translate }} + + + + + + +
diff --git a/src/app/components/node-version/node-version-form.component.scss b/src/app/components/node-version/node-version-form.component.scss new file mode 100644 index 000000000..1eba72091 --- /dev/null +++ b/src/app/components/node-version/node-version-form.component.scss @@ -0,0 +1,24 @@ +.app-node-version-form__container { + display: flex; + max-width: 400px; + + .form { + width: 100%; + } + + .form__version { + display: flex; + flex-direction: column; + padding: 20px 0; + } + + .form__version--option:last-child { + padding-top: 20px; + } + + .form { + padding: 0 10px; + display: flex; + flex-direction: column; + } +} diff --git a/src/app/components/node-version/node-version-form.component.ts b/src/app/components/node-version/node-version-form.component.ts new file mode 100644 index 000000000..5caed5ac1 --- /dev/null +++ b/src/app/components/node-version/node-version-form.component.ts @@ -0,0 +1,80 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 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 . + */ + +import { + Component, + OnDestroy, + OnInit, + ViewEncapsulation, + Output, + EventEmitter +} from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + selector: 'app-node-version-form', + templateUrl: './node-version-form.component.html', + styleUrls: ['./node-version-form.component.scss'], + encapsulation: ViewEncapsulation.None, + host: { class: 'app-node-version-form__container' }, + exportAs: 'nodeVersionForm' +}) +export class AppNodeVersionFormComponent implements OnInit, OnDestroy { + @Output() update: EventEmitter = new EventEmitter(); + + form: FormGroup; + + private onDestroy$: Subject = new Subject(); + private versionOptions = [ + { label: 'VERSION.FORM.VERSION.MAJOR', value: 'major' }, + { label: 'VERSION.FORM.VERSION.MINOR', value: 'minor' } + ]; + + constructor(private formBuilder: FormBuilder) {} + + ngOnInit() { + this.form = this.formBuilder.group({ + comment: ['', Validators.required], + version: [this.versionOptions[0].value] + }); + + this.form.valueChanges + .pipe(takeUntil(this.onDestroy$)) + .subscribe(values => { + this.update.emit(values); + }); + } + + get versions() { + return this.versionOptions; + } + + ngOnDestroy() { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } +} diff --git a/src/app/components/node-version/node-version.module.ts b/src/app/components/node-version/node-version.module.ts new file mode 100644 index 000000000..d8d5672c0 --- /dev/null +++ b/src/app/components/node-version/node-version.module.ts @@ -0,0 +1,57 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 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 . + */ + +import { AppNodeVersionFormComponent } from './node-version-form.component'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatButtonModule } from '@angular/material/button'; + +import { CoreModule } from '@alfresco/adf-core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { CommonModule } from '@angular/common'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; + +import { NgModule } from '@angular/core'; +@NgModule({ + imports: [ + CoreModule, + MatButtonModule, + MatDialogModule, + FormsModule, + ReactiveFormsModule, + BrowserModule, + CommonModule, + MatRadioModule, + MatFormFieldModule, + MatInputModule + ], + exports: [AppNodeVersionFormComponent], + declarations: [AppNodeVersionFormComponent], + providers: [], + entryComponents: [AppNodeVersionFormComponent] +}) +export class AppNodeVersionModule {} diff --git a/src/app/components/recent-files/recent-files.component.ts b/src/app/components/recent-files/recent-files.component.ts index 4e29af244..95e4996ef 100644 --- a/src/app/components/recent-files/recent-files.component.ts +++ b/src/app/components/recent-files/recent-files.component.ts @@ -31,6 +31,8 @@ import { PageComponent } from '../page.component'; import { Store } from '@ngrx/store'; import { AppStore } from '../../store/states/app.state'; import { AppExtensionService } from '../../extensions/extension.service'; +import { FileUploadEvent, UploadService } from '@alfresco/adf-core'; +import { debounceTime } from 'rxjs/operators'; @Component({ templateUrl: './recent-files.component.html' @@ -44,6 +46,7 @@ export class RecentFilesComponent extends PageComponent implements OnInit { store: Store, extensions: AppExtensionService, content: ContentManagementService, + private uploadService: UploadService, private breakpointObserver: BreakpointObserver ) { super(store, extensions, content); @@ -57,6 +60,13 @@ export class RecentFilesComponent extends PageComponent implements OnInit { this.content.nodesMoved.subscribe(() => this.reload()), this.content.nodesRestored.subscribe(() => this.reload()), + this.uploadService.fileUploadComplete + .pipe(debounceTime(300)) + .subscribe(file => this.onFileUploadedEvent(file)), + this.uploadService.fileUploadDeleted + .pipe(debounceTime(300)) + .subscribe(file => this.onFileUploadedEvent(file)), + this.breakpointObserver .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) .subscribe(result => { @@ -77,4 +87,8 @@ export class RecentFilesComponent extends PageComponent implements OnInit { this.showPreview(node); } } + + private onFileUploadedEvent(event: FileUploadEvent) { + this.documentList.reload(); + } } diff --git a/src/app/components/shared-files/shared-files.component.ts b/src/app/components/shared-files/shared-files.component.ts index 0dcacc2eb..3808bf162 100644 --- a/src/app/components/shared-files/shared-files.component.ts +++ b/src/app/components/shared-files/shared-files.component.ts @@ -31,6 +31,7 @@ import { Store } from '@ngrx/store'; import { AppStore } from '../../store/states/app.state'; import { AppExtensionService } from '../../extensions/extension.service'; import { debounceTime } from 'rxjs/operators'; +import { UploadService } from '@alfresco/adf-core'; @Component({ templateUrl: './shared-files.component.html' @@ -44,6 +45,7 @@ export class SharedFilesComponent extends PageComponent implements OnInit { store: Store, extensions: AppExtensionService, content: ContentManagementService, + private uploadService: UploadService, private breakpointObserver: BreakpointObserver ) { super(store, extensions, content); @@ -60,6 +62,13 @@ export class SharedFilesComponent extends PageComponent implements OnInit { .pipe(debounceTime(300)) .subscribe(() => this.reload()), + this.uploadService.fileUploadComplete + .pipe(debounceTime(300)) + .subscribe(file => this.reload()), + this.uploadService.fileUploadDeleted + .pipe(debounceTime(300)) + .subscribe(file => this.reload()), + this.breakpointObserver .observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]) .subscribe(result => { diff --git a/src/app/dialogs/node-version-upload/node-version-upload.dialog.html b/src/app/dialogs/node-version-upload/node-version-upload.dialog.html new file mode 100644 index 000000000..fe3def10e --- /dev/null +++ b/src/app/dialogs/node-version-upload/node-version-upload.dialog.html @@ -0,0 +1,20 @@ +

+ {{ 'VERSION.DIALOG.TITLE' | translate }} +

+ +
+ +
+ +
+ + +
diff --git a/src/app/dialogs/node-version-upload/node-version-upload.dialog.scss b/src/app/dialogs/node-version-upload/node-version-upload.dialog.scss new file mode 100644 index 000000000..54210b635 --- /dev/null +++ b/src/app/dialogs/node-version-upload/node-version-upload.dialog.scss @@ -0,0 +1,11 @@ +.aca-node-version-upload-dialog { + overflow: unset; + + .dialog-actions { + justify-content: flex-end; + } + + .node-version-dialog__title { + padding-left: 8px; + } +} diff --git a/src/app/dialogs/node-version-upload/node-version-upload.dialog.ts b/src/app/dialogs/node-version-upload/node-version-upload.dialog.ts new file mode 100644 index 000000000..52ce3e598 --- /dev/null +++ b/src/app/dialogs/node-version-upload/node-version-upload.dialog.ts @@ -0,0 +1,34 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2018 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 . + */ + +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 {} diff --git a/src/app/dialogs/node-versions/node-versions.dialog.html b/src/app/dialogs/node-versions/node-versions.dialog.html index 10beef5b9..48a3c7d81 100644 --- a/src/app/dialogs/node-versions/node-versions.dialog.html +++ b/src/app/dialogs/node-versions/node-versions.dialog.html @@ -1,15 +1,17 @@ -
{{'VERSION.DIALOG.TITLE' | translate}}
+
+ {{ 'VERSION.DIALOG_ADF.TITLE' | translate }} +
diff --git a/src/app/services/content-management.service.ts b/src/app/services/content-management.service.ts index 7a0c4e48e..d1de29484 100644 --- a/src/app/services/content-management.service.ts +++ b/src/app/services/content-management.service.ts @@ -56,12 +56,13 @@ import { import { NodePermissionService } from './node-permission.service'; import { NodeInfo, DeletedNodeInfo, DeleteStatus } from '../store/models'; import { ContentApiService } from './content-api.service'; -import { sharedUrl } from '../store/selectors/app.selectors'; +import { sharedUrl, appSelection } from '../store/selectors/app.selectors'; import { NodeActionsService } from './node-actions.service'; import { TranslationService, ViewUtilService } from '@alfresco/adf-core'; +import { NodeVersionUploadDialogComponent } from '../dialogs/node-version-upload/node-version-upload.dialog'; import { NodeVersionsDialogComponent } from '../dialogs/node-versions/node-versions.dialog'; import { ShareDialogComponent } from '../components/shared/content-node-share/content-node-share.dialog'; -import { take, map, tap, mergeMap, catchError } from 'rxjs/operators'; +import { take, map, tap, mergeMap, catchError, flatMap } from 'rxjs/operators'; import { NodePermissionsDialogComponent } from '../components/permissions/permission-dialog/node-permissions.dialog'; interface RestoredNode { @@ -180,6 +181,13 @@ export class ContentManagementService { } } + versionUploadDialog() { + return this.dialogRef.open(NodeVersionUploadDialogComponent, { + disableClose: true, + panelClass: 'aca-node-version-dialog' + }); + } + shareNode(node: any): void { if (node && node.entry) { // shared and favorite @@ -1195,4 +1203,18 @@ export class ContentManagementService { } } } + + getNodeInfo() { + return this.store.select(appSelection).pipe( + take(1), + flatMap(({ file }) => { + const id = (file).entry.nodeId || (file).entry.guid; + if (!id) { + return of(file.entry); + } else { + return this.contentApi.getNodeInfo(id); + } + }) + ); + } } diff --git a/src/app/store/actions.ts b/src/app/store/actions.ts index 7e41eafa9..3d823daec 100644 --- a/src/app/store/actions.ts +++ b/src/app/store/actions.ts @@ -35,3 +35,4 @@ export * from './actions/upload.actions'; export * from './actions/modals.actions'; export * from './actions/repository.actions'; export * from './actions/info-drawer.actions'; +export * from './actions/upload.actions'; diff --git a/src/app/store/actions/node.actions.ts b/src/app/store/actions/node.actions.ts index 19be6d228..c97b4e215 100644 --- a/src/app/store/actions/node.actions.ts +++ b/src/app/store/actions/node.actions.ts @@ -39,9 +39,9 @@ export const UNSHARE_NODES = 'UNSHARE_NODES'; export const COPY_NODES = 'COPY_NODES'; export const MOVE_NODES = 'MOVE_NODES'; export const MANAGE_PERMISSIONS = 'MANAGE_PERMISSIONS'; -export const MANAGE_VERSIONS = 'MANAGE_VERSIONS'; export const PRINT_FILE = 'PRINT_FILE'; export const FULLSCREEN_VIEWER = 'FULLSCREEN_VIEWER'; +export const MANAGE_VERSIONS = 'MANAGE_VERSIONS'; export const EDIT_OFFLINE = 'EDIT_OFFLINE'; export class SetSelectedNodesAction implements Action { @@ -109,11 +109,6 @@ export class ManagePermissionsAction implements Action { constructor(public payload: MinimalNodeEntity) {} } -export class ManageVersionsAction implements Action { - readonly type = MANAGE_VERSIONS; - constructor(public payload: MinimalNodeEntity) {} -} - export class PrintFileAction implements Action { readonly type = PRINT_FILE; constructor(public payload: MinimalNodeEntity) {} @@ -124,6 +119,11 @@ export class FullscreenViewerAction implements Action { constructor(public payload: MinimalNodeEntity) {} } +export class ManageVersionsAction implements Action { + readonly type = MANAGE_VERSIONS; + constructor(public payload: MinimalNodeEntity) {} +} + export class EditOfflineAction implements Action { readonly type = EDIT_OFFLINE; constructor(public payload: any) {} diff --git a/src/app/store/actions/upload.actions.ts b/src/app/store/actions/upload.actions.ts index f60858e18..267c3ac3b 100644 --- a/src/app/store/actions/upload.actions.ts +++ b/src/app/store/actions/upload.actions.ts @@ -27,6 +27,7 @@ import { Action } from '@ngrx/store'; export const UPLOAD_FILES = 'UPLOAD_FILES'; export const UPLOAD_FOLDER = 'UPLOAD_FOLDER'; +export const UPLOAD_FILE_VERSION = 'UPLOAD_FILE_VERSION'; export class UploadFilesAction implements Action { readonly type = UPLOAD_FILES; @@ -37,3 +38,7 @@ export class UploadFolderAction implements Action { readonly type = UPLOAD_FOLDER; constructor(public payload: any) {} } + +export class UploadFileVersionAction implements Action { + readonly type = UPLOAD_FILE_VERSION; +} diff --git a/src/app/store/effects.ts b/src/app/store/effects.ts index ce4fa76b5..bbe4e8fed 100644 --- a/src/app/store/effects.ts +++ b/src/app/store/effects.ts @@ -34,3 +34,4 @@ export * from './effects/search.effects'; export * from './effects/library.effects'; export * from './effects/upload.effects'; export * from './effects/modals.effects'; +export * from './effects/upload.effects'; diff --git a/src/app/store/effects/node.effects.spec.ts b/src/app/store/effects/node.effects.spec.ts index 1c9ad669c..cb78022a9 100644 --- a/src/app/store/effects/node.effects.spec.ts +++ b/src/app/store/effects/node.effects.spec.ts @@ -42,8 +42,7 @@ import { EditFolderAction, CopyNodesAction, MoveNodesAction, - ManagePermissionsAction, - ManageVersionsAction + ManagePermissionsAction } from '../actions/node.actions'; import { SetCurrentFolderAction } from '../actions/app.actions'; @@ -399,36 +398,4 @@ describe('NodeEffects', () => { expect(contentService.managePermissions).not.toHaveBeenCalled(); }); }); - - describe('manageVersions$', () => { - it('should manage versions from the payload', () => { - spyOn(contentService, 'manageVersions').and.stub(); - - const node: any = { entry: { isFile: true } }; - store.dispatch(new ManageVersionsAction(node)); - - expect(contentService.manageVersions).toHaveBeenCalledWith(node); - }); - - it('should manage versions from the active selection', fakeAsync(() => { - spyOn(contentService, 'manageVersions').and.stub(); - - const node: any = { entry: { isFile: true } }; - store.dispatch(new SetSelectedNodesAction([node])); - - tick(100); - - store.dispatch(new ManageVersionsAction(null)); - - expect(contentService.manageVersions).toHaveBeenCalledWith(node); - })); - - it('should do nothing if invoking manage versions with no data', () => { - spyOn(contentService, 'manageVersions').and.stub(); - - store.dispatch(new ManageVersionsAction(null)); - - expect(contentService.manageVersions).not.toHaveBeenCalled(); - }); - }); }); diff --git a/src/app/store/effects/node.effects.ts b/src/app/store/effects/node.effects.ts index 94cbc5359..10d60f2d7 100644 --- a/src/app/store/effects/node.effects.ts +++ b/src/app/store/effects/node.effects.ts @@ -42,7 +42,9 @@ import { RestoreDeletedNodesAction, RESTORE_DELETED_NODES, ShareNodeAction, - SHARE_NODE + SHARE_NODE, + ManageVersionsAction, + MANAGE_VERSIONS } from '../actions'; import { ContentManagementService } from '../../services/content-management.service'; import { currentFolder, appSelection } from '../selectors/app.selectors'; @@ -55,8 +57,6 @@ import { MOVE_NODES, ManagePermissionsAction, MANAGE_PERMISSIONS, - ManageVersionsAction, - MANAGE_VERSIONS, PRINT_FILE, PrintFileAction, FULLSCREEN_VIEWER, diff --git a/src/app/store/effects/upload.effects.ts b/src/app/store/effects/upload.effects.ts index 45a09f6cc..640881a19 100644 --- a/src/app/store/effects/upload.effects.ts +++ b/src/app/store/effects/upload.effects.ts @@ -27,23 +27,41 @@ import { Injectable, RendererFactory2, NgZone } from '@angular/core'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { AppStore } from '../states'; -import { UploadFilesAction, UPLOAD_FILES } from '../actions'; -import { map, take } from 'rxjs/operators'; +import { + UploadFilesAction, + UPLOAD_FILES, + UploadFolderAction, + UPLOAD_FOLDER, + UPLOAD_FILE_VERSION, + UploadFileVersionAction, + SnackbarErrorAction +} from '../actions'; +import { + map, + take, + flatMap, + distinctUntilChanged, + catchError, + switchMap +} from 'rxjs/operators'; import { FileUtils, FileModel, UploadService } from '@alfresco/adf-core'; import { currentFolder } from '../selectors/app.selectors'; -import { UploadFolderAction, UPLOAD_FOLDER } from '../actions/upload.actions'; +import { fromEvent, of, forkJoin } from 'rxjs'; +import { ContentManagementService } from '../../services/content-management.service'; @Injectable() export class UploadEffects { private fileInput: HTMLInputElement; private folderInput: HTMLInputElement; + private fileVersionInput: HTMLInputElement; constructor( private store: Store, private actions$: Actions, private ngZone: NgZone, private uploadService: UploadService, - rendererFactory: RendererFactory2 + rendererFactory: RendererFactory2, + private contentService: ContentManagementService ) { const renderer = rendererFactory.createRenderer(null, null); @@ -55,6 +73,13 @@ export class UploadEffects { this.fileInput.addEventListener('change', event => this.upload(event)); renderer.appendChild(document.body, this.fileInput); + this.fileVersionInput = renderer.createElement('input') as HTMLInputElement; + this.fileVersionInput.id = 'app-upload-file-version'; + this.fileVersionInput.type = 'file'; + this.fileVersionInput.style.display = 'none'; + this.fileVersionInput.addEventListener('change', event => event); + renderer.appendChild(document.body, this.fileVersionInput); + this.folderInput = renderer.createElement('input') as HTMLInputElement; this.folderInput.id = 'app-upload-folder'; this.folderInput.type = 'file'; @@ -81,6 +106,44 @@ export class UploadEffects { }) ); + @Effect({ dispatch: false }) + uploadVersion$ = this.actions$.pipe( + ofType(UPLOAD_FILE_VERSION), + switchMap(() => { + this.fileVersionInput.click(); + return fromEvent(this.fileVersionInput, 'change').pipe( + distinctUntilChanged(), + flatMap(() => this.contentService.versionUploadDialog().afterClosed()), + flatMap(form => forkJoin(of(form), this.contentService.getNodeInfo())), + map(([form, node]) => { + const file = this.fileVersionInput.files[0]; + const fileModel = new FileModel( + file, + { + comment: form.comment, + majorVersion: form.major ? true : false, + parentId: node.parentId, + path: ((file).webkitRelativePath || '').replace( + /\/[^\/]*$/, + '' + ), + newVersion: true, + nodeType: 'cm:content' + }, + node.id + ); + + this.fileVersionInput.value = ''; + this.uploadQueue([fileModel]); + }), + catchError(error => { + this.fileVersionInput.value = ''; + return of(new SnackbarErrorAction('VERSION.ERROR.GENERIC')); + }) + ); + }) + ); + private upload(event: any): void { this.store .select(currentFolder) diff --git a/src/assets/app.extensions.json b/src/assets/app.extensions.json index cc17cc946..97c142fdc 100644 --- a/src/assets/app.extensions.json +++ b/src/assets/app.extensions.json @@ -637,6 +637,18 @@ "type": "separator", "order": 980 }, + { + "id": "app.toolbar.uploadNodeVersion", + "order": 1000, + "title": "APP.ACTIONS.UPLOAD_VERSION", + "icon": "playlist_add", + "actions": { + "click": "UPLOAD_FILE_VERSION" + }, + "rules": { + "visible": "app.toolbar.versions" + } + }, { "id": "app.toolbar.versions", "order": 1000, @@ -644,10 +656,10 @@ "icon": "history", "actions": { "click": "MANAGE_VERSIONS" - }, - "rules": { + }, + "rules": { "visible": "app.toolbar.versions" - } + } }, { "id": "app.toolbar.permissions", @@ -841,6 +853,18 @@ "type": "separator", "order": 1080 }, + { + "id": "app.context.menu.uploadNodeVersion", + "title": "APP.ACTIONS.UPLOAD_VERSION", + "order": 1100, + "icon": "playlist_add", + "actions": { + "click": "UPLOAD_FILE_VERSION" + }, + "rules": { + "visible": "app.toolbar.versions" + } + }, { "id": "app.context.menu.versions", "title": "APP.ACTIONS.VERSIONS", @@ -1054,8 +1078,20 @@ "order": 680 }, { - "id": "app.viewer.versions", + "id": "app.toolbar.uploadNodeVersion", "order": 700, + "title": "APP.ACTIONS.UPLOAD_VERSION", + "icon": "playlist_add", + "actions": { + "click": "UPLOAD_FILE_VERSION" + }, + "rules": { + "visible": "app.toolbar.versions" + } + }, + { + "id": "app.viewer.versions", + "order": 800, "title": "APP.ACTIONS.VERSIONS", "icon": "history", "actions": { @@ -1067,7 +1103,7 @@ }, { "id": "app.viewer.permissions", - "order": 800, + "order": 900, "title": "APP.ACTIONS.PERMISSIONS", "icon": "settings_input_component", "actions": { @@ -1117,7 +1153,7 @@ { "id": "app.sidebar.versions", "order": 300, - "disabled": true, + "disabled": false, "title": "APP.INFO_DRAWER.TABS.VERSIONS", "component": "app.components.tabs.versions", "rules": { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 386a7fe68..fc8441913 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -195,6 +195,7 @@ "UNSHARE": "Unshare", "DETAILS": "View details", "VERSIONS": "Manage Versions", + "UPLOAD_VERSION": "Upload new version", "TOGGLE-SIDENAV": "Toggle side navigation bar", "SHARE": "Share", "SHARE_EDIT": "Shared link settings", @@ -360,13 +361,31 @@ "UNSHARE_PERMISSION_ERROR": "You don't have permission to unshare this file" }, "VERSION": { - "DIALOG": { + "DIALOG_ADF": { "TITLE": "Manage Versions", "CLOSE": "Close" }, + "DIALOG": { + "TITLE": "Upload New Version", + "CLOSE": "Close", + "UPLOAD": "Upload" + }, + "FORM": { + "SUBTITLE": "What level of changes were made to this version?", + "VERSION": { + "MINOR": "Minor (2.#)", + "MAJOR": "Major (#.1)" + }, + "COMMENT": { + "PLACEHOLDER": "Add notes describing what changed" + } + }, "SELECTION": { "EMPTY": "Please choose a document to see the versions of it.", "NO_PERMISSION": "You don't have permission to manage the versions of this content." + }, + "ERROR": { + "GENERIC": "There was an error versioning the file" } }, "LIBRARY": {