diff --git a/angular.json b/angular.json index 6b7e8338..f8c2a6ed 100644 --- a/angular.json +++ b/angular.json @@ -18,7 +18,6 @@ "css-tree", "save-svg-as-png", "angular-draggable-droppable", - "angular2-hotkeys", "dom-set", "dom-plane", "mousetrap", @@ -26,13 +25,9 @@ "rxjs/Rx", "rxjs/add/operator/map", "rxjs-compat/add/operator/map", - "angular-react-core.js", - "react", - "react-dom", "classnames", "stylenames" ], - "aot": true, "outputPath": "dist", "index": "src/index.html", "main": "src/main.ts", @@ -49,7 +44,13 @@ "src/styles.scss", "src/theme.scss" ], - "scripts": [] + "scripts": [], + "vendorChunk": true, + "extractLicenses": false, + "buildOptimizer": false, + "sourceMap": true, + "optimization": false, + "namedChunks": true }, "configurations": { "production": { @@ -67,7 +68,6 @@ "styles": false }, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, @@ -89,7 +89,6 @@ "outputHashing": "all", "sourceMap": false, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, @@ -125,7 +124,6 @@ "outputHashing": "all", "sourceMap": false, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, diff --git a/package.json b/package.json index 4a43aea6..f8af4902 100644 --- a/package.json +++ b/package.json @@ -41,39 +41,32 @@ }, "private": true, "dependencies": { - "@angular-react/core": "^3.0.0", - "@angular-react/fabric": "^3.0.0", - "@angular/animations": "^11.2.13", - "@angular/cdk": "^11.2.12", - "@angular/common": "^11.2.13", - "@angular/compiler": "^11.2.13", - "@angular/core": "^11.2.13", - "@angular/forms": "^11.2.13", - "@angular/http": "^7.2.16", - "@angular/material": "^11.2.12", - "@angular/platform-browser": "^11.2.13", - "@angular/platform-browser-dynamic": "^11.2.13", - "@angular/router": "^11.2.13", + "@angular/animations": "^12.0.2", + "@angular/cdk": "^12.0.2", + "@angular/common": "^12.0.2", + "@angular/compiler": "^12.0.2", + "@angular/core": "^12.0.2", + "@angular/forms": "^12.0.2", + "@angular/material": "^12.0.2", + "@angular/platform-browser": "^12.0.2", + "@angular/platform-browser-dynamic": "^12.0.2", + "@angular/router": "^12.0.2", "@sentry/browser": "^6.3.6", "@types/jest": "^26.0.23", "@types/mocha": "^8.2.2", "@types/react": "^17.0.5", "@types/react-dom": "^17.0.3", "angular-draggable-droppable": "^4.6.0", - "angular-persistence": "^1.0.1", "angular-resizable-element": "^3.3.5", - "angular2-draggable": "^2.3.2", - "angular2-hotkeys": "^2.2.0", - "angular2-indexeddb": "^1.2.3", "bootstrap": "^5.0.0", "command-exists": "^1.2.9", "core-js": "^3.12.1", "d3-ng2-service": "^2.2.0", "eev": "^0.1.5", - "file-saver": "^2.0.5", "ini": "^2.0.0", "marked": "^2.0.3", "material-design-icons": "^3.0.1", + "mousetrap": "^1.6.5", "ng-circle-progress": "^1.6.0", "ng2-file-upload": "^1.4.0", "ngx-childprocess": "^0.0.6", @@ -81,16 +74,10 @@ "ngx-electron": "^2.2.0", "node-fetch": "^2.6.1", "notosans-fontface": "1.2.2", - "office-ui-fabric-react": "^7.170.0", "prettier-plugin-organize-imports": "^2.0.0", - "react": "^17.0.2", - "react-bootstrap": "^1.5.2", - "react-dom": "^17.0.2", - "rxjs": "^6.6.7", - "rxjs-compat": "^6.6.7", - "save-html-as-image": "^1.5.2", + "rxjs": "^6.5.3", + "rxjs-compat": "^6.5.3", "save-svg-as-png": "^1.4.17", - "schematics-scss-migrate": "^1.3.13", "snyk": "^1.589.0", "spark-md5": "^3.0.1", "svg-crowbar": "^0.6.5", @@ -101,21 +88,21 @@ "xterm-addon-attach": "^0.6.0", "xterm-addon-fit": "^0.5.0", "yargs": "^17.0.1", - "zone.js": "^0.11.4" + "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "^0.1102.12", - "@angular/cli": "^11.2.12", - "@angular/compiler-cli": "^11.2.13", - "@angular/language-service": "^11.2.13", + "@angular-devkit/build-angular": "^12.0.2", + "@angular/cli": "^12.0.2", + "@angular/compiler-cli": "^12.0.2", + "@angular/language-service": "^12.0.2", "@sentry/cli": "^1.64.2", "@sentry/electron": "^2.4.1", "@types/jasmine": "^3.7.1", "@types/jasminewd2": "^2.0.9", - "@types/node": "15.0.2", + "@types/node": "15.6.1", "codelyzer": "^6.0.2", - "electron": "^12.0.7", - "electron-builder": "22.11.1", + "electron": "^13.0.1", + "electron-builder": "22.10.5", "file-loader": "^6.2.0", "jasmine-core": "~3.7.1", "jasmine-spec-reporter": "~7.0.0", @@ -133,11 +120,11 @@ "replace": "^1.2.1", "rxjs-tslint": "^0.1.8", "ts-mockito": "^2.6.1", - "ts-node": "~9.1.1", + "ts-node": "~10.0.0", "tslint": "^6.1.3", "tslint-config-prettier": "^1.18.0", - "typescript": "4.0.2", - "webpack": "5.36.2", + "typescript": "4.2.4", + "webpack": "5.38.0", "yarn-upgrade-all": "^0.5.4" }, "greenkeeper": { @@ -146,4 +133,4 @@ ] }, "snyk": true -} +} \ No newline at end of file diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index a4292aee..6a84c41b 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -3,7 +3,6 @@ import { RouterModule, Routes } from '@angular/router'; import { BundledServerFinderComponent } from './components/bundled-server-finder/bundled-server-finder.component'; import { DirectLinkComponent } from './components/direct-link/direct-link.component'; import { HelpComponent } from './components/help/help.component'; -import { ReportIssueComponent } from './components/help/report-issue/report-issue.component'; import { InstalledSoftwareComponent } from './components/installed-software/installed-software.component'; import { LoginComponent } from './components/login/login.component'; import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component'; @@ -57,6 +56,7 @@ import { LoginGuard } from './guards/login-guard'; import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.component'; import { ServerResolve } from './resolvers/server-resolve'; import { UserManagementComponent } from './components/user-management/user-management.component'; +import { LoggedUserComponent } from './components/users/logged-user/logged-user.component'; const routes: Routes = [ { @@ -67,6 +67,7 @@ const routes: Routes = [ { path: 'servers', component: ServersComponent }, { path: 'bundled', component: BundledServerFinderComponent }, { path: 'server/:server_id/login', component: LoginComponent }, + { path: 'server/:server_id/loggeduser', component: LoggedUserComponent }, { path: 'server/:server_id/projects', component: ProjectsComponent, @@ -74,7 +75,6 @@ const routes: Routes = [ resolve: { server: ServerResolve }, }, { path: 'help', component: HelpComponent }, - { path: 'help/reportissue', component: ReportIssueComponent }, { path: 'settings', component: SettingsComponent }, { path: 'settings/console', component: ConsoleComponent }, { path: 'installed-software', component: InstalledSoftwareComponent }, diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 388f4ac6..0de4b0c6 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -2,7 +2,6 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIconModule } from '@angular/material/icon'; import { RouterTestingModule } from '@angular/router/testing'; -import { PersistenceService } from 'angular-persistence'; import { ElectronService, NgxElectronModule } from 'ngx-electron'; import { AppComponent } from './app.component'; import { ProgressService } from './common/progress/progress.service'; @@ -21,12 +20,12 @@ describe('AppComponent', () => { TestBed.configureTestingModule({ declarations: [AppComponent], imports: [RouterTestingModule, MatIconModule, NgxElectronModule], - providers: [SettingsService, PersistenceService, ProgressService], + providers: [SettingsService, ProgressService], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); - electronService = TestBed.get(ElectronService); - settingsService = TestBed.get(SettingsService); + electronService = TestBed.inject(ElectronService); + settingsService = TestBed.inject(SettingsService); })); beforeEach(() => { @@ -46,23 +45,18 @@ describe('AppComponent', () => { })); it('should receive changed settings and forward to electron', async(() => { - const spy = createSpyObj('Electron.IpcRenderer', ['send']); spyOnProperty(electronService, 'isElectronApp').and.returnValue(true); - spyOnProperty(electronService, 'ipcRenderer').and.returnValue(spy); - settingsService.set('crash_reports', true); + settingsService.setReportsSettings(true); component.ngOnInit(); - settingsService.set('crash_reports', false); - expect(spy.send).toHaveBeenCalled(); - expect(spy.send.calls.mostRecent().args[0]).toEqual('settings.changed'); - expect(spy.send.calls.mostRecent().args[1].crash_reports).toEqual(false); + settingsService.setReportsSettings(false); })); it('should receive changed settings and do not forward to electron', async(() => { const spy = createSpyObj('Electron.IpcRenderer', ['send']); spyOnProperty(electronService, 'isElectronApp').and.returnValue(false); - settingsService.set('crash_reports', true); + settingsService.setReportsSettings(true); component.ngOnInit(); - settingsService.set('crash_reports', false); + settingsService.setReportsSettings(false); expect(spy.send).not.toHaveBeenCalled(); })); }); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e9e5233a..7a45db45 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -37,12 +37,6 @@ export class AppComponent implements OnInit { @HostBinding('class') componentCssClass; ngOnInit(): void { - if (this.electronService.isElectronApp) { - this.settingsService.subscribe((settings) => { - this.electronService.ipcRenderer.send('settings.changed', settings); - }); - } - this.applyTheme(this.themeService.savedTheme + '-theme'); this.themeService.themeChanged.subscribe((event: string) => { this.applyTheme(event); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 293598e6..febb72fd 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,4 +1,3 @@ -import { AngularReactBrowserModule } from '@angular-react/core'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { OverlayModule } from '@angular/cdk/overlay'; import { CdkTableModule } from '@angular/cdk/table'; @@ -9,7 +8,6 @@ import { MatSidenavModule } from '@angular/material/sidenav'; import { BrowserModule, Title } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { DragAndDropModule } from 'angular-draggable-droppable'; -import { PersistenceModule } from 'angular-persistence'; import { ResizableModule } from 'angular-resizable-element'; import { D3Service } from 'd3-ng2-service'; import { NgCircleProgressModule } from 'ng-circle-progress'; @@ -44,7 +42,6 @@ import { NodeLabelDraggedComponent } from './components/drawings-listeners/node- import { TextAddedComponent } from './components/drawings-listeners/text-added/text-added.component'; import { TextEditedComponent } from './components/drawings-listeners/text-edited/text-edited.component'; import { HelpComponent } from './components/help/help.component'; -import { ReportIssueComponent } from './components/help/report-issue/report-issue.component'; import { InstallSoftwareComponent } from './components/installed-software/install-software/install-software.component'; import { InstalledSoftwareComponent } from './components/installed-software/installed-software.component'; import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component'; @@ -204,6 +201,7 @@ import { TemplateComponent } from './components/template/template.component'; import { TopologySummaryComponent } from './components/topology-summary/topology-summary.component'; import { WebConsoleFullWindowComponent } from './components/web-console-full-window/web-console-full-window.component'; import { DataSourceFilter } from './filters/dataSourceFilter'; +import { AuthImageFilter } from './filters/authImageFilter'; import { DateFilter } from './filters/dateFilter.pipe'; import { NameFilter } from './filters/nameFilter.pipe'; import { ProjectsFilter } from './filters/projectsFilter.pipe'; @@ -226,7 +224,6 @@ import { ExternalSoftwareDefinitionService } from './services/external-software- import { Gns3vmService } from './services/gns3vm.service'; import { GoogleAnalyticsService } from './services/google-analytics.service'; import { HttpServer, ServerErrorHandler } from './services/http-server.service'; -import { IndexedDbService } from './services/indexed-db.service'; import { InfoService } from './services/info.service'; import { InstalledSoftwareService } from './services/installed-software.service'; import { IosConfigurationService } from './services/ios-configuration.service'; @@ -274,10 +271,13 @@ import { LoginComponent } from './components/login/login.component'; import { LoginService } from './services/login.service'; import { HttpRequestsInterceptor } from './interceptors/http.interceptor'; import { UserManagementComponent } from './components/user-management/user-management.component' +import { UserService } from './services/user.service'; +import { LoggedUserComponent } from './components/users/logged-user/logged-user.component'; @NgModule({ declarations: [ AppComponent, + LoggedUserComponent, ProjectMapComponent, LoginComponent, ServersComponent, @@ -382,6 +382,7 @@ import { UserManagementComponent } from './components/user-management/user-manag DataSourceFilter, TemplateFilter, ProjectsFilter, + AuthImageFilter, ListOfSnapshotsComponent, CustomAdaptersComponent, NodesMenuComponent, @@ -458,11 +459,10 @@ import { UserManagementComponent } from './components/user-management/user-manag TemplateNameDialogComponent, ConfigureCustomAdaptersDialogComponent, EditNetworkConfigurationDialogComponent, - ReportIssueComponent, - UserManagementComponent + UserManagementComponent, + ProjectReadmeComponent ], imports: [ - AngularReactBrowserModule, BrowserModule, HttpClientModule, AppRoutingModule, @@ -471,7 +471,6 @@ import { UserManagementComponent } from './components/user-management/user-manag BrowserAnimationsModule, CdkTableModule, CartographyModule, - PersistenceModule, NgxElectronModule, FileUploadModule, MatSidenavModule, @@ -496,7 +495,6 @@ import { UserManagementComponent } from './components/user-management/user-manag NodeService, LinkService, DrawingService, - IndexedDbService, HttpServer, SnapshotService, ProgressDialogService, @@ -558,7 +556,8 @@ import { UserManagementComponent } from './components/user-management/user-manag Title, ApplianceService, UpdatesService, - LoginService + LoginService, + UserService ], entryComponents: [ AddServerDialogComponent, diff --git a/src/app/cartography/components/experimental-map/drawing/drawing.component.ts b/src/app/cartography/components/experimental-map/drawing/drawing.component.ts index f8bc603f..724f61e5 100644 --- a/src/app/cartography/components/experimental-map/drawing/drawing.component.ts +++ b/src/app/cartography/components/experimental-map/drawing/drawing.component.ts @@ -26,9 +26,7 @@ export class DrawingComponent implements OnInit { ngOnInit() { try { this.drawing.element = this.svgToDrawingConverter.convert(this.drawing.svg); - } catch (error) { - console.log(`Cannot convert due to Error: '${error}'`); - } + } catch (error) {} } OnDragging(evt) { diff --git a/src/app/cartography/widgets/drawings.backup.ts b/src/app/cartography/widgets/drawings.backup.ts index 239768ac..0a255a71 100644 --- a/src/app/cartography/widgets/drawings.backup.ts +++ b/src/app/cartography/widgets/drawings.backup.ts @@ -44,9 +44,7 @@ export class DrawingsWidget implements Widget { layer.drawings.forEach((d: MapDrawing) => { try { d.element = this.svgToDrawingConverter.convert(d.svg); - } catch (error) { - console.log(`Cannot convert due to Error: '${error}'`); - } + } catch (error) {} }); return layer.drawings; }, @@ -81,9 +79,7 @@ export class DrawingsWidget implements Widget { .on('start', (datum: MapDrawing) => { document.body.style.cursor = 'ns-resize'; topEdge = datum.y; - console.log('started'); y = event.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y; - // startEvent = event; }) .on('drag', (datum: MapDrawing) => { const evt = event; @@ -91,55 +87,10 @@ export class DrawingsWidget implements Widget { y = event.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y; let height = datum.element.height - dy; if (height < 0) { - // height = datum.y - startEvent.y; datum.y += height; height = topEdge - datum.y; - // console.log(topEdge - datum.y); } - console.log('Height', height); datum.element.height = height; - - // datum.element.height -= dy; - // if(datum.element.height < 0) { - // datum.y -= datum.element.height; - // datum.element.height = Math.abs(datum.element.height); - // } - - // if (!isReflectedVertical) { - // if ((datum.element.height + evt.dy) < 0) { - // isReflectedVertical = true; - // y = topEdge; - // console.log(y); - // datum.element.height = Math.abs(datum.element.height + evt.dy); - // console.log(datum.element.height); - // } else { - // datum.element.height += evt.dy; - - // if (datum.element instanceof EllipseElement){ - // (datum.element as EllipseElement).cy = (datum.element as EllipseElement).cy + evt.dy/2 < 0 ? 1 : (datum.element as EllipseElement).cy += evt.dy/2; - // (datum.element as EllipseElement).ry = (datum.element as EllipseElement).ry + evt.dy/2 < 0 ? 1 : (datum.element as EllipseElement).ry += evt.dy/2; - // } - // } - // } else { - // dy = y - (evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y); - // y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y; - - // if ((datum.element.height + dy) < 0){ - // isReflectedVertical = false; - // y = topEdge; - // console.log(y); - // datum.element.height = Math.abs(datum.element.height + evt.dy); - // console.log(datum.element.height); - // } else { - // datum.y = evt.sourceEvent.clientY - this.context.getZeroZeroTransformationPoint().y; - // datum.element.height += dy; - // if (datum.element instanceof EllipseElement) { - // (datum.element as EllipseElement).cy = (datum.element as EllipseElement).cy + dy/2 < 0 ? 1 : (datum.element as EllipseElement).cy += dy/2; - // (datum.element as EllipseElement).ry = (datum.element as EllipseElement).ry + dy/2 < 0 ? 1 : (datum.element as EllipseElement).ry += dy/2; - // } - // } - // } - this.redrawDrawing(view, datum); }) .on('end', (datum: MapDrawing) => { diff --git a/src/app/cartography/widgets/drawings.ts b/src/app/cartography/widgets/drawings.ts index a4f9a174..2c2e0528 100644 --- a/src/app/cartography/widgets/drawings.ts +++ b/src/app/cartography/widgets/drawings.ts @@ -46,9 +46,7 @@ export class DrawingsWidget implements Widget { layer.drawings.forEach((d: MapDrawing) => { try { d.element = this.svgToDrawingConverter.convert(d.svg); - } catch (error) { - console.log(`Cannot convert due to Error: '${error}'`); - } + } catch (error) {} }); return layer.drawings; }, diff --git a/src/app/cartography/widgets/node.ts b/src/app/cartography/widgets/node.ts index fc217f85..c97957e2 100644 --- a/src/app/cartography/widgets/node.ts +++ b/src/app/cartography/widgets/node.ts @@ -1,4 +1,5 @@ import { EventEmitter, Injectable } from '@angular/core'; +import { SymbolService } from '../../services/symbol.service'; import { event, select } from 'd3-selection'; import { MapSettingsService } from '../../services/mapsettings.service'; import { ClickedDataEvent } from '../events/event-source'; @@ -22,7 +23,8 @@ export class NodeWidget implements Widget { private selectionManager: SelectionManager, private labelWidget: LabelWidget, private nodesEventSource: NodesEventSource, - private mapSettingsService: MapSettingsService + private mapSettingsService: MapSettingsService, + private symbolService: SymbolService ) {} public draw(view: SVGSelection) { @@ -88,7 +90,13 @@ export class NodeWidget implements Widget { event.preventDefault(); self.onContextConsoleMenu.emit(new NodeContextMenu(event, n)); }) - .attr('xnode:href', (n: MapNode) => n.symbolUrl) + .attr('xnode:href', (n: MapNode) => { + let symbol = this.symbolService.get(n.symbol.split('/')[2]); + if (symbol) { + return 'data:image/svg+xml;base64,' + btoa(symbol.raw); + } + return ''; + }) .attr('width', (n: MapNode) => { if (!n.width) return 60; return n.width; diff --git a/src/app/common/error-handlers/toaster-error-handler.spec.ts b/src/app/common/error-handlers/toaster-error-handler.spec.ts index 199b114f..aaaf02e7 100644 --- a/src/app/common/error-handlers/toaster-error-handler.spec.ts +++ b/src/app/common/error-handlers/toaster-error-handler.spec.ts @@ -1,7 +1,6 @@ import { Injector } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { SettingsService } from '../../services/settings.service'; -import { MockedSettingsService } from '../../services/settings.service.spec'; import { ToasterService } from '../../services/toaster.service'; import { MockedToasterService } from '../../services/toaster.service.spec'; import { SentryErrorHandler } from './sentry-error-handler'; @@ -17,19 +16,21 @@ class MockedToasterErrorHandler extends ToasterErrorHandler { describe('ToasterErrorHandler', () => { let handler: ToasterErrorHandler; let toasterService: MockedToasterService; + let settingsService: SettingsService; beforeEach(() => { TestBed.configureTestingModule({ providers: [ { provide: ToasterService, useClass: MockedToasterService }, - { provide: SettingsService, useClass: MockedSettingsService }, + { provide: SettingsService}, SentryErrorHandler, ToasterErrorHandler, ], }); - handler = new MockedToasterErrorHandler(TestBed.get(Injector)); + handler = new MockedToasterErrorHandler(TestBed.inject(Injector)); toasterService = TestBed.get(ToasterService); + settingsService = TestBed.inject(SettingsService); }); it('should call toaster with error message', () => { diff --git a/src/app/components/bundled-server-finder/bundled-server-finder.component.ts b/src/app/components/bundled-server-finder/bundled-server-finder.component.ts index 9fea4c8e..39b9904c 100644 --- a/src/app/components/bundled-server-finder/bundled-server-finder.component.ts +++ b/src/app/components/bundled-server-finder/bundled-server-finder.component.ts @@ -21,7 +21,15 @@ export class BundledServerFinderComponent implements OnInit { ngOnInit() { this.progressService.activate(); setTimeout(() => { - let port = parseInt(this.document.location.port, 10) ? parseInt(this.document.location.port, 10) : 80; + let port; + + if (parseInt(this.document.location.port, 10)) { + port = parseInt(this.document.location.port, 10); + } else if (this.document.location.protocol == "https:") { + port = 443; + } else { + port = 80; + } this.serverService.getLocalServer(this.document.location.hostname, port).then((server: Server) => { this.progressService.deactivate(); diff --git a/src/app/components/direct-link/direct-link.component.ts b/src/app/components/direct-link/direct-link.component.ts index eb8d7b9b..d8f6a3b9 100644 --- a/src/app/components/direct-link/direct-link.component.ts +++ b/src/app/components/direct-link/direct-link.component.ts @@ -59,7 +59,6 @@ export class DirectLinkComponent implements OnInit { const servers = await this.serverService.findAll(); const server = servers.filter((server) => server.host === this.serverIp && server.port === this.serverPort)[0]; - console.log(servers); if (server) { this.router.navigate(['/server', server.id, 'project', this.projectId]); } else { diff --git a/src/app/components/help/help.component.html b/src/app/components/help/help.component.html index 124fe11e..d6f11ce5 100644 --- a/src/app/components/help/help.component.html +++ b/src/app/components/help/help.component.html @@ -31,9 +31,6 @@ - - + diff --git a/src/app/components/help/help.component.scss b/src/app/components/help/help.component.scss index d6a023a9..6e0e106f 100644 --- a/src/app/components/help/help.component.scss +++ b/src/app/components/help/help.component.scss @@ -1,4 +1,4 @@ .full-width { - width: 50%; + width: 100%; margin-top: 20px; } diff --git a/src/app/components/help/help.component.ts b/src/app/components/help/help.component.ts index 032c5793..57d9d1b2 100644 --- a/src/app/components/help/help.component.ts +++ b/src/app/components/help/help.component.ts @@ -28,4 +28,8 @@ export class HelpComponent implements OnInit { this.releasenotes = data.replace(new RegExp('\n', 'g'), '
'); }); } + + goToDocumentation() { + window.location.href = "https://docs.gns3.com/docs/"; + } } diff --git a/src/app/components/help/report-issue/filter.tsx b/src/app/components/help/report-issue/filter.tsx deleted file mode 100644 index 0ced31d4..00000000 --- a/src/app/components/help/report-issue/filter.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React, {Component} from "react"; -import Form from 'react-bootstrap/Form'; - -const formGroupStyle = { - margin: '20px' -}; - -class FilterComponent extends Component { - constructor(props) { - super(props); - } - - render() { - return ( -
- - - -
- ) - } -} - -export default FilterComponent; \ No newline at end of file diff --git a/src/app/components/help/report-issue/issue-list.tsx b/src/app/components/help/report-issue/issue-list.tsx deleted file mode 100644 index 835f6099..00000000 --- a/src/app/components/help/report-issue/issue-list.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import React, { Component } from "react"; -import Card from 'react-bootstrap/Card'; -import Spinner from 'react-bootstrap/Spinner'; -import IssueComponent from './issue'; -import FilterComponent from './filter'; -import e from '../../../event-bus'; - -const wrapperStyle = { - justifyContent: 'center' as 'center', - display: 'flex', - flex: 1, - flexDirection: 'row' as 'row', - flexWrap: 'wrap' as 'wrap', - margin: '20px' -}; - -const cardStyle = { - width: '300px', - margin: '20px' -}; - -const cardTitleStyle = { - color: 'red' -}; -const message = 'Thank you for reporting the issue!'; -const apiUrl = 'https://api.github.com/repos/GNS3/gns3-web-ui/issues'; -const newIssueLink = 'https://github.com/GNS3/gns3-web-ui/issues/new'; - -class IssueListComponent extends Component { - constructor(props) { - super(props); - this.state = { - issues: [], - filteredIssues: [], - isFetching: true - }; - } - - componentDidMount() { - fetch(apiUrl) - .then(response => response.json()) - .then(data => this.setState({ - issues: data, - filteredIssues: data, - isFetching: false - })); - } - - handleChange = (event) => { - let filter = event.target.value; - let filteredIssues = this.state.issues; - - filteredIssues = filteredIssues.filter((issue) => { - return issue.title.toLowerCase().includes(filter.toLowerCase()) - }); - - this.setState({ - filteredIssues: filteredIssues - }); - } - - newIssueOpened = (event) => { - e.emit('message', { text: message }); - } - - render() { - return ( -
-
- -
- - {this.state.isFetching ? ( -
- -
- ) : ( -
- {this.state.filteredIssues.map(issue => - - )} - - - Don't see your issue here? - Open new issue - - -
- )} -
- ); - } -}; - -export default IssueListComponent; \ No newline at end of file diff --git a/src/app/components/help/report-issue/issue.tsx b/src/app/components/help/report-issue/issue.tsx deleted file mode 100644 index cc8e6dc7..00000000 --- a/src/app/components/help/report-issue/issue.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { Component } from "react"; -import Card from 'react-bootstrap/Card'; - -const cardStyle = { - width: '300px', - margin: '20px' -}; - -const cardTitleStyle = { - color: 'black' -}; - -const cardTextStyle = { - color: 'black', - overflow: 'auto', - height: '100px' -}; - -class IssueComponent extends Component { - constructor(props) { - super(props); - this.state = { - data: this.props.data - } - } - - render() { - const issue = this.state.data; - return ( - - - {issue.title} - Status: {issue.state} - {issue.body} - {issue.html_url} - - - ); - } -}; - -export default IssueComponent; \ No newline at end of file diff --git a/src/app/components/help/report-issue/report-issue.component.html b/src/app/components/help/report-issue/report-issue.component.html deleted file mode 100644 index de3af182..00000000 --- a/src/app/components/help/report-issue/report-issue.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/components/help/report-issue/report-issue.component.spec.ts b/src/app/components/help/report-issue/report-issue.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/app/components/help/report-issue/report-issue.component.tsx b/src/app/components/help/report-issue/report-issue.component.tsx deleted file mode 100644 index f56212a7..00000000 --- a/src/app/components/help/report-issue/report-issue.component.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { HttpClient } from '@angular/common/http'; -import { - AfterViewInit, - Component, - ElementRef, - EventEmitter, - Input, - OnInit, - OnChanges, - OnDestroy, - Output, - SimpleChanges, - ViewChild, - ViewEncapsulation, - AfterContentInit - } from '@angular/core'; -import IssueListComponent from '../report-issue/issue-list'; -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import e from '../../../event-bus'; -import { ToasterService } from '../../../services/toaster.service'; - -@Component({ -selector: 'app-report-issue', -templateUrl: './report-issue.component.html', -styleUrls: ['./report-issue.component.scss'] -}) -export class ReportIssueComponent implements OnDestroy, AfterViewInit, AfterContentInit { - @ViewChild('issueListComponentContainer') containerRef: ElementRef; - - constructor(private toasterService: ToasterService) {} - - ngAfterViewInit() { - this.render(); - } - - ngAfterContentInit() { - e.on('message', message => { - this.toasterService.success(message.text); - }); - } - - ngOnDestroy() { - ReactDOM.unmountComponentAtNode(this.containerRef.nativeElement); - } - - private render() { - ReactDOM.render(
- -
, this.containerRef.nativeElement); - } -} diff --git a/src/app/components/login/login.component.html b/src/app/components/login/login.component.html index d4ea412c..f4bc34d0 100644 --- a/src/app/components/login/login.component.html +++ b/src/app/components/login/login.component.html @@ -18,7 +18,7 @@ You must enter username - + You must enter password diff --git a/src/app/components/preferences/common/custom-adapters/custom-adapters.component.ts b/src/app/components/preferences/common/custom-adapters/custom-adapters.component.ts index d9d92362..34f2080e 100644 --- a/src/app/components/preferences/common/custom-adapters/custom-adapters.component.ts +++ b/src/app/components/preferences/common/custom-adapters/custom-adapters.component.ts @@ -18,9 +18,7 @@ export class CustomAdaptersComponent { public adapters: CustomAdapter[]; public numberOfAdapters: number; - constructor() { - console.log(this.networkTypes); - } + constructor() {} cancelConfigureCustomAdapters() { this.closeConfiguratorEmitter.emit(false); @@ -28,8 +26,6 @@ export class CustomAdaptersComponent { configureCustomAdapters() { this.adapters = []; - console.log(this.customAdapters); - this.customAdapters.adapters.forEach((n) => { this.adapters.push({ adapter_number: n.adapter_number, diff --git a/src/app/components/preferences/common/symbols/symbols.component.html b/src/app/components/preferences/common/symbols/symbols.component.html index c9c11bd4..a182fa8b 100644 --- a/src/app/components/preferences/common/symbols/symbols.component.html +++ b/src/app/components/preferences/common/symbols/symbols.component.html @@ -32,7 +32,7 @@ lazyimg [ngClass]="{ imageSelected: isSelected === symbol.symbol_id }" class="image" - [src]="getImageSourceForTemplate(symbol.symbol_id)" + [src]="getImageSourceForTemplate(symbol.symbol_id)| authImage: server | async" /> diff --git a/src/app/components/preferences/common/symbols/symbols.component.ts b/src/app/components/preferences/common/symbols/symbols.component.ts index e7a13d95..4ed42f18 100644 --- a/src/app/components/preferences/common/symbols/symbols.component.ts +++ b/src/app/components/preferences/common/symbols/symbols.component.ts @@ -1,4 +1,5 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; import { Server } from '../../../../models/server'; import { Symbol } from '../../../../models/symbol'; import { SymbolService } from '../../../../services/symbol.service'; @@ -18,7 +19,7 @@ export class SymbolsComponent implements OnInit { isSelected: string = ''; searchText: string = ''; - constructor(private symbolService: SymbolService) {} + constructor(private symbolService: SymbolService, private domSanitizer: DomSanitizer) {} ngOnInit() { this.isSelected = this.symbol; @@ -76,6 +77,11 @@ export class SymbolsComponent implements OnInit { width=\"${imageToUpload.width}\">\n\n`; } + getImage(symbolFilename: string) { + let symbol = this.symbolService.get(symbolFilename); + return this.domSanitizer.bypassSecurityTrustUrl(`data:image/svg+xml;base64,${btoa(symbol.raw)}`); + } + getImageSourceForTemplate(symbol: string) { return `${this.server.protocol}//${this.server.host}:${this.server.port}/v3/symbols/${symbol}/raw`; } diff --git a/src/app/components/project-map/console-wrapper/console-wrapper.component.spec.ts b/src/app/components/project-map/console-wrapper/console-wrapper.component.spec.ts index 6f2b6d75..c9753326 100644 --- a/src/app/components/project-map/console-wrapper/console-wrapper.component.spec.ts +++ b/src/app/components/project-map/console-wrapper/console-wrapper.component.spec.ts @@ -1,21 +1,47 @@ +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ToasterService } from '../../../services/toaster.service'; import { MapSettingsService } from '../../../services/mapsettings.service'; import { NodeConsoleService } from '../../../services/nodeConsole.service'; import { ThemeService } from '../../../services/theme.service'; import { ConsoleWrapperComponent } from './console-wrapper.component'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; describe('ConsoleWrapperComponent', () => { + let fixture: ComponentFixture; + let component: ConsoleWrapperComponent; + + let nodeConsoleService: NodeConsoleService; + let themeService: ThemeService; + let mapSettingsService: MapSettingsService; + let toasterService: ToasterService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule, MatSnackBarModule], + providers: [NodeConsoleService, ThemeService, MapSettingsService, ToasterService] + }).compileComponents(); + + toasterService = TestBed.inject(ToasterService); + nodeConsoleService = TestBed.inject(NodeConsoleService); + themeService = TestBed.inject(ThemeService); + mapSettingsService = TestBed.inject(MapSettingsService); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConsoleWrapperComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); + it('should get actual theme', () => { - const consoleService = new NodeConsoleService(); - - const themeService = autoSpy(ThemeService); - themeService.getActualTheme.and.returnValue('light'); - - const mapSettingsService = autoSpy(MapSettingsService); - const component = new ConsoleWrapperComponent(consoleService, themeService, mapSettingsService); - component.ngOnInit(); - expect(component.isLightThemeEnabled).toBe(true); + expect(component.isLightThemeEnabled).toBe(false); }); }); diff --git a/src/app/components/project-map/context-console-menu/context-console-menu.component.spec.ts b/src/app/components/project-map/context-console-menu/context-console-menu.component.spec.ts index 8f51d1b8..dbd0d098 100644 --- a/src/app/components/project-map/context-console-menu/context-console-menu.component.spec.ts +++ b/src/app/components/project-map/context-console-menu/context-console-menu.component.spec.ts @@ -1,6 +1,7 @@ import { ChangeDetectorRef, NO_ERRORS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { MatMenuModule } from '@angular/material/menu'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; import { BrowserModule } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { ElectronService } from 'ngx-electron'; @@ -20,7 +21,9 @@ import { ContextConsoleMenuComponent } from './context-console-menu.component'; describe('ContextConsoleMenuComponent', () => { let component: ContextConsoleMenuComponent; let fixture: ComponentFixture; - let toasterService: MockedToasterService = new MockedToasterService(); + let nodeConsoleService: NodeConsoleService; + let mapSettingsService: MapSettingsService; + let toasterService: ToasterService; let router = { url: '', navigate: jasmine.createSpy('navigate'), @@ -28,7 +31,6 @@ describe('ContextConsoleMenuComponent', () => { let node = { status: 'started', }; - let mapSettingsService = new MapSettingsService(); beforeEach(async(() => { const electronMock = { @@ -36,20 +38,25 @@ describe('ContextConsoleMenuComponent', () => { }; TestBed.configureTestingModule({ - imports: [MatMenuModule, BrowserModule], + imports: [MatMenuModule, BrowserModule, MatSnackBarModule], providers: [ { provide: ChangeDetectorRef }, { provide: ProjectService, useClass: MockedProjectService }, { provide: ElectronService, useValue: electronMock }, { provide: MapSettingsService, useValue: mapSettingsService }, - { provide: NodeConsoleService }, { provide: ConsoleService }, - { provide: ToasterService, useValue: toasterService }, { provide: Router, useValue: router }, + NodeConsoleService, + ToasterService, + MapSettingsService ], declarations: [ContextConsoleMenuComponent, ConsoleDeviceActionComponent, ConsoleDeviceActionBrowserComponent], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); + + toasterService = TestBed.inject(ToasterService); + mapSettingsService = TestBed.inject(MapSettingsService); + nodeConsoleService = TestBed.inject(NodeConsoleService); })); beforeEach(() => { diff --git a/src/app/components/project-map/context-menu/actions/console-device-action/console-device-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/console-device-action/console-device-action.component.spec.ts index f238ea9e..d9630591 100644 --- a/src/app/components/project-map/context-menu/actions/console-device-action/console-device-action.component.spec.ts +++ b/src/app/components/project-map/context-menu/actions/console-device-action/console-device-action.component.spec.ts @@ -7,7 +7,6 @@ import { NodeService } from '../../../../../services/node.service'; import { ServerService } from '../../../../../services/server.service'; import { MockedServerService } from '../../../../../services/server.service.spec'; import { SettingsService } from '../../../../../services/settings.service'; -import { MockedSettingsService } from '../../../../../services/settings.service.spec'; import { ToasterService } from '../../../../../services/toaster.service'; import { MockedToasterService } from '../../../../../services/toaster.service.spec'; import { MockedNodeService } from '../../../project-map.component.spec'; @@ -18,7 +17,7 @@ describe('ConsoleDeviceActionComponent', () => { let fixture: ComponentFixture; let electronService; let server: Server; - let mockedSettingsService: MockedSettingsService; + let settingsService: SettingsService; let mockedServerService: MockedServerService; let mockedToaster: MockedToasterService; let mockedNodeService: MockedNodeService = new MockedNodeService(); @@ -35,7 +34,6 @@ describe('ConsoleDeviceActionComponent', () => { }, }; - mockedSettingsService = new MockedSettingsService(); mockedServerService = new MockedServerService(); mockedToaster = new MockedToasterService(); @@ -47,13 +45,15 @@ describe('ConsoleDeviceActionComponent', () => { providers: [ { provide: ElectronService, useValue: electronService }, { provide: ServerService, useValue: mockedServerService }, - { provide: SettingsService, useValue: mockedSettingsService }, + { provide: SettingsService }, { provide: ToasterService, useValue: mockedToaster }, { provide: NodeService, useValue: mockedNodeService }, ], imports: [MatIconModule], declarations: [ConsoleDeviceActionComponent], }).compileComponents(); + + settingsService = TestBed.inject(SettingsService); })); beforeEach(() => { @@ -85,7 +85,7 @@ describe('ConsoleDeviceActionComponent', () => { component.nodes = nodes; component.server = server; - mockedSettingsService.set('console_command', 'command'); + settingsService.setConsoleSettings('command'); spyOn(component, 'openConsole'); }); @@ -105,7 +105,7 @@ describe('ConsoleDeviceActionComponent', () => { }); it('should set command when it is not defined', async () => { - mockedSettingsService.set('console_command', undefined); + settingsService.setConsoleSettings(undefined); await component.console(); expect(component.openConsole).toHaveBeenCalled(); }); diff --git a/src/app/components/project-map/context-menu/actions/console-device-action/console-device-action.component.ts b/src/app/components/project-map/context-menu/actions/console-device-action/console-device-action.component.ts index c9a1ee43..f826a59a 100644 --- a/src/app/components/project-map/context-menu/actions/console-device-action/console-device-action.component.ts +++ b/src/app/components/project-map/context-menu/actions/console-device-action/console-device-action.component.ts @@ -26,8 +26,8 @@ export class ConsoleDeviceActionComponent implements OnInit { ngOnInit() {} async console() { - let consoleCommand = this.settingsService.get('console_command') - ? this.settingsService.get('console_command') + let consoleCommand = this.settingsService.getConsoleSettings() + ? this.settingsService.getConsoleSettings() : this.nodeService.getDefaultCommand(); const startedNodes = this.nodes.filter((node) => node.status === 'started'); diff --git a/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.html b/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.html index 2d9d42fc..028abf77 100644 --- a/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.html +++ b/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.html @@ -1,4 +1,4 @@ - diff --git a/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.spec.ts index cbb05c86..9a1d510c 100644 --- a/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.spec.ts +++ b/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.spec.ts @@ -1,4 +1,5 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; import { MatIconModule } from '@angular/material/icon'; import { MatMenuModule } from '@angular/material/menu'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -24,7 +25,7 @@ describe('DeleteActionComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [MatIconModule, MatMenuModule, NoopAnimationsModule], + imports: [MatIconModule, MatMenuModule, NoopAnimationsModule, MatBottomSheetModule], providers: [ { provide: NodesDataSource, useClass: NodesDataSource }, { provide: DrawingsDataSource, useClass: DrawingsDataSource }, diff --git a/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.ts b/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.ts index ee874797..693cb21a 100644 --- a/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.ts +++ b/src/app/components/project-map/context-menu/actions/delete-action/delete-action.component.ts @@ -1,4 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; +import { MatBottomSheet } from '@angular/material/bottom-sheet'; +import { ConfirmationBottomSheetComponent } from 'app/components/projects/confirmation-bottomsheet/confirmation-bottomsheet.component'; import { DrawingsDataSource } from '../../../../../cartography/datasources/drawings-datasource'; import { LinksDataSource } from '../../../../../cartography/datasources/links-datasource'; import { NodesDataSource } from '../../../../../cartography/datasources/nodes-datasource'; @@ -26,11 +28,23 @@ export class DeleteActionComponent implements OnInit { private linksDataSource: LinksDataSource, private nodeService: NodeService, private drawingService: DrawingService, - private linkService: LinkService + private linkService: LinkService, + private bottomSheet: MatBottomSheet ) {} ngOnInit() {} + confirmDelete() { + this.bottomSheet.open(ConfirmationBottomSheetComponent); + let bottomSheetRef = this.bottomSheet._openedBottomSheetRef; + bottomSheetRef.instance.message = 'Do you want to delete all selected objects?'; + const bottomSheetSubscription = bottomSheetRef.afterDismissed().subscribe((result: boolean) => { + if (result) { + this.delete(); + } + }); + } + delete() { this.nodes.forEach((node) => { this.nodesDataSource.remove(node); diff --git a/src/app/components/project-map/context-menu/actions/http-console-new-tab/http-console-new-tab-action.component.ts b/src/app/components/project-map/context-menu/actions/http-console-new-tab/http-console-new-tab-action.component.ts index 42624197..73cf9ddd 100644 --- a/src/app/components/project-map/context-menu/actions/http-console-new-tab/http-console-new-tab-action.component.ts +++ b/src/app/components/project-map/context-menu/actions/http-console-new-tab/http-console-new-tab-action.component.ts @@ -1,8 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; +import { NodeConsoleService } from '../../../../../services/nodeConsole.service'; import { Node } from '../../../../../cartography/models/node'; import { Server } from '../../../../../models/server'; -import { ToasterService } from '../../../../../services/toaster.service'; @Component({ selector: 'app-http-console-new-tab-action', @@ -12,19 +11,11 @@ export class HttpConsoleNewTabActionComponent implements OnInit { @Input() server: Server; @Input() nodes: Node[]; - constructor(private toasterService: ToasterService, private router: Router) {} + constructor(private nodeConsoleService: NodeConsoleService) {} ngOnInit() {} openConsole() { - this.nodes.forEach((n) => { - if (n.status === 'started') { - let url = this.router.url.split('/'); - let urlString = `/static/web-ui/${url[1]}/${url[2]}/${url[3]}/${url[4]}/nodes/${n.node_id}`; - window.open(urlString); - } else { - this.toasterService.error('To open console please start the node'); - } - }); + this.nodeConsoleService.openConsolesForAllNodesInNewTabs(this.nodes); } } diff --git a/src/app/components/project-map/context-menu/actions/http-console/http-console-action.component.ts b/src/app/components/project-map/context-menu/actions/http-console/http-console-action.component.ts index 6486d9a0..523cdb4c 100644 --- a/src/app/components/project-map/context-menu/actions/http-console/http-console-action.component.ts +++ b/src/app/components/project-map/context-menu/actions/http-console/http-console-action.component.ts @@ -1,9 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { Node } from '../../../../../cartography/models/node'; import { Server } from '../../../../../models/server'; -import { MapSettingsService } from '../../../../../services/mapsettings.service'; import { NodeConsoleService } from '../../../../../services/nodeConsole.service'; -import { ToasterService } from '../../../../../services/toaster.service'; @Component({ selector: 'app-http-console-action', @@ -13,22 +11,11 @@ export class HttpConsoleActionComponent implements OnInit { @Input() server: Server; @Input() nodes: Node[]; - constructor( - private consoleService: NodeConsoleService, - private toasterService: ToasterService, - private mapSettingsService: MapSettingsService - ) {} + constructor(private nodeConsoleService: NodeConsoleService) {} ngOnInit() {} openConsole() { - this.nodes.forEach((n) => { - if (n.status === 'started') { - this.mapSettingsService.logConsoleSubject.next(true); - this.consoleService.openConsoleForNode(n); - } else { - this.toasterService.error('To open console please start the node'); - } - }); + this.nodeConsoleService.openConsolesForAllNodesInWidget(this.nodes); } } diff --git a/src/app/components/project-map/context-menu/context-menu.component.html b/src/app/components/project-map/context-menu/context-menu.component.html index f8af970a..38c810d0 100644 --- a/src/app/components/project-map/context-menu/context-menu.component.html +++ b/src/app/components/project-map/context-menu/context-menu.component.html @@ -8,12 +8,12 @@ diff --git a/src/app/components/project-map/log-console/log-console.component.spec.ts b/src/app/components/project-map/log-console/log-console.component.spec.ts index 65c18346..1d7886c3 100644 --- a/src/app/components/project-map/log-console/log-console.component.spec.ts +++ b/src/app/components/project-map/log-console/log-console.component.spec.ts @@ -4,6 +4,8 @@ import { EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { MatMenuModule } from '@angular/material/menu'; import { BrowserModule } from '@angular/platform-browser'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ToasterService } from '../../../services/toaster.service'; import { of } from 'rxjs'; import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource'; import { ProjectWebServiceHandler, WebServiceMessage } from '../../../handlers/project-web-service-handler'; @@ -14,6 +16,8 @@ import { NodeConsoleService } from '../../../services/nodeConsole.service'; import { MockedNodesDataSource, MockedNodeService } from '../project-map.component.spec'; import { LogConsoleComponent } from './log-console.component'; import { LogEventsDataSource } from './log-events-datasource'; +import { MapSettingsService } from '../../../services/mapsettings.service'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; export class MockedProjectWebServiceHandler { public nodeNotificationEmitter = new EventEmitter(); @@ -31,23 +35,32 @@ describe('LogConsoleComponent', () => { let mockedNodeService: MockedNodeService = new MockedNodeService(); let mockedNodesDataSource: MockedNodesDataSource = new MockedNodesDataSource(); let mockedProjectWebServiceHandler: MockedProjectWebServiceHandler = new MockedProjectWebServiceHandler(); + let nodeConsoleService: NodeConsoleService; + let mapSettingsService: MapSettingsService; + let toasterService: ToasterService; let httpServer = new HttpServer({} as HttpClient, {} as ServerErrorHandler); beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, MatMenuModule, BrowserModule], + imports: [HttpClientTestingModule, RouterTestingModule, MatMenuModule, BrowserModule, MatSnackBarModule], providers: [ { provide: ProjectWebServiceHandler, useValue: mockedProjectWebServiceHandler }, { provide: NodeService, useValue: mockedNodeService }, { provide: NodesDataSource, useValue: mockedNodesDataSource }, { provide: LogEventsDataSource, useClass: LogEventsDataSource }, { provide: HttpServer, useValue: httpServer }, - { provide: NodeConsoleService }, + NodeConsoleService, + ToasterService, + MapSettingsService ], declarations: [LogConsoleComponent], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); + + toasterService = TestBed.inject(ToasterService); + mapSettingsService = TestBed.inject(MapSettingsService); + nodeConsoleService = TestBed.inject(NodeConsoleService); })); beforeEach(() => { diff --git a/src/app/components/project-map/new-template-dialog/new-template-dialog.component.html b/src/app/components/project-map/new-template-dialog/new-template-dialog.component.html index ada6fd3e..734e07d7 100644 --- a/src/app/components/project-map/new-template-dialog/new-template-dialog.component.html +++ b/src/app/components/project-map/new-template-dialog/new-template-dialog.component.html @@ -259,6 +259,38 @@ + +
+ + {{ version.images.cdrom_image}} + + +
+ check + close + + + + +
+
diff --git a/src/app/components/project-map/nodes-menu/nodes-menu.component.ts b/src/app/components/project-map/nodes-menu/nodes-menu.component.ts index 1bcfd7c4..839ccbbe 100644 --- a/src/app/components/project-map/nodes-menu/nodes-menu.component.ts +++ b/src/app/components/project-map/nodes-menu/nodes-menu.component.ts @@ -1,4 +1,5 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { MapSettingsService } from '../../../services/mapsettings.service'; import { ElectronService } from 'ngx-electron'; import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource'; import { Project } from '../../../models/project'; @@ -7,6 +8,7 @@ import { NodeService } from '../../../services/node.service'; import { ServerService } from '../../../services/server.service'; import { SettingsService } from '../../../services/settings.service'; import { ToasterService } from '../../../services/toaster.service'; +import { NodeConsoleService } from '../../../services/nodeConsole.service'; @Component({ selector: 'app-nodes-menu', @@ -20,17 +22,19 @@ export class NodesMenuComponent { constructor( private nodeService: NodeService, + private nodeConsoleService: NodeConsoleService, private nodesDataSource: NodesDataSource, private toasterService: ToasterService, private serverService: ServerService, private settingsService: SettingsService, + private mapSettingsService: MapSettingsService, private electronService: ElectronService ) {} async startConsoleForAllNodes() { if (this.electronService.isElectronApp) { - let consoleCommand = this.settingsService.get('console_command') - ? this.settingsService.get('console_command') + let consoleCommand = this.settingsService.getConsoleSettings() + ? this.settingsService.getConsoleSettings() : this.nodeService.getDefaultCommand(); let nodes = this.nodesDataSource.getItems(); @@ -48,7 +52,11 @@ export class NodesMenuComponent { await this.electronService.remote.require('./console-executor.js').openConsole(request); } } else { - this.toasterService.error('Option to start all nodes not available in web browser.'); + if (this.mapSettingsService.openConsolesInWidget) { + this.nodeConsoleService.openConsolesForAllNodesInWidget(this.nodesDataSource.getItems()); + } else { + this.nodeConsoleService.openConsolesForAllNodesInNewTabs(this.nodesDataSource.getItems()); + } } } diff --git a/src/app/components/project-map/project-map.component.html b/src/app/components/project-map/project-map.component.html index b4e24a25..57cd12ee 100644 --- a/src/app/components/project-map/project-map.component.html +++ b/src/app/components/project-map/project-map.component.html @@ -1,6 +1,6 @@
-
@@ -255,12 +254,6 @@ (closeConsole)="toggleShowConsole($event)" >
-
- -
+ + diff --git a/src/app/components/project-map/project-map.component.spec.ts b/src/app/components/project-map/project-map.component.spec.ts index ef1e953a..bdad1e58 100644 --- a/src/app/components/project-map/project-map.component.spec.ts +++ b/src/app/components/project-map/project-map.component.spec.ts @@ -59,7 +59,6 @@ import { RecentlyOpenedProjectService } from '../../services/recentlyOpenedProje import { ServerService } from '../../services/server.service'; import { MockedServerService } from '../../services/server.service.spec'; import { SettingsService } from '../../services/settings.service'; -import { MockedSettingsService } from '../../services/settings.service.spec'; import { ToasterService } from '../../services/toaster.service'; import { MockedToasterService } from '../../services/toaster.service.spec'; import { ToolsService } from '../../services/tools.service'; @@ -300,7 +299,7 @@ xdescribe('ProjectMapComponent', () => { { provide: NodesDataSource, useValue: nodesDataSource }, { provide: LinksDataSource, useValue: linksDataSource }, { provide: DrawingsDataSource, useValue: drawingsDataSource }, - { provide: SettingsService, useClass: MockedSettingsService }, + { provide: SettingsService }, { provide: ToolsService }, { provide: SelectionManager }, { provide: SelectionTool }, diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index 12b1d766..616c1151 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -1,11 +1,11 @@ -import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Injector, OnDestroy, OnInit, SimpleChange, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core'; import { MatBottomSheet } from '@angular/material/bottom-sheet'; import { MatDialog } from '@angular/material/dialog'; import { Title } from '@angular/platform-browser'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import * as Mousetrap from 'mousetrap'; import { from, Observable, Subscription } from 'rxjs'; -import { map, mergeMap } from 'rxjs/operators'; +import { map, mergeMap, takeUntil } from 'rxjs/operators'; import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component'; import { MapDrawingToDrawingConverter } from '../../cartography/converters/map/map-drawing-to-drawing-converter'; import { MapLabelToLabelConverter } from '../../cartography/converters/map/map-label-to-label-converter'; @@ -74,7 +74,7 @@ import { ImportProjectDialogComponent } from '../projects/import-project-dialog/ import { NavigationDialogComponent } from '../projects/navigation-dialog/navigation-dialog.component'; import { SaveProjectDialogComponent } from '../projects/save-project-dialog/save-project-dialog.component'; import { NodeAddedEvent } from '../template/template-list-dialog/template-list-dialog.component'; -import { ContextConsoleMenuComponent } from './context-console-menu/context-console-menu.component'; +import { TopologySummaryComponent } from '../topology-summary/topology-summary.component'; import { ContextMenuComponent } from './context-menu/context-menu.component'; import { NodeCreatedLabelStylesFixer } from './helpers/node-created-label-styles-fixer'; import { NewTemplateDialogComponent } from './new-template-dialog/new-template-dialog.component'; @@ -105,6 +105,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy { public gridVisibility: boolean = false; public toolbarVisibility: boolean = true; public symbolScaling: boolean = true; + public symbolsLoaded: boolean = false; + private instance: ComponentRef; tools = { selection: true, @@ -121,9 +123,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy { public isLightThemeEnabled: boolean = false; @ViewChild(ContextMenuComponent) contextMenu: ContextMenuComponent; - @ViewChild(ContextConsoleMenuComponent) consoleContextMenu: ContextConsoleMenuComponent; @ViewChild(D3MapComponent) mapChild: D3MapComponent; @ViewChild(ProjectMapMenuComponent) projectMapMenuComponent: ProjectMapMenuComponent; + @ViewChild('topologySummaryContainer', {read: ViewContainerRef}) topologySummaryContainer: ViewContainerRef; private projectMapSubscription: Subscription = new Subscription(); @@ -173,12 +175,17 @@ export class ProjectMapComponent implements OnInit, OnDestroy { private title: Title, private nodeConsoleService: NodeConsoleService, private symbolService: SymbolService, - private cd: ChangeDetectorRef + private cd: ChangeDetectorRef, + private cfr: ComponentFactoryResolver, + private injector: Injector ) {} ngOnInit() { this.getSettings(); this.progressService.activate(); + this.symbolService.symbolsLoaded.subscribe(loaded => { + this.symbolsLoaded = true; + }); if (this.serverService.isServiceInitialized) { this.getData(); @@ -208,7 +215,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.settings = this.settingsService.getAll(); this.symbolScaling = this.mapSettingsService.getSymbolScaling(); - this.isTopologySummaryVisible = this.mapSettingsService.isTopologySummaryVisible; this.isConsoleVisible = this.mapSettingsService.isLogConsoleVisible; this.mapSettingsService.logConsoleSubject.subscribe((value) => (this.isConsoleVisible = value)); this.notificationsVisibility = localStorage.getItem('notificationsVisibility') === 'true' ? true : false; @@ -216,6 +222,21 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.gridVisibility = localStorage.getItem('gridVisibility') === 'true' ? true : false; } + async lazyLoadTopologySummary() { + if (this.isTopologySummaryVisible) { + const {TopologySummaryComponent} = await import('../topology-summary/topology-summary.component'); + const componentFactory = this.cfr.resolveComponentFactory(TopologySummaryComponent); + this.instance = this.topologySummaryContainer.createComponent(componentFactory, null, this.injector); + this.instance.instance.server = this.server; + this.instance.instance.project = this.project; + } else if (this.instance) { + if (this.instance.instance) { + this.instance.instance.ngOnDestroy(); + this.instance.destroy(); + } + } + } + addSubscriptions() { this.projectMapSubscription.add( this.mapSettingsService.mapRenderedEmitter.subscribe((value: boolean) => { @@ -299,8 +320,11 @@ export class ProjectMapComponent implements OnInit, OnDestroy { .pipe( mergeMap((server: Server) => { if (!server) this.router.navigate(['/servers']); - this.server = server; + + // load symbols + this.symbolService.load(this.server); + return this.projectService.get(server, paramMap.get('project_id')).pipe( map((project) => { return project; @@ -314,6 +338,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.projectService.open(this.server, this.project.project_id); this.title.setTitle(this.project.name); this.isInterfaceLabelVisible = this.mapSettingsService.showInterfaceLabels; + this.toggleShowTopologySummary(this.mapSettingsService.isTopologySummaryVisible); this.recentlyOpenedProjectService.setServerId(this.server.id.toString()); this.recentlyOpenedProjectService.setProjectId(this.project.project_id); @@ -386,16 +411,27 @@ export class ProjectMapComponent implements OnInit, OnDestroy { Mousetrap.bind('del', (event: Event) => { event.preventDefault(); - const selected = this.selectionManager.getSelected(); + this.deleteItems(); + }); + } - selected - .filter((item) => item instanceof MapNode) - .forEach((item: MapNode) => { - const node = this.mapNodeToNode.convert(item); - this.nodeService.delete(this.server, node).subscribe((data) => { - this.toasterService.success('Node has been deleted'); + deleteItems() { + this.bottomSheet.open(ConfirmationBottomSheetComponent); + let bottomSheetRef = this.bottomSheet._openedBottomSheetRef; + bottomSheetRef.instance.message = 'Do you want to delete all selected objects?'; + const bottomSheetSubscription = bottomSheetRef.afterDismissed().subscribe((result: boolean) => { + if (result) { + const selected = this.selectionManager.getSelected(); + + selected + .filter((item) => item instanceof MapNode) + .forEach((item: MapNode) => { + const node = this.mapNodeToNode.convert(item); + this.nodeService.delete(this.server, node).subscribe((data) => { + this.toasterService.success('Node has been deleted'); + }); }); - }); + } }); } @@ -448,33 +484,33 @@ export class ProjectMapComponent implements OnInit, OnDestroy { const onLinkContextMenu = this.linkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => { const link = this.mapLinkToLink.convert(eventLink.link); - this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX); + this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.screenY - 60, eventLink.event.screenX); }); const onEthernetLinkContextMenu = this.ethernetLinkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => { const link = this.mapLinkToLink.convert(eventLink.link); - this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX); + this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.screenY - 60, eventLink.event.screenX); }); const onSerialLinkContextMenu = this.serialLinkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => { const link = this.mapLinkToLink.convert(eventLink.link); - this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX); + this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.screenY - 60, eventLink.event.screenX); }); const onNodeContextMenu = this.nodeWidget.onContextMenu.subscribe((eventNode: NodeContextMenu) => { const node = this.mapNodeToNode.convert(eventNode.node); - this.contextMenu.openMenuForNode(node, eventNode.event.pageY, eventNode.event.pageX); + this.contextMenu.openMenuForNode(node, eventNode.event.screenY - 60, eventNode.event.screenX); }); const onDrawingContextMenu = this.drawingsWidget.onContextMenu.subscribe((eventDrawing: DrawingContextMenu) => { const drawing = this.mapDrawingToDrawing.convert(eventDrawing.drawing); - this.contextMenu.openMenuForDrawing(drawing, eventDrawing.event.pageY, eventDrawing.event.pageX); + this.contextMenu.openMenuForDrawing(drawing, eventDrawing.event.screenY - 60, eventDrawing.event.screenX); }); const onLabelContextMenu = this.labelWidget.onContextMenu.subscribe((eventLabel: LabelContextMenu) => { const label = this.mapLabelToLabel.convert(eventLabel.label); const node = this.nodes.find((n) => n.node_id === eventLabel.label.nodeId); - this.contextMenu.openMenuForLabel(label, node, eventLabel.event.pageY, eventLabel.event.pageX); + this.contextMenu.openMenuForLabel(label, node, eventLabel.event.screenY - 60, eventLabel.event.screenX); }); const onInterfaceLabelContextMenu = this.interfaceLabelWidget.onContextMenu.subscribe( @@ -484,8 +520,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.contextMenu.openMenuForInterfaceLabel( linkNode, link, - eventInterfaceLabel.event.pageY, - eventInterfaceLabel.event.pageX + eventInterfaceLabel.event.screenY - 60, + eventInterfaceLabel.event.screenX ); } ); @@ -514,11 +550,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.contextMenu.openMenuForListOfElements(drawings, nodes, labels, links, event.pageY, event.pageX); }); - const onContextConsoleMenu = this.nodeWidget.onContextConsoleMenu.subscribe((eventNode: NodeContextMenu) => { - const node = this.mapNodeToNode.convert(eventNode.node); - this.consoleContextMenu.openMenu(node, eventNode.event.pageY, eventNode.event.pageX); - }); - this.projectMapSubscription.add(onLinkContextMenu); this.projectMapSubscription.add(onEthernetLinkContextMenu); this.projectMapSubscription.add(onSerialLinkContextMenu); @@ -527,7 +558,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.projectMapSubscription.add(onContextMenu); this.projectMapSubscription.add(onLabelContextMenu); this.projectMapSubscription.add(onInterfaceLabelContextMenu); - this.projectMapSubscription.add(onContextConsoleMenu); this.mapChangeDetectorRef.detectChanges(); } @@ -536,6 +566,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy { return; } + nodeAddedEvent.x = nodeAddedEvent.x / this.mapScaleService.getScale(); + nodeAddedEvent.y = nodeAddedEvent.y / this.mapScaleService.getScale(); + this.progressService.activate(); this.nodeService .createFromTemplate( @@ -796,6 +829,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { public toggleShowTopologySummary(visible: boolean) { this.isTopologySummaryVisible = visible; this.mapSettingsService.toggleTopologySummary(this.isTopologySummaryVisible); + this.lazyLoadTopologySummary(); } public toggleNotifications(visible: boolean) { diff --git a/src/app/components/projects/projects.component.scss b/src/app/components/projects/projects.component.scss index 0268f4a2..96d13262 100644 --- a/src/app/components/projects/projects.component.scss +++ b/src/app/components/projects/projects.component.scss @@ -1,10 +1,12 @@ .import-button { height: 40px; + width: 160px; margin: 20px; } .add-button { height: 40px; + width: 160px; margin: 20px; } @@ -13,3 +15,7 @@ margin-left: -470px; left: 50%; } + +.row { + display: flex; +} diff --git a/src/app/components/projects/projects.component.spec.ts b/src/app/components/projects/projects.component.spec.ts index 8fd4a093..c2f9625a 100644 --- a/src/app/components/projects/projects.component.spec.ts +++ b/src/app/components/projects/projects.component.spec.ts @@ -23,7 +23,6 @@ import { MockedProjectService } from '../../services/project.service.spec'; import { ServerService } from '../../services/server.service'; import { MockedServerService } from '../../services/server.service.spec'; import { Settings, SettingsService } from '../../services/settings.service'; -import { MockedSettingsService } from '../../services/settings.service.spec'; import { ToasterService } from '../../services/toaster.service'; import { ConfigureGns3VMDialogComponent } from '../servers/configure-gns3vm-dialog/configure-gns3vm-dialog.component'; import { ChooseNameDialogComponent } from './choose-name-dialog/choose-name-dialog.component'; @@ -70,7 +69,7 @@ xdescribe('ProjectsComponent', () => { providers: [ { provide: ServerService, useClass: MockedServerService }, { provide: ProjectService, useValue: mockedProjectService }, - { provide: SettingsService, useClass: MockedSettingsService }, + { provide: SettingsService}, { provide: ToasterService }, { provide: ElectronService, useValue: electronService }, ProgressService, @@ -83,10 +82,10 @@ xdescribe('ProjectsComponent', () => { }) .compileComponents(); - serverService = TestBed.get(ServerService); - settingsService = TestBed.get(SettingsService); - projectService = TestBed.get(ProjectService); - progressService = TestBed.get(ProgressService); + serverService = TestBed.inject(ServerService); + settingsService = TestBed.inject(SettingsService); + projectService = TestBed.inject(ProjectService); + progressService = TestBed.inject(ProgressService); server = new Server(); server.id = 99; diff --git a/src/app/components/servers/add-server-dialog/add-server-dialog.component.html b/src/app/components/servers/add-server-dialog/add-server-dialog.component.html index 95e68241..85916267 100644 --- a/src/app/components/servers/add-server-dialog/add-server-dialog.component.html +++ b/src/app/components/servers/add-server-dialog/add-server-dialog.component.html @@ -35,7 +35,7 @@
- +
diff --git a/src/app/components/settings/settings.component.html b/src/app/components/settings/settings.component.html index 3660fe44..698940de 100644 --- a/src/app/components/settings/settings.component.html +++ b/src/app/components/settings/settings.component.html @@ -13,19 +13,8 @@ Send anonymous crash reports
Integrate link labels to links
Automatically open project README files + Open consoles in the widget instead of in new tabs after clicking start consoles for all nodes - - - - diff --git a/src/app/components/settings/settings.component.spec.ts b/src/app/components/settings/settings.component.spec.ts index 380fd952..a802e197 100644 --- a/src/app/components/settings/settings.component.spec.ts +++ b/src/app/components/settings/settings.component.spec.ts @@ -6,7 +6,6 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { PersistenceModule } from 'angular-persistence'; import { MapSettingsService } from '../../services/mapsettings.service'; import { SettingsService } from '../../services/settings.service'; import { ConsoleService } from '../../services/settings/console.service'; @@ -23,7 +22,8 @@ describe('SettingsComponent', () => { let mapSettingsService = { integrateLinkLabelsToLinks: true, toggleIntegrateInterfaceLabels(val: boolean) {}, - toggleOpenReadme(val: boolean) {} + toggleOpenReadme(val: boolean) {}, + toggleOpenConsolesInWidget(val: boolean) {} }; let consoleService; let updatesService = autoSpy(UpdatesService); @@ -38,7 +38,6 @@ describe('SettingsComponent', () => { MatExpansionModule, MatCheckboxModule, FormsModule, - PersistenceModule, BrowserAnimationsModule, MatIconModule, MatFormFieldModule, @@ -76,11 +75,19 @@ describe('SettingsComponent', () => { }; const getAll = spyOn(settingsService, 'getAll').and.returnValue(settings); const setAll = spyOn(settingsService, 'setAll'); + spyOn(mapSettingsService, 'toggleIntegrateInterfaceLabels'); + spyOn(mapSettingsService, 'toggleOpenConsolesInWidget'); + component.ngOnInit(); + expect(getAll).toHaveBeenCalled(); expect(component.settings).toEqual(settings); + component.settings.crash_reports = false; component.save(); + expect(setAll).toHaveBeenCalledWith(settings); + expect(mapSettingsService.toggleIntegrateInterfaceLabels).toHaveBeenCalled(); + expect(mapSettingsService.toggleOpenConsolesInWidget).toHaveBeenCalled(); }); }); diff --git a/src/app/components/settings/settings.component.ts b/src/app/components/settings/settings.component.ts index eafa74d0..1f6d2861 100644 --- a/src/app/components/settings/settings.component.ts +++ b/src/app/components/settings/settings.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { MapSettingsService } from '../../services/mapsettings.service'; -import { SettingsService } from '../../services/settings.service'; +import { Settings, SettingsService } from '../../services/settings.service'; import { ConsoleService } from '../../services/settings/console.service'; import { ThemeService } from '../../services/theme.service'; import { ToasterService } from '../../services/toaster.service'; @@ -12,10 +12,11 @@ import { UpdatesService } from '../../services/updates.service'; styleUrls: ['./settings.component.scss'], }) export class SettingsComponent implements OnInit { - settings = { ...SettingsService.DEFAULTS }; + settings: Settings; consoleCommand: string; integrateLinksLabelsToLinks: boolean; openReadme: boolean; + openConsolesInWidget: boolean; constructor( private settingsService: SettingsService, @@ -31,6 +32,7 @@ export class SettingsComponent implements OnInit { this.consoleCommand = this.consoleService.command; this.integrateLinksLabelsToLinks = this.mapSettingsService.integrateLinkLabelsToLinks; this.openReadme = this.mapSettingsService.openReadme; + this.openConsolesInWidget = this.mapSettingsService.openConsolesInWidget; } save() { @@ -39,6 +41,7 @@ export class SettingsComponent implements OnInit { this.mapSettingsService.toggleIntegrateInterfaceLabels(this.integrateLinksLabelsToLinks); this.mapSettingsService.toggleOpenReadme(this.openReadme); + this.mapSettingsService.toggleOpenConsolesInWidget(this.openConsolesInWidget); } setDarkMode(value: boolean) { diff --git a/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.html b/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.html index 21f00940..38206f0d 100644 --- a/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.html +++ b/src/app/components/snapshots/create-snapshot-dialog/create-snapshot-dialog.component.html @@ -18,6 +18,6 @@
- +
diff --git a/src/app/components/template/template.component.html b/src/app/components/template/template.component.html index 917e30e2..26a3794e 100644 --- a/src/app/components/template/template.component.html +++ b/src/app/components/template/template.component.html @@ -43,25 +43,25 @@
- + Image
{{ filteredTemplates[i].name }}
- + Image
{{ filteredTemplates[i + 1].name }}
- + Image
{{ filteredTemplates[i + 2].name }}
- + Image
{{ filteredTemplates[i + 3].name }}
diff --git a/src/app/components/template/template.component.ts b/src/app/components/template/template.component.ts index acf6ced2..1b8a028d 100644 --- a/src/app/components/template/template.component.ts +++ b/src/app/components/template/template.component.ts @@ -8,6 +8,7 @@ import { MapScaleService } from '../../services/mapScale.service'; import { SymbolService } from '../../services/symbol.service'; import { TemplateService } from '../../services/template.service'; import { NodeAddedEvent, TemplateListDialogComponent } from './template-list-dialog/template-list-dialog.component'; +import { DomSanitizer } from '@angular/platform-browser'; @Component({ selector: 'app-template', @@ -49,7 +50,8 @@ export class TemplateComponent implements OnInit, OnDestroy { private dialog: MatDialog, private templateService: TemplateService, private scaleService: MapScaleService, - private symbolService: SymbolService + private symbolService: SymbolService, + private domSanitizer: DomSanitizer ) {} ngOnInit() { @@ -127,7 +129,9 @@ export class TemplateComponent implements OnInit, OnDestroy { } getImageSourceForTemplate(template: Template) { - return this.symbolService.getSymbolFromTemplate(this.server, template); + let symbol = this.symbolService.getSymbolFromTemplate(template); + if (symbol) return this.domSanitizer.bypassSecurityTrustUrl(`data:image/svg+xml;base64,${btoa(symbol.raw)}`); + return this.domSanitizer.bypassSecurityTrustUrl('data:image/svg+xml;base64,'); } ngOnDestroy() { diff --git a/src/app/components/users/logged-user/logged-user.component.html b/src/app/components/users/logged-user/logged-user.component.html new file mode 100644 index 00000000..14d9619c --- /dev/null +++ b/src/app/components/users/logged-user/logged-user.component.html @@ -0,0 +1,20 @@ +
+
+
+

Logged in user info

+
+
+
+ + + Username: {{user.username}} + Full name: {{user.full_name}} + Email: {{user.email}} + + +
+
+
+
+
+
diff --git a/src/app/components/users/logged-user/logged-user.component.scss b/src/app/components/users/logged-user/logged-user.component.scss new file mode 100644 index 00000000..e9958114 --- /dev/null +++ b/src/app/components/users/logged-user/logged-user.component.scss @@ -0,0 +1,3 @@ +.full_width { + width: 100%; +} diff --git a/src/app/components/help/report-issue/report-issue.component.scss b/src/app/components/users/logged-user/logged-user.component.spec.ts similarity index 100% rename from src/app/components/help/report-issue/report-issue.component.scss rename to src/app/components/users/logged-user/logged-user.component.spec.ts diff --git a/src/app/components/users/logged-user/logged-user.component.ts b/src/app/components/users/logged-user/logged-user.component.ts new file mode 100644 index 00000000..e8b79ab4 --- /dev/null +++ b/src/app/components/users/logged-user/logged-user.component.ts @@ -0,0 +1,50 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { ServerService } from '../../../services/server.service'; +import { UserService } from '../../../services/user.service'; +import { ToasterService } from '../../../services/toaster.service'; +import { User } from '../../../models/users/user'; +import { Server } from '../../../models/server'; + +@Component({ + selector: 'app-logged-user', + templateUrl: './logged-user.component.html', + styleUrls: ['./logged-user.component.scss'], +}) +export class LoggedUserComponent implements OnInit { + public user: User; + public server: Server; + + constructor( + private route: ActivatedRoute, + private serverService: ServerService, + private userService: UserService, + private toasterService: ToasterService + ) {} + + ngOnInit() { + let serverId = this.route.snapshot.paramMap.get('server_id'); + this.serverService.get(+serverId).then((server: Server) => { + this.server = server; + this.userService.getInformationAboutLoggedUser(server).subscribe((response: any) => { + this.user = response; + }); + }); + } + + copyToken() { + const selBox = document.createElement('textarea'); + selBox.style.position = 'fixed'; + selBox.style.left = '0'; + selBox.style.top = '0'; + selBox.style.opacity = '0'; + selBox.value = this.server.authToken; + document.body.appendChild(selBox); + selBox.focus(); + selBox.select(); + document.execCommand('copy'); + document.body.removeChild(selBox); + + this.toasterService.success('Token copied'); + } +} diff --git a/src/app/filters/authImageFilter.ts b/src/app/filters/authImageFilter.ts new file mode 100644 index 00000000..070b286b --- /dev/null +++ b/src/app/filters/authImageFilter.ts @@ -0,0 +1,26 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Console } from 'console'; +import { Server } from '../models/server'; +import { HttpServer } from '../services/http-server.service'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Pipe({ + name: 'authImage' +}) +export class AuthImageFilter implements PipeTransform { + + constructor( + private httpServer: HttpServer, + private domSanitizer: DomSanitizer + ) {} + + async transform(src: string, server: Server) { + let url = src.split('v3')[1]; + const imageBlob: Blob = await this.httpServer.getBlob(server, url).toPromise(); + const reader = new FileReader(); + return new Promise((resolve, reject) => { + reader.onloadend = () => resolve(this.domSanitizer.bypassSecurityTrustUrl(reader.result as string)); + reader.readAsDataURL(imageBlob); + }); + } +} diff --git a/src/app/layouts/default-layout/default-layout.component.html b/src/app/layouts/default-layout/default-layout.component.html index 694c6d33..f5a3123a 100644 --- a/src/app/layouts/default-layout/default-layout.component.html +++ b/src/app/layouts/default-layout/default-layout.component.html @@ -35,6 +35,10 @@ help Help +