From 01c2fc18f2ecbda3c5c9a8b55288a1e472f7caa9 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Thu, 4 Mar 2021 11:26:39 -0600 Subject: [PATCH] #4 - add support for sharing and viewing a page publicly without login --- src/app/app-routing.module.ts | 3 +- .../editor/files/files.component.html | 3 ++ .../editor/files/files.component.scss | 12 +++--- .../editor/files/files.component.ts | 2 + .../nodes/file-box/file-box.component.html | 8 ++-- .../nodes/markdown/markdown.component.scss | 2 +- .../sharing/selector/selector.component.html | 10 +++-- .../sharing/selector/selector.component.ts | 21 ++++++--- src/app/home/home.page.ts | 6 ++- src/app/service/api.service.ts | 30 +++++++++++++ src/app/service/guard/Editor.guard.ts | 43 +++++++++++++++++++ src/environments/environment.prod.ts | 1 + src/environments/environment.ts | 1 + 13 files changed, 118 insertions(+), 24 deletions(-) create mode 100644 src/app/service/guard/Editor.guard.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 0cc5b2b..e26a037 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -3,6 +3,7 @@ import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; import {LoginPage} from './pages/login/login.page'; import {AuthService} from './service/auth.service'; import {GuestOnlyGuard} from './service/guard/GuestOnly.guard'; +import {EditorGuard} from './service/guard/Editor.guard'; const routes: Routes = [ { @@ -17,7 +18,7 @@ const routes: Routes = [ }, { path: 'editor', - canActivate: [AuthService], + canActivate: [EditorGuard], loadChildren: () => import('./components/components.module').then( m => m.ComponentsModule) }, { diff --git a/src/app/components/editor/files/files.component.html b/src/app/components/editor/files/files.component.html index 4cdf41c..820c1fc 100644 --- a/src/app/components/editor/files/files.component.html +++ b/src/app/components/editor/files/files.component.html @@ -17,6 +17,9 @@ + +
There are no files in this group yet.
+
diff --git a/src/app/components/editor/files/files.component.scss b/src/app/components/editor/files/files.component.scss index 72344ed..472a053 100644 --- a/src/app/components/editor/files/files.component.scss +++ b/src/app/components/editor/files/files.component.scss @@ -2,11 +2,11 @@ div.files-wrapper { border: 2px solid #8c8c8c; border-radius: 3px; margin-top: 15px; +} - &.not-available { - height: 150px; - text-align: center; - padding-top: 40px; - color: #494949; - } +.not-available { + height: 150px; + text-align: center; + padding-top: 40px; + color: #494949; } diff --git a/src/app/components/editor/files/files.component.ts b/src/app/components/editor/files/files.component.ts index e8bd448..ab85d18 100644 --- a/src/app/components/editor/files/files.component.ts +++ b/src/app/components/editor/files/files.component.ts @@ -50,6 +50,8 @@ export class FilesComponent extends EditorNodeContract implements OnInit { this.node.Value = {}; } + console.log('files load', this) + if ( !this.node.Value.Value && !this.readonly ) { this.dbRecord = await this.api.createFileGroup(this.page.UUID, this.node.UUID); this.fileRecords = this.dbRecord.files; diff --git a/src/app/components/nodes/file-box/file-box.component.html b/src/app/components/nodes/file-box/file-box.component.html index 1e16d12..2407ddc 100644 --- a/src/app/components/nodes/file-box/file-box.component.html +++ b/src/app/components/nodes/file-box/file-box.component.html @@ -22,9 +22,9 @@ (click)="dismiss()" > - -   Folder -   Upload Files + +   Folder +   Upload Files   Open
@@ -38,7 +38,7 @@ > - +
diff --git a/src/app/components/nodes/markdown/markdown.component.scss b/src/app/components/nodes/markdown/markdown.component.scss index 5be88bd..ced4bd5 100644 --- a/src/app/components/nodes/markdown/markdown.component.scss +++ b/src/app/components/nodes/markdown/markdown.component.scss @@ -1,6 +1,6 @@ .container { display: flex; - min-height: 600px; + min-height: 200px; flex-direction: row; .editor-container, .display { diff --git a/src/app/components/sharing/selector/selector.component.html b/src/app/components/sharing/selector/selector.component.html index 5bb3bfc..f22a258 100644 --- a/src/app/components/sharing/selector/selector.component.html +++ b/src/app/components/sharing/selector/selector.component.html @@ -22,9 +22,13 @@   Edit - +   Manage + + +   Public link? + @@ -49,7 +53,7 @@ - + @@ -66,7 +70,7 @@ - + diff --git a/src/app/components/sharing/selector/selector.component.ts b/src/app/components/sharing/selector/selector.component.ts index 4cda107..ed05974 100644 --- a/src/app/components/sharing/selector/selector.component.ts +++ b/src/app/components/sharing/selector/selector.component.ts @@ -2,6 +2,7 @@ import {Component, Input, OnInit} from '@angular/core'; import {ModalController} from '@ionic/angular'; import {ApiService} from '../../../service/api.service'; import {Observable} from 'rxjs'; +import {environment} from '../../../../environments/environment'; @Component({ selector: 'app-selector', @@ -11,11 +12,12 @@ import {Observable} from 'rxjs'; export class SelectorComponent implements OnInit { @Input() node: any; public sharingInfo: { - view: Array<{username: string, id: string, level: 'view'|'update'|'manage'}>, - update: Array<{username: string, id: string, level: 'view'|'update'|'manage'}>, - manage: Array<{username: string, id: string, level: 'view'|'update'|'manage'}>, + view: Array<{username: string, public?: boolean, id: string, level: 'view'|'update'|'manage'}>, + update: Array<{username: string, public?: boolean, id: string, level: 'view'|'update'|'manage'}>, + manage: Array<{username: string, public?: boolean, id: string, level: 'view'|'update'|'manage'}>, } = {view: [], update: [], manage: []}; public generatedLink = ''; + public publicLink = false; constructor( protected modals: ModalController, @@ -54,7 +56,7 @@ export class SelectorComponent implements OnInit { } setShareLevel(group, level) { - this.api.post(`/share/page/${this.node.id}/share`, { user_id: group.id, level }).subscribe(result => { + this.api.post(`/share/page/${this.node.id}/share?public=${!!group.public}`, { user_id: group.id, level }).subscribe(result => { this.loadShareInfo().subscribe(data => { this.sharingInfo = data; }); @@ -62,7 +64,7 @@ export class SelectorComponent implements OnInit { } unsharePage(group) { - this.api.post(`/share/page/${this.node.id}/revoke`, { user_id: group.id }).subscribe(result => { + this.api.post(`/share/page/${this.node.id}/revoke?public=${!!group.public}`, { user_id: group.id }).subscribe(result => { this.loadShareInfo().subscribe(data => { this.sharingInfo = data; }); @@ -70,8 +72,13 @@ export class SelectorComponent implements OnInit { } getShareLink(level) { - this.api.get(`/share/page/${this.node.id}/link/${level}`).subscribe(result => { - this.generatedLink = result.data.link; + this.api.get(`/share/page/${this.node.id}/link/${level}?public=${this.publicLink}`).subscribe(result => { + if ( this.publicLink ) { + this.generatedLink = `${window.location.origin}${environment.appBase}editor;id=${this.node.id}`; + this.ngOnInit(); + } else { + this.generatedLink = result.data.link; + } }); } } diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts index ce564e3..172f86c 100644 --- a/src/app/home/home.page.ts +++ b/src/app/home/home.page.ts @@ -1,6 +1,7 @@ import {Component, OnInit} from '@angular/core'; import {ApiService} from '../service/api.service'; import {isDebug, debug} from '../utility'; +import {Router} from '@angular/router'; @Component({ selector: 'app-home', @@ -10,6 +11,7 @@ import {isDebug, debug} from '../utility'; export class HomePage implements OnInit { constructor( public readonly api: ApiService, + public readonly router: Router, ) {} ngOnInit() { @@ -17,10 +19,10 @@ export class HomePage implements OnInit { if ( isDebug() ) { debug('Forcing authentication...'); setTimeout(() => { - this.api.forceRestart(); + this.router.navigate(['/login']); }, 2000); } else { - this.api.forceRestart(); + this.router.navigate(['/login']); } } } diff --git a/src/app/service/api.service.ts b/src/app/service/api.service.ts index cfae368..e2b1243 100644 --- a/src/app/service/api.service.ts +++ b/src/app/service/api.service.ts @@ -1463,6 +1463,36 @@ export class ApiService { }); } + public checkPermission(permission: string): Promise { + return new Promise(async (res, rej) => { + if ( this.isOffline ) { + return rej(new ResourceNotAvailableOfflineError()); + } + + this.post('/share/check', { permission }).subscribe({ + next: result => { + return res(result.data.check); + }, + error: rej, + }); + }); + } + + public checkPagePermission(PageId: string, level: string = 'view'): Promise { + return new Promise(async (res, rej) => { + if ( this.isOffline ) { + return rej(new ResourceNotAvailableOfflineError()); + } + + this.post(`/share/check-page/${PageId}/${level}`, {}).subscribe({ + next: result => { + return res(result.data.check); + }, + error: rej, + }); + }); + } + public attemptLogin(uid: string, password: string): Promise<{ success: boolean, message?: string }> { return new Promise(async (res, rej) => { if ( this.isOffline ) { diff --git a/src/app/service/guard/Editor.guard.ts b/src/app/service/guard/Editor.guard.ts new file mode 100644 index 0000000..47f699c --- /dev/null +++ b/src/app/service/guard/Editor.guard.ts @@ -0,0 +1,43 @@ +import {ActivatedRoute, ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router'; +import {ApiService} from '../api.service'; +import {NavigationService} from '../navigation.service'; +import {Injectable} from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class EditorGuard implements CanActivate { + constructor( + protected readonly api: ApiService, + protected readonly router: Router, + protected readonly nav: NavigationService, + ) { } + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { + const checkCanActivate = async () => { + const PageId = route.paramMap.get('id'); + + const canView = await this.api.checkPagePermission(String(PageId)); + + if ( !canView ) { + await this.router.navigate(['/login']); + return false; + } else { + return true; + } + }; + + return new Promise(async res => { + if ( !this.nav.initialized$.getValue() ) { + const sub = this.nav.initialized$.subscribe(async initialized => { + if ( initialized ) { + sub.unsubscribe(); + return res(await checkCanActivate()); + } + }); + } else { + return res(await checkCanActivate()); + } + }); + } +} diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index d2e6f80..9d99ada 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -6,5 +6,6 @@ export const environment = { versionUrl: '/i/version.html?ngsw-bypass', logoUrl: '/i/assets/icon/logo_lines.svg', starshipUrl: '/auth/starship_oauth/login', + appBase: '/i/', outputDebug: false, }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 810ec42..b62e0b6 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -10,6 +10,7 @@ export const environment = { versionUrl: '/link_api/assets/version.html?ngsw-bypass', logoUrl: '/assets/icon/logo_lines.svg', starshipUrl: '/link_api/auth/starship_oauth/login', + appBase: '/', outputDebug: true, };