[ACA-1115] Quick Share (#492)

* share file

* fix

* fix test dependency

* experimental guard
This commit is contained in:
Cilibiu Bogdan
2018-07-06 12:42:13 +03:00
committed by Denys Vuika
parent e5bc3bb755
commit 3e123bee62
21 changed files with 175 additions and 6 deletions

View File

@@ -11,7 +11,8 @@
"experimental": { "experimental": {
"libraries": false, "libraries": false,
"comments": false, "comments": false,
"cardview": false "cardview": false,
"share": false
}, },
"headerColor": "#2196F3", "headerColor": "#2196F3",
"languagePicker": false, "languagePicker": false,

View File

@@ -30,7 +30,13 @@ import {
AuthenticationService, AlfrescoApiService } from '@alfresco/adf-core'; AuthenticationService, AlfrescoApiService } from '@alfresco/adf-core';
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 { SetHeaderColorAction, SetAppNameAction, SetLogoPathAction, SetLanguagePickerAction } from './store/actions'; import {
SetHeaderColorAction,
SetAppNameAction,
SetLogoPathAction,
SetLanguagePickerAction,
SetSharedUrlAction
} from './store/actions';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@@ -94,5 +100,8 @@ export class AppComponent implements OnInit {
} }
const languagePicker = this.config.get<boolean>('languagePicker'); const languagePicker = this.config.get<boolean>('languagePicker');
this.store.dispatch(new SetLanguagePickerAction(languagePicker)); this.store.dispatch(new SetLanguagePickerAction(languagePicker));
const sharedPreviewUrl = this.config.get<string>('ecmHost') + '/#/preview/s/';
this.store.dispatch(new SetSharedUrlAction(sharedPreviewUrl));
} }
} }

View File

@@ -53,6 +53,7 @@ import { SidenavComponent } from './components/sidenav/sidenav.component';
import { AboutComponent } from './components/about/about.component'; import { AboutComponent } from './components/about/about.component';
import { LocationLinkComponent } from './components/location-link/location-link.component'; import { LocationLinkComponent } from './components/location-link/location-link.component';
import { CustomDlRowComponent } from './components/custom-dl-row/custom-dl-row.component'; import { CustomDlRowComponent } from './components/custom-dl-row/custom-dl-row.component';
import { SharedLinkViewComponent } from './components/shared-link-view/shared-link-view.component';
import { NodeCopyDirective } from './common/directives/node-copy.directive'; import { NodeCopyDirective } from './common/directives/node-copy.directive';
import { NodeDeleteDirective } from './common/directives/node-delete.directive'; import { NodeDeleteDirective } from './common/directives/node-delete.directive';
import { NodeMoveDirective } from './common/directives/node-move.directive'; import { NodeMoveDirective } from './common/directives/node-move.directive';
@@ -69,6 +70,7 @@ import { SearchComponent } from './components/search/search.component';
import { SettingsComponent } from './components/settings/settings.component'; import { SettingsComponent } from './components/settings/settings.component';
import { PageTitleService as AcaPageTitleService } from './common/services/page-title.service'; import { PageTitleService as AcaPageTitleService } from './common/services/page-title.service';
import { ProfileResolver } from './common/services/profile.resolver'; import { ProfileResolver } from './common/services/profile.resolver';
import { ExperimentalGuard } from './common/services/experimental-guard.service';
import { InfoDrawerComponent } from './components/info-drawer/info-drawer.component'; import { InfoDrawerComponent } from './components/info-drawer/info-drawer.component';
import { EditFolderDirective } from './directives/edit-folder.directive'; import { EditFolderDirective } from './directives/edit-folder.directive';
@@ -128,6 +130,7 @@ import { ContentApiService } from './services/content-api.service';
SearchComponent, SearchComponent,
SettingsComponent, SettingsComponent,
InfoDrawerComponent, InfoDrawerComponent,
SharedLinkViewComponent,
EditFolderDirective, EditFolderDirective,
CreateFolderDirective, CreateFolderDirective,
DownloadNodesDirective, DownloadNodesDirective,
@@ -151,6 +154,7 @@ import { ContentApiService } from './services/content-api.service';
NodeActionsService, NodeActionsService,
NodePermissionService, NodePermissionService,
ProfileResolver, ProfileResolver,
ExperimentalGuard,
ContentApiService ContentApiService
], ],
entryComponents: [ entryComponents: [

View File

@@ -25,6 +25,7 @@
import { Routes } from '@angular/router'; import { Routes } from '@angular/router';
import { AuthGuardEcm } from '@alfresco/adf-core'; import { AuthGuardEcm } from '@alfresco/adf-core';
import { SharedLinkViewComponent } from './components/shared-link-view/shared-link-view.component';
import { LayoutComponent } from './components/layout/layout.component'; import { LayoutComponent } from './components/layout/layout.component';
@@ -43,6 +44,7 @@ import { SearchComponent } from './components/search/search.component';
import { SettingsComponent } from './components/settings/settings.component'; import { SettingsComponent } from './components/settings/settings.component';
import { ProfileResolver } from './common/services/profile.resolver'; import { ProfileResolver } from './common/services/profile.resolver';
import { ExperimentalGuard } from './common/services/experimental-guard.service';
export const APP_ROUTES: Routes = [ export const APP_ROUTES: Routes = [
{ {
@@ -59,6 +61,15 @@ export const APP_ROUTES: Routes = [
title: 'Settings' title: 'Settings'
} }
}, },
{
path: 'preview/s/:id',
component: SharedLinkViewComponent,
canActivate: [ ExperimentalGuard ],
data: {
ifExperimentalKey: 'share',
title: 'APP.PREVIEW.TITLE',
}
},
{ {
path: '', path: '',
component: LayoutComponent, component: LayoutComponent,

View File

@@ -0,0 +1,20 @@
import { CanActivate, ActivatedRouteSnapshot } from '@angular/router';
import { Injectable } from '@angular/core';
import { AppConfigService } from '@alfresco/adf-core';
import { environment } from '../../../environments/environment';
@Injectable()
export class ExperimentalGuard implements CanActivate {
constructor(private config: AppConfigService) {}
canActivate(route: ActivatedRouteSnapshot) {
const key = `experimental.${route.data.ifExperimentalKey}`;
const value = this.config.get(key);
if (!environment.production) {
return value === true || value === 'true';
}
return false;
}
}

View File

@@ -44,6 +44,17 @@
<mat-icon>info_outline</mat-icon> <mat-icon>info_outline</mat-icon>
</button> </button>
<ng-container *ifExperimental="'share'">
<button mat-icon-button
color="primary"
title="{{ 'APP.ACTIONS.SHARE' | translate }}"
*ngIf="selection.file"
[baseShareUrl]="sharedPreviewUrl$ | async"
[adf-share]="selection.file">
<mat-icon>share</mat-icon>
</button>
</ng-container>
<button <button
color="primary" color="primary"
mat-icon-button mat-icon-button

View File

@@ -46,6 +46,17 @@
<mat-icon>info_outline</mat-icon> <mat-icon>info_outline</mat-icon>
</button> </button>
<ng-container *ifExperimental="'share'">
<button mat-icon-button
color="primary"
title="{{ 'APP.ACTIONS.SHARE' | translate }}"
*ngIf="selection.file"
[baseShareUrl]="sharedPreviewUrl$ | async"
[adf-share]="selection.file">
<mat-icon>share</mat-icon>
</button>
</ng-container>
<button <button
color="primary" color="primary"
mat-icon-button mat-icon-button

View File

@@ -31,9 +31,10 @@ import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { Subject, Subscription } from 'rxjs/Rx'; import { Subject, Subscription } from 'rxjs/Rx';
import { SnackbarErrorAction, ViewNodeAction, SetSelectedNodesAction } from '../store/actions'; import { SnackbarErrorAction, ViewNodeAction, SetSelectedNodesAction } from '../store/actions';
import { appSelection } from '../store/selectors/app.selectors'; import { appSelection, sharedUrl } from '../store/selectors/app.selectors';
import { AppStore } from '../store/states/app.state'; import { AppStore } from '../store/states/app.state';
import { SelectionState } from '../store/states/selection.state'; import { SelectionState } from '../store/states/selection.state';
import { Observable } from 'rxjs/Rx';
export abstract class PageComponent implements OnInit, OnDestroy { export abstract class PageComponent implements OnInit, OnDestroy {
@@ -47,6 +48,7 @@ export abstract class PageComponent implements OnInit, OnDestroy {
node: MinimalNodeEntryEntity; node: MinimalNodeEntryEntity;
selection: SelectionState; selection: SelectionState;
displayMode = DisplayMode.List; displayMode = DisplayMode.List;
sharedPreviewUrl$: Observable<string>;
protected subscriptions: Subscription[] = []; protected subscriptions: Subscription[] = [];
@@ -57,6 +59,8 @@ export abstract class PageComponent implements OnInit, OnDestroy {
constructor(protected store: Store<AppStore>) {} constructor(protected store: Store<AppStore>) {}
ngOnInit() { ngOnInit() {
this.sharedPreviewUrl$ = this.store.select(sharedUrl);
this.store this.store
.select(appSelection) .select(appSelection)
.pipe(takeUntil(this.onDestroy$)) .pipe(takeUntil(this.onDestroy$))

View File

@@ -46,6 +46,16 @@
<span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span> <span>{{ 'APP.ACTIONS.FAVORITE' | translate }}</span>
</button> </button>
<ng-container *ifExperimental="'share'">
<button mat-menu-item
color="primary"
[baseShareUrl]="sharedPreviewUrl$ | async"
[adf-share]="selectedEntities[0]">
<mat-icon>share</mat-icon>
<span>{{ 'APP.ACTIONS.SHARE' | translate }}</span>
</button>
</ng-container>
<button <button
mat-menu-item mat-menu-item
[acaCopyNode]="selectedEntities"> [acaCopyNode]="selectedEntities">

View File

@@ -30,6 +30,7 @@ import { UserPreferencesService, AppConfigPipe, NodeFavoriteDirective } from '@a
import { PreviewComponent } from './preview.component'; import { PreviewComponent } from './preview.component';
import { Observable } from 'rxjs/Rx'; import { Observable } from 'rxjs/Rx';
import { EffectsModule } from '@ngrx/effects'; import { EffectsModule } from '@ngrx/effects';
import { ExperimentalDirective } from '../../directives/experimental.directive';
import { NodeEffects } from '../../store/effects/node.effects'; import { NodeEffects } from '../../store/effects/node.effects';
import { AppTestingModule } from '../../testing/app-testing.module'; import { AppTestingModule } from '../../testing/app-testing.module';
import { ContentApiService } from '../../services/content-api.service'; import { ContentApiService } from '../../services/content-api.service';
@@ -52,7 +53,8 @@ describe('PreviewComponent', () => {
declarations: [ declarations: [
AppConfigPipe, AppConfigPipe,
PreviewComponent, PreviewComponent,
NodeFavoriteDirective NodeFavoriteDirective,
ExperimentalDirective
], ],
schemas: [ NO_ERRORS_SCHEMA ] schemas: [ NO_ERRORS_SCHEMA ]
}); });

View File

@@ -35,6 +35,16 @@
<mat-icon>info_outline</mat-icon> <mat-icon>info_outline</mat-icon>
</button> </button>
<ng-container *ifExperimental="'share'">
<button mat-icon-button
title="{{ 'APP.ACTIONS.SHARE' | translate }}"
*ngIf="selection.file"
[baseShareUrl]="sharedPreviewUrl$ | async"
[adf-share]="selection.file">
<mat-icon>share</mat-icon>
</button>
</ng-container>
<button <button
color="primary" color="primary"
mat-icon-button mat-icon-button

View File

@@ -80,5 +80,13 @@
Cardview Cardview
</mat-checkbox> </mat-checkbox>
</div> </div>
<div>
<mat-checkbox
[(ngModel)]="share"
(change)="onChangeShareFeature($event)">
Share
</mat-checkbox>
</div>
</mat-expansion-panel> </mat-expansion-panel>
</mat-accordion> </mat-accordion>

View File

@@ -51,6 +51,7 @@ export class SettingsComponent implements OnInit {
libraries: boolean; libraries: boolean;
comments: boolean; comments: boolean;
cardview: boolean; cardview: boolean;
share: boolean;
constructor( constructor(
private store: Store<AppStore>, private store: Store<AppStore>,
@@ -82,6 +83,9 @@ export class SettingsComponent implements OnInit {
const cardview = this.appConfig.get('experimental.cardview'); const cardview = this.appConfig.get('experimental.cardview');
this.cardview = (cardview === true || cardview === 'true'); this.cardview = (cardview === true || cardview === 'true');
const share = this.appConfig.get('experimental.share');
this.share = (share === true || share === 'true');
} }
apply(model: any, isValid: boolean) { apply(model: any, isValid: boolean) {
@@ -113,4 +117,8 @@ export class SettingsComponent implements OnInit {
onChangeCardviewFeature(event: MatCheckboxChange) { onChangeCardviewFeature(event: MatCheckboxChange) {
this.storage.setItem('experimental.cardview', event.checked.toString()); this.storage.setItem('experimental.cardview', event.checked.toString());
} }
onChangeShareFeature(event: MatCheckboxChange) {
this.storage.setItem('experimental.share', event.checked.toString());
}
} }

View File

@@ -0,0 +1,6 @@
<ng-container *ngIf="sharedLinkId">
<adf-viewer
[sharedLinkId]="sharedLinkId"
[allowGoBack]="false">
</adf-viewer>
</ng-container>

View File

@@ -0,0 +1,4 @@
.app-shared-link-view {
width: 100%;
height: 100%;
}

View File

@@ -0,0 +1,24 @@
import { Component, ViewEncapsulation, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-shared-link-view',
templateUrl: 'shared-link-view.component.html',
styleUrls: [ 'shared-link-view.component.scss' ],
encapsulation: ViewEncapsulation.None,
// tslint:disable-next-line:use-host-property-decorator
host: { 'class': 'app-shared-link-view' }
})
export class SharedLinkViewComponent implements OnInit {
sharedLinkId: string = null;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.params.subscribe(params => {
this.sharedLinkId = params.id;
});
}
}

View File

@@ -29,6 +29,7 @@ export const SET_APP_NAME = 'SET_APP_NAME';
export const SET_HEADER_COLOR = 'SET_HEADER_COLOR'; export const SET_HEADER_COLOR = 'SET_HEADER_COLOR';
export const SET_LOGO_PATH = 'SET_LOGO_PATH'; export const SET_LOGO_PATH = 'SET_LOGO_PATH';
export const SET_LANGUAGE_PICKER = 'SET_LANGUAGE_PICKER'; export const SET_LANGUAGE_PICKER = 'SET_LANGUAGE_PICKER';
export const SET_SHARED_URL = 'SET_SHARED_URL';
export class SetAppNameAction implements Action { export class SetAppNameAction implements Action {
readonly type = SET_APP_NAME; readonly type = SET_APP_NAME;
@@ -49,3 +50,8 @@ export class SetLanguagePickerAction implements Action {
readonly type = SET_LANGUAGE_PICKER; readonly type = SET_LANGUAGE_PICKER;
constructor(public payload: boolean) {} constructor(public payload: boolean) {}
} }
export class SetSharedUrlAction implements Action {
readonly type = SET_SHARED_URL;
constructor(public payload: string) {}
}

View File

@@ -39,7 +39,9 @@ import {
} from '../actions'; } from '../actions';
import { import {
SET_LANGUAGE_PICKER, SET_LANGUAGE_PICKER,
SetLanguagePickerAction SetLanguagePickerAction,
SET_SHARED_URL,
SetSharedUrlAction
} from '../actions/app.actions'; } from '../actions/app.actions';
export function appReducer( export function appReducer(
@@ -71,6 +73,11 @@ export function appReducer(
action action
)); ));
break; break;
case SET_SHARED_URL:
newState = updateSharedUrl(state, <SetSharedUrlAction>(
action
));
break;
default: default:
newState = Object.assign({}, state); newState = Object.assign({}, state);
} }
@@ -108,6 +115,15 @@ function updateLogoPath(state: AppState, action: SetLogoPathAction): AppState {
return newState; return newState;
} }
function updateSharedUrl(
state: AppState,
action: SetSharedUrlAction
): AppState {
const newState = Object.assign({}, state);
newState.sharedUrl = action.payload;
return newState;
}
function updateUser(state: AppState, action: SetUserAction): AppState { function updateUser(state: AppState, action: SetUserAction): AppState {
const newState = Object.assign({}, state); const newState = Object.assign({}, state);
const user = action.payload; const user = action.payload;

View File

@@ -33,3 +33,4 @@ export const selectLogoPath = createSelector(selectApp, state => state.logoPath)
export const appSelection = createSelector(selectApp, state => state.selection); export const appSelection = createSelector(selectApp, state => state.selection);
export const appLanguagePicker = createSelector(selectApp, state => state.languagePicker); export const appLanguagePicker = createSelector(selectApp, state => state.languagePicker);
export const selectUser = createSelector(selectApp, state => state.user); export const selectUser = createSelector(selectApp, state => state.user);
export const sharedUrl = createSelector(selectApp, state => state.sharedUrl);

View File

@@ -31,6 +31,7 @@ export interface AppState {
headerColor: string; headerColor: string;
logoPath: string; logoPath: string;
languagePicker: boolean; languagePicker: boolean;
sharedUrl: string;
selection: SelectionState; selection: SelectionState;
user: ProfileState; user: ProfileState;
} }
@@ -40,6 +41,7 @@ export const INITIAL_APP_STATE: AppState = {
headerColor: '#2196F3', headerColor: '#2196F3',
logoPath: 'assets/images/alfresco-logo-white.svg', logoPath: 'assets/images/alfresco-logo-white.svg',
languagePicker: false, languagePicker: false,
sharedUrl: '',
user: { user: {
isAdmin: true, // 5.2.x isAdmin: true, // 5.2.x
id: null, id: null,

View File

@@ -124,7 +124,8 @@
"UNSHARE": "Unshare", "UNSHARE": "Unshare",
"DETAILS": "View details", "DETAILS": "View details",
"VERSIONS": "Manage Versions", "VERSIONS": "Manage Versions",
"TOGGLE-SIDENAV": "Toggle side navigation bar" "TOGGLE-SIDENAV": "Toggle side navigation bar",
"SHARE": "Share"
}, },
"DIALOGS": { "DIALOGS": {
"CONFIRM_PURGE": { "CONFIRM_PURGE": {