mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[ACA-1887] Shared files - un-share file (#706)
* local ADF component * add share dialog to module * allow shared action from Shared view * change string reference * workaround share dialog from Shared view * debounce reload view time * add e2e test * mark delete on dialog closed * emit event on dialog closed * formcontrolname * cspell datetimepicker * disabled test that need ACA-1886
This commit is contained in:
parent
7f275a8151
commit
4dfc087624
@ -18,6 +18,8 @@
|
|||||||
"classlist",
|
"classlist",
|
||||||
"folderlink",
|
"folderlink",
|
||||||
"filelink",
|
"filelink",
|
||||||
|
"formcontrolname",
|
||||||
|
"datetimepicker",
|
||||||
"datatable",
|
"datatable",
|
||||||
"repo",
|
"repo",
|
||||||
"snackbar",
|
"snackbar",
|
||||||
|
@ -282,7 +282,8 @@ describe('Context menu actions - single selection : ', () => {
|
|||||||
expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`);
|
expect(await contextMenu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`);
|
||||||
expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`);
|
expect(await contextMenu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`);
|
||||||
expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`);
|
expect(await contextMenu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`);
|
||||||
expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${fileUser}`);
|
// todo enable when ACA-1886 is fixed
|
||||||
|
// expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${fileUser}`);
|
||||||
expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions is not displayed for ${fileUser}`);
|
expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions is not displayed for ${fileUser}`);
|
||||||
// TODO: enable when ACA-1794 is fixed
|
// TODO: enable when ACA-1794 is fixed
|
||||||
// expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${fileUser}`);
|
// expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${fileUser}`);
|
||||||
|
@ -456,7 +456,8 @@ describe('Granular permissions available actions : ', () => {
|
|||||||
// TODO: enable when ACA-1737 is done
|
// TODO: enable when ACA-1737 is done
|
||||||
// expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`);
|
// expect(await contextMenu.isMenuItemPresent('Move')).toBe(false, `Move is displayed for ${file1}`);
|
||||||
// expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`);
|
// expect(await contextMenu.isMenuItemPresent('Delete')).toBe(false, `Delete is displayed for ${file1}`);
|
||||||
expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${file1}`);
|
// todo enable when ACA-1886 is fixed
|
||||||
|
// expect(await contextMenu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed for ${file1}`);
|
||||||
expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions is not displayed for ${file1}`);
|
expect(await contextMenu.isMenuItemPresent('Manage Versions')).toBe(true, `Manage Versions is not displayed for ${file1}`);
|
||||||
// TODO: enable when ACA-1794 is fixed
|
// TODO: enable when ACA-1794 is fixed
|
||||||
// expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${file1}`);
|
// expect(await contextMenu.isMenuItemPresent('Permissions')).toBe(true, `Permissions is not displayed for ${file1}`);
|
||||||
@ -707,7 +708,8 @@ describe('Granular permissions available actions : ', () => {
|
|||||||
expect(await viewerToolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed`);
|
expect(await viewerToolbar.isButtonPresent('View details')).toBe(true, `View details is not displayed`);
|
||||||
await viewerToolbar.openMoreMenu();
|
await viewerToolbar.openMoreMenu();
|
||||||
expect(await viewerToolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`);
|
expect(await viewerToolbar.menu.isMenuItemPresent('Favorite')).toBe(true, `Favorite is not displayed`);
|
||||||
expect(await viewerToolbar.menu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed`);
|
// todo enable when ACA-1886 is fixed
|
||||||
|
// expect(await viewerToolbar.menu.isMenuItemPresent('Share')).toBe(true, `Share is not displayed`);
|
||||||
expect(await viewerToolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`);
|
expect(await viewerToolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed`);
|
||||||
// TODO: enable when ACA-1737 is done
|
// TODO: enable when ACA-1737 is done
|
||||||
// expect(await viewerToolbar.menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`);
|
// expect(await viewerToolbar.menu.isMenuItemPresent('Move')).toBe(false, `Move is displayed`);
|
||||||
|
@ -213,6 +213,7 @@ describe('Toolbar actions - single selection : ', () => {
|
|||||||
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`);
|
expect(await toolbar.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`);
|
||||||
expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`);
|
expect(await toolbar.isButtonPresent('Edit')).toBe(false, `Edit is displayed for ${fileUser}`);
|
||||||
await toolbar.openMoreMenu();
|
await toolbar.openMoreMenu();
|
||||||
|
expect(await toolbar.menu.isMenuItemPresent('Shared link settings')).toBe(true, `Shared is not displayed for ${fileUser}`);
|
||||||
expect(await toolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`);
|
expect(await toolbar.menu.isMenuItemPresent('Copy')).toBe(true, `Copy is not displayed for ${fileUser}`);
|
||||||
expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`);
|
expect(await toolbar.menu.isMenuItemPresent('Delete')).toBe(true, `Delete is not displayed for ${fileUser}`);
|
||||||
expect(await toolbar.menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`);
|
expect(await toolbar.menu.isMenuItemPresent('Move')).toBe(true, `Move is not displayed for ${fileUser}`);
|
||||||
|
@ -786,7 +786,8 @@ describe('Viewer actions', () => {
|
|||||||
expect(await dataTable.getRowByName(pdfFavorites).isPresent()).toBe(true, 'Item is not present in Trash');
|
expect(await dataTable.getRowByName(pdfFavorites).isPresent()).toBe(true, 'Item is not present in Trash');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Share action - [C286395]', async () => {
|
// todo enable when ACA-1886 is fixed
|
||||||
|
xit('Share action - [C286395]', async () => {
|
||||||
await dataTable.doubleClickOnRowByName(docxFavorites);
|
await dataTable.doubleClickOnRowByName(docxFavorites);
|
||||||
expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened');
|
expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened');
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import { PageComponent } from '../page.component';
|
|||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import { AppStore } from '../../store/states/app.state';
|
import { AppStore } from '../../store/states/app.state';
|
||||||
import { AppExtensionService } from '../../extensions/extension.service';
|
import { AppExtensionService } from '../../extensions/extension.service';
|
||||||
|
import { debounceTime } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './shared-files.component.html'
|
templateUrl: './shared-files.component.html'
|
||||||
@ -53,7 +54,9 @@ export class SharedFilesComponent extends PageComponent implements OnInit {
|
|||||||
this.content.nodesDeleted.subscribe(() => this.reload()),
|
this.content.nodesDeleted.subscribe(() => this.reload()),
|
||||||
this.content.nodesMoved.subscribe(() => this.reload()),
|
this.content.nodesMoved.subscribe(() => this.reload()),
|
||||||
this.content.nodesRestored.subscribe(() => this.reload()),
|
this.content.nodesRestored.subscribe(() => this.reload()),
|
||||||
this.content.linksUnshared.subscribe(() => this.reload()),
|
this.content.linksUnshared
|
||||||
|
.pipe(debounceTime(300))
|
||||||
|
.subscribe(() => this.reload()),
|
||||||
|
|
||||||
this.breakpointObserver
|
this.breakpointObserver
|
||||||
.observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape])
|
.observe([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape])
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
<div class="adf-share-link__dialog-content">
|
||||||
|
<h1 data-automation-id="adf-share-dialog-title"
|
||||||
|
class="adf-share-link__title">
|
||||||
|
{{ 'SHARE.DIALOG-TITLE' | translate }} {{ fileName }}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<mat-dialog-content>
|
||||||
|
<p class="adf-share-link__info">{{ 'SHARE.DESCRIPTION' | translate }}</p>
|
||||||
|
|
||||||
|
<div class="adf-share-link--row">
|
||||||
|
<h1 class="adf-share-link__label">{{ 'SHARE.TITLE' | translate }}</h1>
|
||||||
|
|
||||||
|
<mat-slide-toggle
|
||||||
|
color="primary"
|
||||||
|
data-automation-id="adf-share-toggle"
|
||||||
|
[checked]="isFileShared"
|
||||||
|
[disabled]="!canUpdate || isDisabled"
|
||||||
|
(change)="onSlideShareChange($event)">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form [formGroup]="form">
|
||||||
|
<mat-form-field class="full-width">
|
||||||
|
<input #sharedLinkInput
|
||||||
|
data-automation-id="adf-share-link"
|
||||||
|
class="adf-share-link__input"
|
||||||
|
matInput
|
||||||
|
cdkFocusInitial
|
||||||
|
placeholder="{{ 'SHARE.PUBLIC-LINK' | translate }}"
|
||||||
|
formControlName="sharedUrl"
|
||||||
|
readonly="readonly">
|
||||||
|
<mat-icon class="input-action" matSuffix
|
||||||
|
[clipboard-notification]="'SHARE.CLIPBOARD-MESSAGE' | translate"
|
||||||
|
[adf-clipboard]="sharedLinkInput">
|
||||||
|
link
|
||||||
|
</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="adf-share-link--row">
|
||||||
|
<h1 class="adf-share-link__label">{{ 'SHARE.EXPIRES' | translate }}</h1>
|
||||||
|
<mat-slide-toggle
|
||||||
|
color="primary"
|
||||||
|
data-automation-id="adf-expire-toggle"
|
||||||
|
[checked]="form.controls['time'].value"
|
||||||
|
[disabled]="!form.controls['time'].value"
|
||||||
|
(change)="removeExpires()">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field class="full-width">
|
||||||
|
<mat-datetimepicker-toggle [for]="datetimePicker" matSuffix></mat-datetimepicker-toggle>
|
||||||
|
<mat-datetimepicker #datetimePicker (closed)="blur(dateTimePickerInput)" type="datetime" openOnFocus="true" timeInterval="1"></mat-datetimepicker>
|
||||||
|
<input class="adf-share-link__input"
|
||||||
|
#dateTimePickerInput
|
||||||
|
matInput
|
||||||
|
[min]="minDate"
|
||||||
|
formControlName="time"
|
||||||
|
[matDatetimepicker]="datetimePicker">
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
||||||
|
</mat-dialog-content>
|
||||||
|
|
||||||
|
<div mat-dialog-actions>
|
||||||
|
<button
|
||||||
|
data-automation-id="adf-share-dialog-close"
|
||||||
|
mat-button color="primary" mat-dialog-close>
|
||||||
|
{{ 'SHARE.CLOSE' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,68 @@
|
|||||||
|
@mixin adf-share-link-typography {
|
||||||
|
letter-spacing: -0.4px;
|
||||||
|
line-height: 2;
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
font-stretch: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
opacity: 0.87;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adf-share-link-dialog {
|
||||||
|
.adf-share-link {
|
||||||
|
&__dialog-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
@include adf-share-link-typography;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
@include adf-share-link-typography;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__info {
|
||||||
|
@include adf-share-link-typography;
|
||||||
|
opacity: 0.54;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__input {
|
||||||
|
opacity: 0.54;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-action {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field-infix {
|
||||||
|
border-top: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-dialog-actions {
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field-flex {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,245 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 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 { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { TestBed, fakeAsync, async } from '@angular/core/testing';
|
||||||
|
import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import {
|
||||||
|
setupTestBed,
|
||||||
|
CoreModule,
|
||||||
|
SharedLinksApiService,
|
||||||
|
NodesApiService,
|
||||||
|
NotificationService
|
||||||
|
} from '@alfresco/adf-core';
|
||||||
|
import { ContentNodeShareModule } from './content-node-share.module';
|
||||||
|
import { ShareDialogComponent } from './content-node-share.dialog';
|
||||||
|
|
||||||
|
describe('ShareDialogComponent', () => {
|
||||||
|
let node;
|
||||||
|
let matDialog: MatDialog;
|
||||||
|
const notificationServiceMock = {
|
||||||
|
openSnackMessage: jasmine.createSpy('openSnackMessage')
|
||||||
|
};
|
||||||
|
let sharedLinksApiService: SharedLinksApiService;
|
||||||
|
let fixture;
|
||||||
|
let component;
|
||||||
|
|
||||||
|
setupTestBed({
|
||||||
|
imports: [
|
||||||
|
NoopAnimationsModule,
|
||||||
|
CoreModule.forRoot(),
|
||||||
|
ContentNodeShareModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
NodesApiService,
|
||||||
|
SharedLinksApiService,
|
||||||
|
{ provide: NotificationService, useValue: notificationServiceMock },
|
||||||
|
{ provide: MatDialogRef, useValue: {} },
|
||||||
|
{ provide: MAT_DIALOG_DATA, useValue: {} }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ShareDialogComponent);
|
||||||
|
matDialog = TestBed.get(MatDialog);
|
||||||
|
sharedLinksApiService = TestBed.get(SharedLinksApiService);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
node = {
|
||||||
|
entry: {
|
||||||
|
id: 'nodeId',
|
||||||
|
allowableOperations: ['update'],
|
||||||
|
isFile: true,
|
||||||
|
properties: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fixture.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should toggle share action when property 'sharedId' does not exists`, () => {
|
||||||
|
spyOn(sharedLinksApiService, 'createSharedLinks').and.returnValue(
|
||||||
|
of({
|
||||||
|
entry: { id: 'sharedId', sharedId: 'sharedId' }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
component.data = {
|
||||||
|
node,
|
||||||
|
permission: true,
|
||||||
|
baseShareUrl: 'some-url/'
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(sharedLinksApiService.createSharedLinks).toHaveBeenCalled();
|
||||||
|
expect(
|
||||||
|
fixture.nativeElement.querySelector('input[formcontrolname="sharedUrl"]')
|
||||||
|
.value
|
||||||
|
).toBe('some-url/sharedId');
|
||||||
|
expect(
|
||||||
|
fixture.nativeElement.querySelector('.mat-slide-toggle').classList
|
||||||
|
).toContain('mat-checked');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should not toggle share action when file has 'sharedId' property`, () => {
|
||||||
|
spyOn(sharedLinksApiService, 'createSharedLinks');
|
||||||
|
|
||||||
|
node.entry.properties['qshare:sharedId'] = 'sharedId';
|
||||||
|
|
||||||
|
component.data = {
|
||||||
|
node,
|
||||||
|
permission: true,
|
||||||
|
baseShareUrl: 'some-url/'
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(sharedLinksApiService.createSharedLinks).not.toHaveBeenCalled();
|
||||||
|
expect(
|
||||||
|
fixture.nativeElement.querySelector('input[formcontrolname="sharedUrl"]')
|
||||||
|
.value
|
||||||
|
).toBe('some-url/sharedId');
|
||||||
|
expect(
|
||||||
|
fixture.nativeElement.querySelector('.mat-slide-toggle').classList
|
||||||
|
).toContain('mat-checked');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should copy shared link and notify on button event`, async(() => {
|
||||||
|
node.entry.properties['qshare:sharedId'] = 'sharedId';
|
||||||
|
spyOn(document, 'execCommand').and.callThrough();
|
||||||
|
|
||||||
|
component.data = {
|
||||||
|
node,
|
||||||
|
permission: true,
|
||||||
|
baseShareUrl: 'some-url/'
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.nativeElement
|
||||||
|
.querySelector('.input-action')
|
||||||
|
.dispatchEvent(new MouseEvent('click'));
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(document.execCommand).toHaveBeenCalledWith('copy');
|
||||||
|
expect(notificationServiceMock.openSnackMessage).toHaveBeenCalledWith(
|
||||||
|
'SHARE.CLIPBOARD-MESSAGE'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should open a confirmation dialog when unshare button is triggered', () => {
|
||||||
|
spyOn(matDialog, 'open').and.returnValue({ beforeClose: () => of(false) });
|
||||||
|
spyOn(sharedLinksApiService, 'deleteSharedLink');
|
||||||
|
node.entry.properties['qshare:sharedId'] = 'sharedId';
|
||||||
|
|
||||||
|
component.data = {
|
||||||
|
node,
|
||||||
|
permission: true,
|
||||||
|
baseShareUrl: 'some-url/'
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.nativeElement
|
||||||
|
.querySelector('.mat-slide-toggle label')
|
||||||
|
.dispatchEvent(new MouseEvent('click'));
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(matDialog.open).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unshare file when confirmation dialog returns true', fakeAsync(() => {
|
||||||
|
spyOn(matDialog, 'open').and.returnValue({ beforeClose: () => of(true) });
|
||||||
|
spyOn(sharedLinksApiService, 'deleteSharedLink');
|
||||||
|
node.entry.properties['qshare:sharedId'] = 'sharedId';
|
||||||
|
|
||||||
|
component.data = {
|
||||||
|
node,
|
||||||
|
permission: true,
|
||||||
|
baseShareUrl: 'some-url/'
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.nativeElement
|
||||||
|
.querySelector('.mat-slide-toggle label')
|
||||||
|
.dispatchEvent(new MouseEvent('click'));
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(sharedLinksApiService.deleteSharedLink).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not unshare file when confirmation dialog returns false', fakeAsync(() => {
|
||||||
|
spyOn(matDialog, 'open').and.returnValue({ beforeClose: () => of(false) });
|
||||||
|
spyOn(sharedLinksApiService, 'deleteSharedLink');
|
||||||
|
node.entry.properties['qshare:sharedId'] = 'sharedId';
|
||||||
|
|
||||||
|
component.data = {
|
||||||
|
node,
|
||||||
|
permission: true,
|
||||||
|
baseShareUrl: 'some-url/'
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
fixture.nativeElement
|
||||||
|
.querySelector('.mat-slide-toggle label')
|
||||||
|
.dispatchEvent(new MouseEvent('click'));
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(sharedLinksApiService.deleteSharedLink).not.toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not allow unshare when node has no update permission', () => {
|
||||||
|
node.entry.properties['qshare:sharedId'] = 'sharedId';
|
||||||
|
node.entry.allowableOperations = [];
|
||||||
|
|
||||||
|
component.data = {
|
||||||
|
node,
|
||||||
|
permission: false,
|
||||||
|
baseShareUrl: 'some-url/'
|
||||||
|
};
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
fixture.nativeElement.querySelector('.mat-slide-toggle').classList
|
||||||
|
).toContain('mat-disabled');
|
||||||
|
expect(
|
||||||
|
fixture.nativeElement.querySelector('input[formcontrolname="time"]')
|
||||||
|
.disabled
|
||||||
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
fixture.nativeElement.querySelector('mat-datetimepicker-toggle button')
|
||||||
|
.disabled
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,226 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 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 {
|
||||||
|
Component,
|
||||||
|
Inject,
|
||||||
|
OnInit,
|
||||||
|
ViewEncapsulation,
|
||||||
|
ViewChild,
|
||||||
|
ElementRef,
|
||||||
|
OnDestroy
|
||||||
|
} from '@angular/core';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef, MatDialog } from '@angular/material';
|
||||||
|
import { FormGroup, FormControl } from '@angular/forms';
|
||||||
|
import { Subscription, Observable, throwError } from 'rxjs';
|
||||||
|
import { skip, skipWhile, mergeMap, catchError } from 'rxjs/operators';
|
||||||
|
import { SharedLinksApiService, NodesApiService } from '@alfresco/adf-core';
|
||||||
|
import { SharedLinkEntry, MinimalNodeEntryEntity } from 'alfresco-js-api';
|
||||||
|
import { ConfirmDialogComponent } from '@alfresco/adf-content-services';
|
||||||
|
import moment from 'moment-es6';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'aca-share-dialog',
|
||||||
|
templateUrl: './content-node-share.dialog.html',
|
||||||
|
styleUrls: ['./content-node-share.dialog.scss'],
|
||||||
|
host: { class: 'adf-share-dialog' },
|
||||||
|
encapsulation: ViewEncapsulation.None
|
||||||
|
})
|
||||||
|
export class ShareDialogComponent implements OnInit, OnDestroy {
|
||||||
|
private subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
|
minDate = moment().add(1, 'd');
|
||||||
|
sharedId: string;
|
||||||
|
fileName: string;
|
||||||
|
baseShareUrl: string;
|
||||||
|
isFileShared = false;
|
||||||
|
isDisabled = false;
|
||||||
|
form: FormGroup = new FormGroup({
|
||||||
|
sharedUrl: new FormControl(''),
|
||||||
|
time: new FormControl({ value: '', disabled: false })
|
||||||
|
});
|
||||||
|
|
||||||
|
@ViewChild('sharedLinkInput')
|
||||||
|
sharedLinkInput: ElementRef;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private sharedLinksApiService: SharedLinksApiService,
|
||||||
|
private dialogRef: MatDialogRef<ShareDialogComponent>,
|
||||||
|
private dialog: MatDialog,
|
||||||
|
private nodesApiService: NodesApiService,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: any
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (!this.canUpdate) {
|
||||||
|
this.form.controls['time'].disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.form.valueChanges
|
||||||
|
.pipe(
|
||||||
|
skip(1),
|
||||||
|
skipWhile(() => !this.isTimeFieldValid),
|
||||||
|
mergeMap(
|
||||||
|
updates => this.updateNode(updates),
|
||||||
|
formUpdates => formUpdates
|
||||||
|
),
|
||||||
|
catchError(error => {
|
||||||
|
this.form.controls.time.setValue(null);
|
||||||
|
return throwError(error);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(updates => {
|
||||||
|
this.updateEntryExpiryDate(updates);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.data.node && this.data.node.entry) {
|
||||||
|
this.fileName = this.data.node.entry.name;
|
||||||
|
this.baseShareUrl = this.data.baseShareUrl;
|
||||||
|
const properties = this.data.node.entry.properties;
|
||||||
|
|
||||||
|
if (properties && !properties['qshare:sharedId']) {
|
||||||
|
this.createSharedLinks(this.data.node.entry.id);
|
||||||
|
} else {
|
||||||
|
this.sharedId = properties['qshare:sharedId'];
|
||||||
|
this.isFileShared = true;
|
||||||
|
|
||||||
|
this.updateForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.subscriptions.forEach(subscription => subscription.unsubscribe);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeShare() {
|
||||||
|
this.deleteSharedLink(this.sharedId);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSlideShareChange(event: any) {
|
||||||
|
if (event.checked) {
|
||||||
|
this.createSharedLinks(this.data.node.entry.id);
|
||||||
|
} else {
|
||||||
|
this.openConfirmationDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get isTimeFieldValid() {
|
||||||
|
return this.form.controls.time.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
get canUpdate() {
|
||||||
|
return this.data.permission;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeExpires() {
|
||||||
|
this.form.controls.time.setValue(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
blur(input: HTMLInputElement) {
|
||||||
|
input.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
private openConfirmationDialog() {
|
||||||
|
this.isFileShared = false;
|
||||||
|
|
||||||
|
this.dialog
|
||||||
|
.open(ConfirmDialogComponent, {
|
||||||
|
data: {
|
||||||
|
title: 'SHARE.CONFIRMATION.DIALOG-TITLE',
|
||||||
|
message: 'SHARE.CONFIRMATION.MESSAGE',
|
||||||
|
yesLabel: 'SHARE.CONFIRMATION.REMOVE',
|
||||||
|
noLabel: 'SHARE.CONFIRMATION.CANCEL'
|
||||||
|
},
|
||||||
|
minWidth: '250px',
|
||||||
|
closeOnNavigation: true
|
||||||
|
})
|
||||||
|
.beforeClose()
|
||||||
|
.subscribe(deleteSharedLink => {
|
||||||
|
if (deleteSharedLink) {
|
||||||
|
this.deleteSharedLink(this.sharedId);
|
||||||
|
} else {
|
||||||
|
this.isFileShared = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private createSharedLinks(nodeId: string) {
|
||||||
|
this.isDisabled = true;
|
||||||
|
|
||||||
|
this.sharedLinksApiService.createSharedLinks(nodeId).subscribe(
|
||||||
|
(sharedLink: SharedLinkEntry) => {
|
||||||
|
if (sharedLink.entry) {
|
||||||
|
this.sharedId = sharedLink.entry.id;
|
||||||
|
this.data.node.entry.properties['qshare:sharedId'] = this.sharedId;
|
||||||
|
this.isDisabled = false;
|
||||||
|
this.isFileShared = true;
|
||||||
|
|
||||||
|
this.updateForm();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.isDisabled = false;
|
||||||
|
this.isFileShared = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private deleteSharedLink(sharedId: string) {
|
||||||
|
this.isDisabled = true;
|
||||||
|
|
||||||
|
this.sharedLinksApiService.deleteSharedLink(sharedId).subscribe(
|
||||||
|
() => {
|
||||||
|
this.data.node.entry.properties['qshare:sharedId'] = null;
|
||||||
|
this.data.node.entry.properties['qshare:expiryDate'] = null;
|
||||||
|
this.dialogRef.close(this.data.node);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.isDisabled = false;
|
||||||
|
this.isFileShared = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateForm() {
|
||||||
|
const { entry } = this.data.node;
|
||||||
|
const expiryDate = entry.properties['qshare:expiryDate'];
|
||||||
|
|
||||||
|
this.form.setValue({
|
||||||
|
sharedUrl: `${this.baseShareUrl}${this.sharedId}`,
|
||||||
|
time: expiryDate ? expiryDate : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateNode(updates): Observable<MinimalNodeEntryEntity> {
|
||||||
|
return this.nodesApiService.updateNode(this.data.node.entry.id, {
|
||||||
|
properties: {
|
||||||
|
'qshare:expiryDate': updates.time ? updates.time.utc().format() : null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateEntryExpiryDate(updates) {
|
||||||
|
const { properties } = this.data.node.entry;
|
||||||
|
|
||||||
|
properties['qshare:expiryDate'] = updates.time
|
||||||
|
? updates.time.local()
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright 2016 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 { NgModule, ModuleWithProviders } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CoreModule } from '@alfresco/adf-core';
|
||||||
|
import { ShareDialogComponent } from './content-node-share.dialog';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CoreModule.forChild(), CommonModule],
|
||||||
|
declarations: [ShareDialogComponent],
|
||||||
|
exports: [ShareDialogComponent],
|
||||||
|
entryComponents: [ShareDialogComponent]
|
||||||
|
})
|
||||||
|
export class ContentNodeShareModule {
|
||||||
|
static forRoot(): ModuleWithProviders {
|
||||||
|
return {
|
||||||
|
ngModule: ContentNodeShareModule
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static forChild(): ModuleWithProviders {
|
||||||
|
return {
|
||||||
|
ngModule: ContentNodeShareModule
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -26,9 +26,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { CoreModule } from '@alfresco/adf-core';
|
import { CoreModule } from '@alfresco/adf-core';
|
||||||
import { ContentNodeShareModule } from '@alfresco/adf-content-services';
|
|
||||||
import { ExtensionsModule } from '@alfresco/adf-extensions';
|
import { ExtensionsModule } from '@alfresco/adf-extensions';
|
||||||
import { ToggleSharedModule } from './toggle-shared/toggle-shared.module';
|
import { ToggleSharedModule } from './toggle-shared/toggle-shared.module';
|
||||||
|
import { ContentNodeShareModule } from './content-node-share/content-node-share.module';
|
||||||
|
import { ShareDialogComponent } from './content-node-share/content-node-share.dialog';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -37,6 +38,7 @@ import { ToggleSharedModule } from './toggle-shared/toggle-shared.module';
|
|||||||
ExtensionsModule,
|
ExtensionsModule,
|
||||||
ContentNodeShareModule,
|
ContentNodeShareModule,
|
||||||
ToggleSharedModule
|
ToggleSharedModule
|
||||||
]
|
],
|
||||||
|
entryComponents: [ShareDialogComponent]
|
||||||
})
|
})
|
||||||
export class AppSharedModule {}
|
export class AppSharedModule {}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<button mat-menu-item data-automation-id="share-action-button" (click)="editSharedNode(selection)">
|
<button mat-menu-item data-automation-id="share-action-button" (click)="editSharedNode(selection)">
|
||||||
<mat-icon>share</mat-icon>
|
<mat-icon>share</mat-icon>
|
||||||
<ng-container *ngIf="isShared(selection); else not_shared">
|
<ng-container *ngIf="isShared(selection); else not_shared">
|
||||||
<span>{{ 'APP.ACTIONS.CONTEXT_EDIT_SHARE' | translate }}</span>
|
<span>{{ 'APP.ACTIONS.SHARE_EDIT' | translate }}</span>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -45,6 +45,11 @@ export class ToggleSharedComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isShared(selection) {
|
isShared(selection) {
|
||||||
|
// workaround for shared files
|
||||||
|
if (selection.first.entry && selection.first.entry.sharedByUser) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
selection.first.entry &&
|
selection.first.entry &&
|
||||||
selection.first.entry.properties &&
|
selection.first.entry.properties &&
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
import { RuleContext, RuleParameter } from '@alfresco/adf-extensions';
|
import { RuleContext, RuleParameter } from '@alfresco/adf-extensions';
|
||||||
import {
|
import {
|
||||||
isNotTrashcan,
|
isNotTrashcan,
|
||||||
isNotSharedFiles,
|
isNotFavorites,
|
||||||
isNotLibraries,
|
isNotLibraries,
|
||||||
isFavorites,
|
isFavorites,
|
||||||
isLibraries,
|
isLibraries,
|
||||||
@ -72,7 +72,7 @@ export function canShareFile(
|
|||||||
): boolean {
|
): boolean {
|
||||||
if (
|
if (
|
||||||
isNotTrashcan(context, ...args) &&
|
isNotTrashcan(context, ...args) &&
|
||||||
isNotSharedFiles(context, ...args) &&
|
isNotFavorites(context, ...args) &&
|
||||||
context.selection.file
|
context.selection.file
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -28,8 +28,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { MatDialog, MatSnackBar } from '@angular/material';
|
import { MatDialog, MatSnackBar } from '@angular/material';
|
||||||
import {
|
import {
|
||||||
FolderDialogComponent,
|
FolderDialogComponent,
|
||||||
ConfirmDialogComponent,
|
ConfirmDialogComponent
|
||||||
ShareDialogComponent
|
|
||||||
} from '@alfresco/adf-content-services';
|
} from '@alfresco/adf-content-services';
|
||||||
import { LibraryDialogComponent } from '../dialogs/library/library.dialog';
|
import { LibraryDialogComponent } from '../dialogs/library/library.dialog';
|
||||||
import {
|
import {
|
||||||
@ -59,6 +58,7 @@ import { sharedUrl } from '../store/selectors/app.selectors';
|
|||||||
import { NodeActionsService } from './node-actions.service';
|
import { NodeActionsService } from './node-actions.service';
|
||||||
import { TranslationService, ViewUtilService } from '@alfresco/adf-core';
|
import { TranslationService, ViewUtilService } from '@alfresco/adf-core';
|
||||||
import { NodeVersionsDialogComponent } from '../dialogs/node-versions/node-versions.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 } from 'rxjs/operators';
|
||||||
import { NodePermissionsDialogComponent } from '../components/permissions/permission-dialog/node-permissions.dialog';
|
import { NodePermissionsDialogComponent } from '../components/permissions/permission-dialog/node-permissions.dialog';
|
||||||
|
|
||||||
@ -173,21 +173,42 @@ export class ContentManagementService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
shareNode(node: MinimalNodeEntity): void {
|
shareNode(node: MinimalNodeEntity): void {
|
||||||
if (node && node.entry && node.entry.isFile) {
|
if (node && node.entry) {
|
||||||
this.store
|
// shared and favorite
|
||||||
.select(sharedUrl)
|
const id = node.entry.nodeId || (<any>node).entry.guid;
|
||||||
.pipe(take(1))
|
|
||||||
.subscribe(baseShareUrl => {
|
if (id) {
|
||||||
this.dialogRef.open(ShareDialogComponent, {
|
this.contentApi.getNodeInfo(id).subscribe(entry => {
|
||||||
|
this.openShareLinkDialog({ entry });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.openShareLinkDialog(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openShareLinkDialog(node) {
|
||||||
|
this.store
|
||||||
|
.select(sharedUrl)
|
||||||
|
.pipe(take(1))
|
||||||
|
.subscribe(baseShareUrl => {
|
||||||
|
this.dialogRef
|
||||||
|
.open(ShareDialogComponent, {
|
||||||
width: '600px',
|
width: '600px',
|
||||||
panelClass: 'adf-share-link-dialog',
|
panelClass: 'adf-share-link-dialog',
|
||||||
data: {
|
data: {
|
||||||
|
permission: this.permission.check(node, ['update']),
|
||||||
node,
|
node,
|
||||||
baseShareUrl
|
baseShareUrl
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe(deletedSharedLink => {
|
||||||
|
if (deletedSharedLink) {
|
||||||
|
this.linksUnshared.next(deletedSharedLink);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createFolder(parentNodeId: string) {
|
createFolder(parentNodeId: string) {
|
||||||
|
@ -131,7 +131,7 @@
|
|||||||
"VERSIONS": "Manage Versions",
|
"VERSIONS": "Manage Versions",
|
||||||
"TOGGLE-SIDENAV": "Toggle side navigation bar",
|
"TOGGLE-SIDENAV": "Toggle side navigation bar",
|
||||||
"SHARE": "Share",
|
"SHARE": "Share",
|
||||||
"CONTEXT_EDIT_SHARE": "Shared link settings",
|
"SHARE_EDIT": "Shared link settings",
|
||||||
"PRINT": "Print",
|
"PRINT": "Print",
|
||||||
"FULLSCREEN": "Activate full-screen mode"
|
"FULLSCREEN": "Activate full-screen mode"
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user