diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2fb9f713..4641877b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -238,6 +238,8 @@ import { QemuImageCreatorComponent } from './components/project-map/node-editors import { ChooseNameDialogComponent } from './components/projects/choose-name-dialog/choose-name-dialog.component'; import { PacketCaptureService } from './services/packet-capture.service'; import { StartCaptureOnStartedLinkActionComponent } from './components/project-map/context-menu/actions/start-capture-on-started-link/start-capture-on-started-link.component'; +import { LockActionComponent } from './components/project-map/context-menu/actions/lock-action/lock-action.component'; +import { NavigationDialogComponent } from './components/projects/navigation-dialog/navigation-dialog.component'; if (environment.production) { Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', { @@ -401,7 +403,9 @@ if (environment.production) { TracengTemplateDetailsComponent, QemuImageCreatorComponent, ChooseNameDialogComponent, - StartCaptureOnStartedLinkActionComponent + StartCaptureOnStartedLinkActionComponent, + LockActionComponent, + NavigationDialogComponent ], imports: [ BrowserModule, @@ -519,7 +523,8 @@ if (environment.production) { ConfiguratorDialogNatComponent, ConfiguratorDialogTracengComponent, QemuImageCreatorComponent, - ChooseNameDialogComponent + ChooseNameDialogComponent, + NavigationDialogComponent ], bootstrap: [AppComponent] }) diff --git a/src/app/cartography/components/draggable-selection/draggable-selection.component.ts b/src/app/cartography/components/draggable-selection/draggable-selection.component.ts index 867d2e40..ffb73210 100644 --- a/src/app/cartography/components/draggable-selection/draggable-selection.component.ts +++ b/src/app/cartography/components/draggable-selection/draggable-selection.component.ts @@ -94,8 +94,10 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { ).subscribe((evt: DraggableDrag) => { if (!this.isMapLocked) { const selected = this.selectionManager.getSelected(); - const selectedNodes = selected.filter(item => item instanceof MapNode); // update nodes + let mapNodes = selected.filter(item => item instanceof MapNode); + const lockedNodes = mapNodes.filter((item: MapNode) => item.locked); + const selectedNodes = mapNodes.filter((item: MapNode) => !item.locked); selectedNodes.forEach((node: MapNode) => { node.x += evt.dx; node.y += evt.dy; @@ -116,52 +118,52 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { }); // update drawings - selected - .filter(item => item instanceof MapDrawing) - .forEach((drawing: MapDrawing) => { - drawing.x += evt.dx; - drawing.y += evt.dy; - this.drawingsWidget.redrawDrawing(svg, drawing); - }); + let mapDrawings = selected.filter(item => item instanceof MapDrawing); + const selectedDrawings = mapDrawings.filter((item: MapDrawing) => !item.locked); + selectedDrawings.forEach((drawing: MapDrawing) => { + drawing.x += evt.dx; + drawing.y += evt.dy; + this.drawingsWidget.redrawDrawing(svg, drawing); + }); // update labels - selected - .filter(item => item instanceof MapLabel) - .forEach((label: MapLabel) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } + let mapLabels = selected.filter(item => item instanceof MapLabel); + const selectedLabels = mapLabels.filter((item: MapLabel) => lockedNodes.filter((node) => node.id === item.nodeId).length === 0); + selectedLabels.forEach((label: MapLabel) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } - const node = this.graphDataManager.getNodes().filter(node => node.id === label.nodeId)[0]; - node.label.x += evt.dx; - node.label.y += evt.dy; - this.labelWidget.redrawLabel(svg, label); - }); + const node = this.graphDataManager.getNodes().filter(node => node.id === label.nodeId)[0]; + node.label.x += evt.dx; + node.label.y += evt.dy; + this.labelWidget.redrawLabel(svg, label); + }); // update interface labels - selected - .filter(item => item instanceof MapLinkNode) - .forEach((interfaceLabel: MapLinkNode) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === interfaceLabel.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } + let mapLinkNodes = selected.filter(item => item instanceof MapLinkNode); + const selectedLinkNodes = mapLinkNodes.filter((item: MapLinkNode) => lockedNodes.filter((node) => node.id === item.nodeId).length === 0); + selectedLinkNodes.forEach((interfaceLabel: MapLinkNode) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === interfaceLabel.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } - const link = this.graphDataManager - .getLinks() - .filter(link => link.nodes[0].id === interfaceLabel.id || link.nodes[1].id === interfaceLabel.id)[0]; - if (link.nodes[0].id === interfaceLabel.id) { - link.nodes[0].label.x += evt.dx; - link.nodes[0].label.y += evt.dy; - } - if (link.nodes[1].id === interfaceLabel.id) { - link.nodes[1].label.x += evt.dx; - link.nodes[1].label.y += evt.dy; - } + const link = this.graphDataManager + .getLinks() + .filter(link => link.nodes[0].id === interfaceLabel.id || link.nodes[1].id === interfaceLabel.id)[0]; + if (link.nodes[0].id === interfaceLabel.id) { + link.nodes[0].label.x += evt.dx; + link.nodes[0].label.y += evt.dy; + } + if (link.nodes[1].id === interfaceLabel.id) { + link.nodes[1].label.x += evt.dx; + link.nodes[1].label.y += evt.dy; + } - this.linksWidget.redrawLink(svg, link); - }); + this.linksWidget.redrawLink(svg, link); + }); } }); @@ -173,39 +175,41 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { ).subscribe((evt: DraggableEnd) => { if (!this.isMapLocked) { const selected = this.selectionManager.getSelected(); - const selectedNodes = selected.filter(item => item instanceof MapNode); + let mapNodes = selected.filter(item => item instanceof MapNode); + const lockedNodes = mapNodes.filter((item: MapNode) => item.locked); + const selectedNodes = mapNodes.filter((item: MapNode) => !item.locked); selectedNodes.forEach((item: MapNode) => { this.nodesEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); }); - selected - .filter(item => item instanceof MapDrawing) - .forEach((item: MapDrawing) => { - this.drawingsEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); - }); + let mapDrawings = selected.filter(item => item instanceof MapDrawing); + const selectedDrawings = mapDrawings.filter((item: MapDrawing) => !item.locked); + selectedDrawings.forEach((item: MapDrawing) => { + this.drawingsEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); + }); - selected - .filter(item => item instanceof MapLabel) - .forEach((label: MapLabel) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } + let mapLabels = selected.filter(item => item instanceof MapLabel); + const selectedLabels = mapLabels.filter((item: MapLabel) => lockedNodes.filter((node) => node.id === item.nodeId).length === 0); + selectedLabels.forEach((label: MapLabel) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } - this.nodesEventSource.labelDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); - }); + this.nodesEventSource.labelDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); + }); - selected - .filter(item => item instanceof MapLinkNode) - .forEach((label: MapLinkNode) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } - this.linksEventSource.interfaceDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); - }); - } + let mapLinkNodes = selected.filter(item => item instanceof MapLinkNode); + const selectedLinkNodes = mapLinkNodes.filter((item: MapLinkNode) => lockedNodes.filter((node) => node.id === item.nodeId).length === 0) + selectedLinkNodes.forEach((label: MapLinkNode) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } + this.linksEventSource.interfaceDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); + }); + } }); } diff --git a/src/app/cartography/converters/map/drawing-to-map-drawing-converter.ts b/src/app/cartography/converters/map/drawing-to-map-drawing-converter.ts index 2c43cf80..a45ebd29 100644 --- a/src/app/cartography/converters/map/drawing-to-map-drawing-converter.ts +++ b/src/app/cartography/converters/map/drawing-to-map-drawing-converter.ts @@ -14,6 +14,7 @@ export class DrawingToMapDrawingConverter implements Converter { node.first_port_name = mapNode.firstPortName; node.height = mapNode.height; node.label = mapNode.label ? this.mapLabelToLabel.convert(mapNode.label) : undefined; + node.locked = mapNode.locked; node.name = mapNode.name; node.node_directory = mapNode.nodeDirectory; node.node_type = mapNode.nodeType; diff --git a/src/app/cartography/converters/map/node-to-map-node-converter.ts b/src/app/cartography/converters/map/node-to-map-node-converter.ts index 1a739069..b3e64b1d 100644 --- a/src/app/cartography/converters/map/node-to-map-node-converter.ts +++ b/src/app/cartography/converters/map/node-to-map-node-converter.ts @@ -28,13 +28,14 @@ export class NodeToMapNodeConverter implements Converter { mapNode.consoleHost = node.console_host; mapNode.firstPortName = node.first_port_name; mapNode.height = node.height; - mapNode.label = this.labelToMapLabel.convert(node.label, { node_id: node.node_id }); + mapNode.label = this.labelToMapLabel ? this.labelToMapLabel.convert(node.label, { node_id: node.node_id }) : undefined; + mapNode.locked = node.locked; mapNode.name = node.name; mapNode.nodeDirectory = node.node_directory; mapNode.nodeType = node.node_type; mapNode.portNameFormat = node.port_name_format; mapNode.portSegmentSize = node.port_segment_size; - mapNode.ports = node.ports.map(port => this.portToMapPort.convert(port)); + mapNode.ports = node.ports ? node.ports.map(port => this.portToMapPort.convert(port)) : []; mapNode.projectId = node.project_id; mapNode.status = node.status; mapNode.symbol = node.symbol; diff --git a/src/app/cartography/managers/graph-data-manager.ts b/src/app/cartography/managers/graph-data-manager.ts index fdf0a786..0de68ed5 100644 --- a/src/app/cartography/managers/graph-data-manager.ts +++ b/src/app/cartography/managers/graph-data-manager.ts @@ -34,31 +34,39 @@ export class GraphDataManager { ) {} public setNodes(nodes: Node[]) { - const mapNodes = nodes.map(n => this.nodeToMapNode.convert(n)); - this.mapNodesDataSource.set(mapNodes); + if (nodes) { + const mapNodes = nodes.map(n => this.nodeToMapNode.convert(n)); + this.mapNodesDataSource.set(mapNodes); - this.assignDataToLinks(); - this.onDataUpdate(); + this.assignDataToLinks(); + this.onDataUpdate(); + } } public setLinks(links: Link[]) { - const mapLinks = links.map(l => this.linkToMapLink.convert(l)); - this.mapLinksDataSource.set(mapLinks); + if (links) { + const mapLinks = links.map(l => this.linkToMapLink.convert(l)); + this.mapLinksDataSource.set(mapLinks); - this.assignDataToLinks(); - this.onDataUpdate(); + this.assignDataToLinks(); + this.onDataUpdate(); + } } public setDrawings(drawings: Drawing[]) { - const mapDrawings = drawings.map(d => this.drawingToMapDrawing.convert(d)); - this.mapDrawingsDataSource.set(mapDrawings); - - this.onDataUpdate(); + if (drawings) { + const mapDrawings = drawings.map(d => this.drawingToMapDrawing.convert(d)); + this.mapDrawingsDataSource.set(mapDrawings); + + this.onDataUpdate(); + } } public setSymbols(symbols: Symbol[]) { - const mapSymbols = symbols.map(s => this.symbolToMapSymbol.convert(s)); - this.mapSymbolsDataSource.set(mapSymbols); + if (symbols) { + const mapSymbols = symbols.map(s => this.symbolToMapSymbol.convert(s)); + this.mapSymbolsDataSource.set(mapSymbols); + } } public getNodes() { diff --git a/src/app/cartography/models/drawing.ts b/src/app/cartography/models/drawing.ts index 984617c5..e3eda3d6 100644 --- a/src/app/cartography/models/drawing.ts +++ b/src/app/cartography/models/drawing.ts @@ -5,6 +5,7 @@ export class Drawing { project_id: string; rotation: number; svg: string; + locked: boolean; x: number; y: number; z: number; diff --git a/src/app/cartography/models/map/map-drawing.ts b/src/app/cartography/models/map/map-drawing.ts index 685fee93..3510125b 100644 --- a/src/app/cartography/models/map/map-drawing.ts +++ b/src/app/cartography/models/map/map-drawing.ts @@ -6,6 +6,7 @@ export class MapDrawing implements Indexed { projectId: string; rotation: number; svg: string; + locked: boolean; x: number; y: number; z: number; diff --git a/src/app/cartography/models/map/map-node.ts b/src/app/cartography/models/map/map-node.ts index ae08ccf7..fda84783 100644 --- a/src/app/cartography/models/map/map-node.ts +++ b/src/app/cartography/models/map/map-node.ts @@ -12,6 +12,7 @@ export class MapNode implements Indexed { firstPortName: string; height: number; label: MapLabel; + locked: boolean; name: string; nodeDirectory: string; nodeType: string; diff --git a/src/app/cartography/models/node.ts b/src/app/cartography/models/node.ts index 86d0fa33..c5960ae2 100644 --- a/src/app/cartography/models/node.ts +++ b/src/app/cartography/models/node.ts @@ -69,6 +69,7 @@ export class Node { first_port_name: string; height: number; label: Label; + locked: boolean; name: string; node_directory: string; node_id: string; diff --git a/src/app/cartography/widgets/interface-status.ts b/src/app/cartography/widgets/interface-status.ts index 119ec6b7..1fd969cf 100644 --- a/src/app/cartography/widgets/interface-status.ts +++ b/src/app/cartography/widgets/interface-status.ts @@ -16,16 +16,17 @@ export class InterfaceStatusWidget implements Widget { const link_group = select(this); const link_path = link_group.select('path'); - const start_point: SVGPoint = link_path.node().getPointAtLength(45); - const end_point: SVGPoint = link_path.node().getPointAtLength(link_path.node().getTotalLength() - 45); - let statuses = []; + if (link_path.node()) { + const start_point: SVGPoint = link_path.node().getPointAtLength(45); + const end_point: SVGPoint = link_path.node().getPointAtLength(link_path.node().getTotalLength() - 45); - if (link_path.node().getTotalLength() > 2 * 45 + 10) { - statuses = [ - new LinkStatus(start_point.x, start_point.y, l.source.status), - new LinkStatus(end_point.x, end_point.y, l.target.status) - ]; + if (link_path.node().getTotalLength() > 2 * 45 + 10) { + statuses = [ + new LinkStatus(start_point.x, start_point.y, l.source.status), + new LinkStatus(end_point.x, end_point.y, l.target.status) + ]; + } } const status_started = link_group diff --git a/src/app/components/drawings-listeners/drawing-dragged/drawing-dragged.component.spec.ts b/src/app/components/drawings-listeners/drawing-dragged/drawing-dragged.component.spec.ts index 841a1b91..cd6e6684 100644 --- a/src/app/components/drawings-listeners/drawing-dragged/drawing-dragged.component.spec.ts +++ b/src/app/components/drawings-listeners/drawing-dragged/drawing-dragged.component.spec.ts @@ -48,6 +48,7 @@ describe('DrawingDraggedComponent', () => { }; const mapDrawing: MapDrawing = { id: 'sampleId', + locked: false, projectId: 'sampleprojectId', rotation: 0, svg: 'sampleSvg', diff --git a/src/app/components/drawings-listeners/drawing-resized/drawing-resized.component.spec.ts b/src/app/components/drawings-listeners/drawing-resized/drawing-resized.component.spec.ts index bd9796f0..0af53fda 100644 --- a/src/app/components/drawings-listeners/drawing-resized/drawing-resized.component.spec.ts +++ b/src/app/components/drawings-listeners/drawing-resized/drawing-resized.component.spec.ts @@ -51,6 +51,7 @@ describe('DrawingResizedComponent', () => { }; const mapDrawing: MapDrawing = { id: 'sampleId', + locked: false, projectId: 'sampleprojectId', rotation: 0, svg: 'sampleSvg', diff --git a/src/app/components/drawings-listeners/link-created/link-created.component.spec.ts b/src/app/components/drawings-listeners/link-created/link-created.component.spec.ts index 1a8e8642..06547cb8 100644 --- a/src/app/components/drawings-listeners/link-created/link-created.component.spec.ts +++ b/src/app/components/drawings-listeners/link-created/link-created.component.spec.ts @@ -72,6 +72,7 @@ describe('LinkCreatedComponent', () => { firstPortName: 'sampleFirstPortName', height: 0, label: {} as MapLabel, + locked: false, name: 'sampleName', nodeDirectory: 'sampleNodeDirectory', nodeType: 'sampleNodeType', diff --git a/src/app/components/drawings-listeners/node-dragged/node-dragged.component.spec.ts b/src/app/components/drawings-listeners/node-dragged/node-dragged.component.spec.ts index 1965cee6..82b67ff5 100644 --- a/src/app/components/drawings-listeners/node-dragged/node-dragged.component.spec.ts +++ b/src/app/components/drawings-listeners/node-dragged/node-dragged.component.spec.ts @@ -52,6 +52,7 @@ describe('NodeDraggedComponent', () => { firstPortName: 'sampleFirstPortName', height: 0, label: {} as MapLabel, + locked: false, name: 'sampleName', nodeDirectory: 'sampleNodeDirectory', nodeType: 'sampleNodeType', diff --git a/src/app/components/project-map/context-menu/actions/edit-config/edit-config-action.component.html b/src/app/components/project-map/context-menu/actions/edit-config/edit-config-action.component.html index 3cd439d9..d3dc8ef0 100644 --- a/src/app/components/project-map/context-menu/actions/edit-config/edit-config-action.component.html +++ b/src/app/components/project-map/context-menu/actions/edit-config/edit-config-action.component.html @@ -1,4 +1,6 @@ - diff --git a/src/app/components/project-map/context-menu/actions/export-config/export-config-action.component.html b/src/app/components/project-map/context-menu/actions/export-config/export-config-action.component.html index 5730195b..70843579 100644 --- a/src/app/components/project-map/context-menu/actions/export-config/export-config-action.component.html +++ b/src/app/components/project-map/context-menu/actions/export-config/export-config-action.component.html @@ -1,4 +1,6 @@ - diff --git a/src/app/components/project-map/context-menu/actions/export-config/export-config-action.component.ts b/src/app/components/project-map/context-menu/actions/export-config/export-config-action.component.ts index 29b221cf..204a6ee1 100644 --- a/src/app/components/project-map/context-menu/actions/export-config/export-config-action.component.ts +++ b/src/app/components/project-map/context-menu/actions/export-config/export-config-action.component.ts @@ -16,7 +16,7 @@ export class ExportConfigActionComponent { ) {} exportConfig() { - this.nodeService.getConfiguration(this.server, this.node).subscribe((config: any) => { + this.nodeService.getStartupConfiguration(this.server, this.node).subscribe((config: any) => { this.downloadByHtmlTag(config); }); } @@ -25,7 +25,11 @@ export class ExportConfigActionComponent { const element = document.createElement('a'); const fileType = 'text/plain'; element.setAttribute('href', `data:${fileType};charset=utf-8,${encodeURIComponent(config)}`); - element.setAttribute('download', `${this.node.name}_startup.vpc`); + if (this.node.node_type === 'vpcs') { + element.setAttribute('download', `${this.node.name}_startup.vpc`); + } else if (this.node.node_type === 'iou' || this.node.node_type === 'dynamips') { + element.setAttribute('download', `${this.node.name}_startup.cfg`); + } var event = new MouseEvent("click"); element.dispatchEvent(event); diff --git a/src/app/components/project-map/context-menu/actions/lock-action/lock-action.component.html b/src/app/components/project-map/context-menu/actions/lock-action/lock-action.component.html new file mode 100644 index 00000000..451af511 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/lock-action/lock-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/lock-action/lock-action.component.ts b/src/app/components/project-map/context-menu/actions/lock-action/lock-action.component.ts new file mode 100644 index 00000000..8557917e --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/lock-action/lock-action.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit, Input, OnDestroy, OnChanges } from '@angular/core'; +import { Server } from '../../../../../models/server'; +import { Node } from '../../../../../cartography/models/node'; +import { NodesDataSource } from '../../../../../cartography/datasources/nodes-datasource'; +import { NodeService } from '../../../../../services/node.service'; +import { Drawing } from '../../../../../cartography/models/drawing'; +import { DrawingsDataSource } from '../../../../../cartography/datasources/drawings-datasource'; +import { DrawingService } from '../../../../../services/drawing.service'; + +@Component({ + selector: 'app-lock-action', + templateUrl: './lock-action.component.html' +}) +export class LockActionComponent implements OnChanges { + @Input() server: Server; + @Input() nodes: Node[]; + @Input() drawings: Drawing[]; + command: string; + + constructor( + private nodesDataSource: NodesDataSource, + private drawingsDataSource: DrawingsDataSource, + private nodeService: NodeService, + private drawingService: DrawingService + ) {} + + ngOnChanges() { + if (this.nodes.length === 1 && this.drawings.length === 0) { + this.command = this.nodes[0].locked ? 'Unlock item' : 'Lock item'; + } else if (this.nodes.length === 0 && this.drawings.length === 1) { + this.command = this.drawings[0].locked ? 'Unlock item' : 'Lock item'; + } else { + this.command = 'Lock/unlock items'; + } + } + + lock() { + this.nodes.forEach((node) => { + node.locked = !node.locked; + this.nodeService.updateNode(this.server, node).subscribe((node) => { this.nodesDataSource.update(node) }); + }); + + this.drawings.forEach((drawing) => { + drawing.locked = ! drawing.locked; + this.drawingService.update(this.server, drawing).subscribe((drawing) => { this.drawingsDataSource.update(drawing) }); + }); + } +} 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 24916844..49904db0 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 @@ -121,6 +121,12 @@ [server]="server" [link]="links[0]" > + Configuration for node {{node.name}} -